diff --git a/.github/actions/setup-gdk-desktop/action.yml b/.github/actions/setup-gdk-desktop/action.yml new file mode 100644 index 0000000000000..10427ace3c9e5 --- /dev/null +++ b/.github/actions/setup-gdk-desktop/action.yml @@ -0,0 +1,82 @@ +name: 'Setup GDK (Game Development Kit) for Windows Desktop' +description: 'Download GDK and install into MSBuild' +inputs: + # Keep edition and ref in sync! + edition: + description: 'GDK edition' + default: '240601' # YYMMUU (Year Month Update) + ref: + description: 'Git reference' + default: 'June_2024_Update_1' + folder: + description: 'Folder where to create Directory.Build.props' + required: true + default: '${{ github.workspace }}' +runs: + using: 'composite' + steps: + - uses: actions/setup-python@main + with: + python-version: 3.x + - name: 'Calculate variables' + id: calc + shell: pwsh + run: | + $vs_folder=@(vswhere -latest -property installationPath) + echo "vs-folder=${vs_folder}" >> $Env:GITHUB_OUTPUT + + echo "gdk-path=${{ runner.temp }}\GDK-${{ inputs.edition }}" >> $Env:GITHUB_OUTPUT + + echo "cache-key=gdk-${{ inputs.ref }}-${{ inputs.edition }}" >> $Env:GITHUB_OUTPUT + - name: 'Restore cached GDK' + id: cache-restore + uses: actions/cache/restore@v4 + with: + path: '${{ steps.calc.outputs.gdk-path }}' + key: ${{ steps.calc.outputs.cache-key }} + - name: 'Download GDK' + if: ${{ !steps.cache-restore.outputs.cache-hit }} + shell: pwsh + run: | + python build-scripts/setup-gdk-desktop.py ` + --download ` + --temp-folder "${{ runner.temp }}" ` + --gdk-path="${{ steps.calc.outputs.gdk-path }}" ` + --ref-edition "${{ inputs.ref }},${{ inputs.edition }}" ` + --vs-folder="${{ steps.calc.outputs.vs-folder }}" ` + --no-user-props + - name: 'Extract GDK' + if: ${{ !steps.cache-restore.outputs.cache-hit }} + shell: pwsh + run: | + python build-scripts/setup-gdk-desktop.py ` + --extract ` + --ref-edition "${{ inputs.ref }},${{ inputs.edition }}" ` + --temp-folder "${{ runner.temp }}" ` + --gdk-path="${{ steps.calc.outputs.gdk-path }}" ` + --vs-folder="${{ steps.calc.outputs.vs-folder }}" ` + --no-user-props + - name: 'Cache GDK' + if: ${{ !steps.cache-restore.outputs.cache-hit }} + uses: actions/cache/save@v4 + with: + path: '${{ steps.calc.outputs.gdk-path }}' + key: ${{ steps.calc.outputs.cache-key }} + - name: 'Copy MSBuild files into GDK' + shell: pwsh + run: | + python build-scripts/setup-gdk-desktop.py ` + --ref-edition "${{ inputs.ref }},${{ inputs.edition }}" ` + --gdk-path="${{ steps.calc.outputs.gdk-path }}" ` + --vs-folder="${{ steps.calc.outputs.vs-folder }}" ` + --copy-msbuild ` + --no-user-props + - name: 'Write user props' + shell: pwsh + run: | + python build-scripts/setup-gdk-desktop.py ` + --ref-edition "${{ inputs.ref }},${{ inputs.edition }}" ` + --temp-folder "${{ runner.temp }}" ` + --vs-folder="${{ steps.calc.outputs.vs-folder }}" ` + --gdk-path="${{ steps.calc.outputs.gdk-path }}" ` + "--props-folder=${{ inputs.folder }}" diff --git a/.github/actions/setup-loongarch64-toolchain/action.yml b/.github/actions/setup-loongarch64-toolchain/action.yml new file mode 100644 index 0000000000000..54698b3a9cb6f --- /dev/null +++ b/.github/actions/setup-loongarch64-toolchain/action.yml @@ -0,0 +1,53 @@ +name: 'Setup LoongArch64 toolchain' +description: 'Download Linux LoongArch64 toolchain and set output variables' +inputs: + version: + description: 'LoongArch64 version' + default: '2022.09.06' +outputs: + prefix: + description: "LoongArch toolchain prefix" + value: ${{ steps.final.outputs.prefix }} + cc: + description: "LoongArch C compiler" + value: ${{ steps.final.outputs.cc }} + cxx: + description: "LoongArch C++ compiler" + value: ${{ steps.final.outputs.cxx }} +runs: + using: 'composite' + steps: + - uses: actions/cache/restore@v4 + id: restore-cache + with: + path: /opt/cross-tools + key: loongarch64-${{ inputs.version }} + + - name: 'Download LoongArch64 gcc+glibc toolchain' + if: ${{ !steps.restore-cache.outputs.cache-hit }} + shell: bash + run: | + url="https://github.com/loongson/build-tools/releases/download/${{ inputs.version }}/loongarch64-clfs-6.3-cross-tools-gcc-glibc.tar.xz" + + wget "$url" -O /tmp/toolchain.tar.xz + + mkdir -p /opt + tar -C /opt -x -f /tmp/toolchain.tar.xz + + - uses: actions/cache/save@v4 + if: ${{ !steps.restore-cache.outputs.cache-hit }} + with: + path: /opt/cross-tools + key: loongarch64-${{ inputs.version }} + - name: 'Set output vars' + id: final + shell: bash + run: | + prefix=/opt/cross-tools + echo "prefix=${prefix}" >> $GITHUB_OUTPUT + cc="${prefix}/bin/loongarch64-unknown-linux-gnu-gcc" + cxx="${prefix}/bin/loongarch64-unknown-linux-gnu-g++" + echo "cc=${cc}" >> $GITHUB_OUTPUT + echo "cxx=${cxx}" >> $GITHUB_OUTPUT + echo "LOONGARCH64_CC=${cc}" >>$GITHUB_ENV + echo "LOONGARCH64_CXX=${cxx}" >>$GITHUB_ENV diff --git a/.github/actions/setup-msvc-libusb-action/action.yml b/.github/actions/setup-msvc-libusb/action.yml similarity index 90% rename from .github/actions/setup-msvc-libusb-action/action.yml rename to .github/actions/setup-msvc-libusb/action.yml index a0e766618141d..cbbf0980b577d 100644 --- a/.github/actions/setup-msvc-libusb-action/action.yml +++ b/.github/actions/setup-msvc-libusb/action.yml @@ -1,5 +1,5 @@ -name: 'Setup libusb for MSBC' -description: 'Greet someone' +name: 'Setup libusb for MSVC' +description: 'Download libusb sdk for MSVC, and set output/environment variables' inputs: version: description: 'libusb version' @@ -68,3 +68,4 @@ runs: exit 1 } echo "root=${libusb_incdir};${libusb_libdir}" >> $env:GITHUB_OUTPUT + echo "LibUSB_ROOT=${libusb_incdir};${libusb_libdir}" >> $env:GITHUB_ENV diff --git a/.github/actions/setup-ninja/action.yml b/.github/actions/setup-ninja/action.yml index bf638eabae32b..b9283598d773d 100644 --- a/.github/actions/setup-ninja/action.yml +++ b/.github/actions/setup-ninja/action.yml @@ -1,5 +1,5 @@ name: 'Setup ninja' -description: 'Setup ninja' +description: 'Download ninja and add it to the PATH environment variable' inputs: version: description: 'Ninja version' @@ -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@main + uses: actions/cache/restore@v4 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 }} - uses: actions/cache/save@main + uses: actions/cache/save@v4 with: path: '${{ runner.temp }}/${{ steps.calc.outputs.archive }}' key: ${{ steps.calc.outputs.cache-key }} diff --git a/.github/actions/setup-vita-gles/action.yml b/.github/actions/setup-vita-gles/action.yml new file mode 100644 index 0000000000000..e263737b31e20 --- /dev/null +++ b/.github/actions/setup-vita-gles/action.yml @@ -0,0 +1,93 @@ +name: 'Setup GLES for PlayStation Vita' +description: 'Download GLES for VITA (PVR or PIB), and copy it into the vita sdk' +inputs: + pib-version: + description: 'PIB version' + default: '1.1.4' + pvr-version: + description: 'PVR_PSP2 version' + default: '3.9' + type: + description: '"pib" or "pvr"' + default: '' +runs: + using: 'composite' + steps: + - name: 'Calculate variables' + id: calc + shell: sh + run: | + if test "x${VITASDK}" = "x"; then + echo "VITASDK must be defined" + exit 1; + fi + case "x${{ inputs.type }}" in + "xpvr") + echo "cache-key=SDL-vita-gles-pvr-${{ inputs.pvr-version}}" >> ${GITHUB_OUTPUT} + ;; + "xpib") + echo "cache-key=SDL-vita-gles-pib-${{ inputs.pib-version}}" >> ${GITHUB_OUTPUT} + ;; + *) + echo "Invalid type. Must be 'pib' or 'pvr'." + exit 1 + ;; + esac + - uses: actions/cache/restore@v4 + id: restore-cache + with: + path: /vita/dependencies + key: '${{ steps.calc.outputs.cache-key }}' + - name: 'Download PVR_PSP2 (GLES)' + if: ${{ !steps.restore-cache.outputs.cache-hit && inputs.type == 'pvr' }} + shell: sh + run: | + pvr_psp2_version=${{ inputs.pvr-version }} + + mkdir -p /vita/dependencies/include + mkdir -p /vita/dependencies/lib + + # Configure PVR_PSP2 headers + wget https://github.com/GrapheneCt/PVR_PSP2/archive/refs/tags/v$pvr_psp2_version.zip -P/tmp + unzip /tmp/v$pvr_psp2_version.zip -d/tmp + cp -r /tmp/PVR_PSP2-$pvr_psp2_version/include/* /vita/dependencies/include + rm /tmp/v$pvr_psp2_version.zip + + # include guard of PVR_PSP2's khrplatform.h does not match the usual one + sed -i -e s/__drvkhrplatform_h_/__khrplatform_h_/ /vita/dependencies/include/KHR/khrplatform.h + + # Configure PVR_PSP2 stub libraries + wget https://github.com/GrapheneCt/PVR_PSP2/releases/download/v$pvr_psp2_version/vitasdk_stubs.zip -P/tmp + unzip /tmp/vitasdk_stubs.zip -d/tmp/pvr_psp2_stubs + find /tmp/pvr_psp2_stubs -type f -name "*.a" -exec cp {} /vita/dependencies/lib \; + rm /tmp/vitasdk_stubs.zip + rm -rf /tmp/pvr_psp2_stubs + + - name: 'Download gl4es4vita (OpenGL)' + if: ${{ !steps.restore-cache.outputs.cache-hit && inputs.type == 'pib' }} + shell: sh + run: | + gl4es4vita_version=${{ inputs.pib-version }} + + mkdir -p /vita/dependencies/include + mkdir -p /vita/dependencies/lib + + # Configure gl4es4vita headers + wget https://github.com/SonicMastr/gl4es4vita/releases/download/v$gl4es4vita_version-vita/include.zip -P/tmp + unzip -o /tmp/include.zip -d/vita/dependencies/include + rm /tmp/include.zip + + # Configure gl4es4vita stub libraries + 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 + if: ${{ !steps.restore-cache.outputs.cache-hit }} + with: + path: /vita/dependencies + key: '${{ steps.calc.outputs.cache-key }}' + + - name: Copy PVR_PSP2 (GLES) or gl4es4vita (OpenGL) to vita toolchain dir + shell: sh + run: | + cp -rv /vita/dependencies/* ${VITASDK}/arm-vita-eabi diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000000000..09652e024817c --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,48 @@ +name: 'Build (All)' + +on: [push, pull_request] + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.run_id }} + cancel-in-progress: true + +jobs: + controller: + name: 'Create test plan' + runs-on: 'ubuntu-latest' + outputs: + platforms-level1: ${{ steps.plan.outputs.platforms-level1 }} + platforms-others: ${{ steps.plan.outputs.platforms-others }} + steps: + - uses: actions/setup-python@main + with: + python-version: 3.x + - uses: actions/checkout@main + with: + sparse-checkout: '.github/workflows/create-test-plan.py' + - name: 'Create plan' + id: plan + run: | + # Adding [sdl-ci-filter GLOB] to the commit message will limit the jobs + # e.g. [sdl-ci-filter msvc-*] + EOF=$(openssl rand -hex 32) + cat >/tmp/commit_message.txt <<$EOF + ${{ github.event.head_commit.message }} + $EOF + + python .github/workflows/create-test-plan.py \ + --github-variable-prefix platforms \ + --github-ci \ + --verbose \ + ${{ (github.repository_owner != 'libsdl-org' && '--no-artifact') || '' }} \ + --commit-message-file /tmp/commit_message.txt + level1: + needs: [controller] + uses: './.github/workflows/generic.yml' + with: + platforms: ${{ needs.controller.outputs.platforms-level1 }} + level2: + needs: [controller, level1] + uses: './.github/workflows/generic.yml' + with: + platforms: ${{ needs.controller.outputs.platforms-others }} diff --git a/.github/workflows/create-test-plan.py b/.github/workflows/create-test-plan.py new file mode 100755 index 0000000000000..a1850134f2a09 --- /dev/null +++ b/.github/workflows/create-test-plan.py @@ -0,0 +1,715 @@ +#!/usr/bin/env python +import argparse +import dataclasses +import fnmatch +from enum import Enum +import json +import logging +import os +import re +from typing import Optional + +logger = logging.getLogger(__name__) + + +class AppleArch(Enum): + Aarch64 = "aarch64" + X86_64 = "x86_64" + + +class MsvcArch(Enum): + X86 = "x86" + X64 = "x64" + Arm32 = "arm" + Arm64 = "arm64" + + +class JobOs(Enum): + WindowsLatest = "windows-latest" + UbuntuLatest = "ubuntu-latest" + MacosLatest = "macos-latest" + Ubuntu20_04 = "ubuntu-20.04" + Ubuntu22_04 = "ubuntu-22.04" + Ubuntu24_04 = "ubuntu-24.04" + Macos12 = "macos-12" + + +class SdlPlatform(Enum): + Android = "android" + Emscripten = "emscripten" + Haiku = "haiku" + LoongArch64 = "loongarch64" + Msys2 = "msys2" + Linux = "linux" + MacOS = "macos" + Ios = "ios" + Tvos = "tvos" + Msvc = "msvc" + N3ds = "n3ds" + PowerPC64 = "powerpc64" + Ps2 = "ps2" + Psp = "psp" + Vita = "vita" + Riscos = "riscos" + FreeBSD = "freebsd" + NetBSD = "netbsd" + + +class Msys2Platform(Enum): + Mingw32 = "mingw32" + Mingw64 = "mingw64" + Clang32 = "clang32" + Clang64 = "clang64" + Ucrt64 = "ucrt64" + + +class IntelCompiler(Enum): + Icc = "icc" + Icx = "icx" + + +class VitaGLES(Enum): + Pib = "pib" + Pvr = "pvr" + + +@dataclasses.dataclass(slots=True) +class JobSpec: + name: str + os: JobOs + platform: SdlPlatform + artifact: Optional[str] + container: Optional[str] = None + no_cmake: bool = False + android_mk: bool = False + android_gradle: bool = False + lean: bool = False + android_arch: Optional[str] = None + android_abi: Optional[str] = None + android_platform: Optional[int] = None + msys2_platform: Optional[Msys2Platform] = None + intel: Optional[IntelCompiler] = None + apple_framework: Optional[bool] = None + apple_archs: Optional[set[AppleArch]] = None + msvc_project: Optional[str] = None + msvc_arch: Optional[MsvcArch] = None + clang_cl: bool = False + uwp: bool = False + gdk: bool = False + vita_gles: Optional[VitaGLES] = None + + +JOB_SPECS = { + "msys2-mingw32": JobSpec(name="Windows (msys2, mingw32)", os=JobOs.WindowsLatest, platform=SdlPlatform.Msys2, artifact="SDL-mingw32", msys2_platform=Msys2Platform.Mingw32, ), + "msys2-mingw64": JobSpec(name="Windows (msys2, mingw64)", os=JobOs.WindowsLatest, platform=SdlPlatform.Msys2, artifact="SDL-mingw64", msys2_platform=Msys2Platform.Mingw64, ), + "msys2-clang32": JobSpec(name="Windows (msys2, clang32)", os=JobOs.WindowsLatest, platform=SdlPlatform.Msys2, artifact="SDL-mingw32-clang", msys2_platform=Msys2Platform.Clang32, ), + "msys2-clang64": JobSpec(name="Windows (msys2, clang64)", os=JobOs.WindowsLatest, platform=SdlPlatform.Msys2, artifact="SDL-mingw64-clang", msys2_platform=Msys2Platform.Clang64, ), + "msys2-ucrt64": JobSpec(name="Windows (msys2, ucrt64)", os=JobOs.WindowsLatest, platform=SdlPlatform.Msys2, artifact="SDL-mingw64-ucrt", msys2_platform=Msys2Platform.Ucrt64, ), + "msvc-x64": JobSpec(name="Windows (MSVC, x64)", os=JobOs.WindowsLatest, platform=SdlPlatform.Msvc, artifact="SDL-VC-x64", msvc_arch=MsvcArch.X64, msvc_project="VisualC/SDL.sln", ), + "msvc-x86": JobSpec(name="Windows (MSVC, x86)", os=JobOs.WindowsLatest, platform=SdlPlatform.Msvc, artifact="SDL-VC-x86", msvc_arch=MsvcArch.X86, msvc_project="VisualC/SDL.sln", ), + "msvc-clang-x64": JobSpec(name="Windows (MSVC, clang-cl x64)", os=JobOs.WindowsLatest, platform=SdlPlatform.Msvc, artifact="SDL-clang-cl-x64", msvc_arch=MsvcArch.X64, clang_cl=True, ), + "msvc-clang-x86": JobSpec(name="Windows (MSVC, clang-cl x86)", os=JobOs.WindowsLatest, platform=SdlPlatform.Msvc, artifact="SDL-clang-cl-x86", msvc_arch=MsvcArch.X86, clang_cl=True, ), + "msvc-arm32": JobSpec(name="Windows (MSVC, ARM)", os=JobOs.WindowsLatest, platform=SdlPlatform.Msvc, artifact="SDL-VC-arm32", msvc_arch=MsvcArch.Arm32, ), + "msvc-arm64": JobSpec(name="Windows (MSVC, ARM64)", os=JobOs.WindowsLatest, platform=SdlPlatform.Msvc, artifact="SDL-VC-arm64", msvc_arch=MsvcArch.Arm64, ), + "msvc-uwp-x64": JobSpec(name="UWP (MSVC, x64)", os=JobOs.WindowsLatest, platform=SdlPlatform.Msvc, artifact="SDL-VC-UWP", msvc_arch=MsvcArch.X64, msvc_project="VisualC-WinRT/SDL-UWP.sln", uwp=True, ), + "msvc-gdk-x64": JobSpec(name="GDK (MSVC, x64)", os=JobOs.WindowsLatest, platform=SdlPlatform.Msvc, artifact="SDL-VC-GDK", msvc_arch=MsvcArch.X64, msvc_project="VisualC-GDK/SDL.sln", gdk=True, no_cmake=True, ), + "ubuntu-20.04": JobSpec(name="Ubuntu 20.04", os=JobOs.Ubuntu20_04, platform=SdlPlatform.Linux, artifact="SDL-ubuntu20.04", ), + "ubuntu-22.04": JobSpec(name="Ubuntu 22.04", os=JobOs.Ubuntu22_04, platform=SdlPlatform.Linux, artifact="SDL-ubuntu22.04", ), + "ubuntu-intel-icx": JobSpec(name="Ubuntu 20.04 (Intel oneAPI)", os=JobOs.Ubuntu20_04, platform=SdlPlatform.Linux, artifact="SDL-ubuntu20.04-oneapi", intel=IntelCompiler.Icx, ), + "ubuntu-intel-icc": JobSpec(name="Ubuntu 20.04 (Intel Compiler)", os=JobOs.Ubuntu20_04, platform=SdlPlatform.Linux, artifact="SDL-ubuntu20.04-icc", intel=IntelCompiler.Icc, ), + "macos-framework-x64": JobSpec(name="MacOS (Framework) (x86_64)", os=JobOs.Macos12, platform=SdlPlatform.MacOS, artifact="SDL-macos-framework", apple_framework=True, apple_archs={AppleArch.Aarch64, AppleArch.X86_64, }, ), + "macos-framework-arm64": JobSpec(name="MacOS (Framework) (arm64)", os=JobOs.MacosLatest, platform=SdlPlatform.MacOS, artifact=None, apple_framework=True, apple_archs={AppleArch.Aarch64, AppleArch.X86_64, }, ), + "macos-gnu-arm64": JobSpec(name="MacOS (GNU prefix)", os=JobOs.MacosLatest, platform=SdlPlatform.MacOS, artifact="SDL-macos-arm64-gnu", apple_framework=False, apple_archs={AppleArch.Aarch64, }, ), + "android-cmake": JobSpec(name="Android (CMake)", os=JobOs.UbuntuLatest, platform=SdlPlatform.Android, artifact="SDL-android-arm64", android_abi="arm64-v8a", android_arch="aarch64", android_platform=23, ), + "android-cmake-lean": JobSpec(name="Android (CMake, lean)", os=JobOs.UbuntuLatest, platform=SdlPlatform.Android, artifact="SDL-lean-android-arm64", android_abi="arm64-v8a", android_arch="aarch64", android_platform=23, lean=True, ), + "android-mk": JobSpec(name="Android (Android.mk)", os=JobOs.UbuntuLatest, platform=SdlPlatform.Android, artifact=None, no_cmake=True, android_mk=True, ), + "android-gradle": JobSpec(name="Android (Gradle)", os=JobOs.UbuntuLatest, platform=SdlPlatform.Android, artifact=None, no_cmake=True, android_gradle=True, ), + "ios-xcode": JobSpec(name="iOS (xcode)", os=JobOs.MacosLatest, platform=SdlPlatform.Ios, artifact=None, no_cmake=True, ), + "tvos-xcode": JobSpec(name="tvOS (xcode)", os=JobOs.MacosLatest, platform=SdlPlatform.Tvos, artifact=None, no_cmake=True, ), + "emscripten": JobSpec(name="Emscripten", os=JobOs.UbuntuLatest, platform=SdlPlatform.Emscripten, artifact="SDL-emscripten", ), + "haiku": JobSpec(name="Haiku", os=JobOs.UbuntuLatest, platform=SdlPlatform.Haiku, artifact="SDL-haiku-x64", container="haiku/cross-compiler:x86_64-r1beta4", ), + "loongarch64": JobSpec(name="LoongArch64", os=JobOs.UbuntuLatest, platform=SdlPlatform.LoongArch64, artifact="SDL-loongarch64", ), + "n3ds": JobSpec(name="Nintendo 3DS", os=JobOs.UbuntuLatest, platform=SdlPlatform.N3ds, artifact="SDL-n3ds", container="devkitpro/devkitarm:latest", ), + "ppc64": JobSpec(name="PowerPC64", os=JobOs.UbuntuLatest, platform=SdlPlatform.PowerPC64, artifact="SDL-ppc64le", container="dockcross/linux-ppc64le:latest", ), + "ps2": JobSpec(name="Sony PlayStation 2", os=JobOs.UbuntuLatest, platform=SdlPlatform.Ps2, artifact="SDL-ps2", container="ps2dev/ps2dev:latest", ), + "psp": JobSpec(name="Sony PlayStation Portable", os=JobOs.UbuntuLatest, platform=SdlPlatform.Psp, artifact="SDL-psp", container="pspdev/pspdev:latest", ), + "vita-pib": JobSpec(name="Sony PlayStation Vita (GLES w/ pib)", os=JobOs.UbuntuLatest, platform=SdlPlatform.Vita, artifact="SDL-vita-pib", container="vitasdk/vitasdk:latest", vita_gles=VitaGLES.Pib, ), + "vita-pvr": JobSpec(name="Sony PlayStation Vita (GLES w/ PVR_PSP2)", os=JobOs.UbuntuLatest, platform=SdlPlatform.Vita, artifact="SDL-vita-pvr", container="vitasdk/vitasdk:latest", vita_gles=VitaGLES.Pvr, ), + "riscos": JobSpec(name="RISC OS", os=JobOs.UbuntuLatest, platform=SdlPlatform.Riscos, artifact="SDL-riscos", container="riscosdotinfo/riscos-gccsdk-4.7:latest", ), + "netbsd": JobSpec(name="NetBSD", os=JobOs.UbuntuLatest, platform=SdlPlatform.NetBSD, artifact="SDL-netbsd-x64", ), + "freebsd": JobSpec(name="FreeBSD", os=JobOs.UbuntuLatest, platform=SdlPlatform.FreeBSD, artifact="SDL-freebsd-x64", ), +} + + +@dataclasses.dataclass(slots=True) +class JobDetails: + name: str + os: str + platform: str + artifact: str + no_cmake: bool + build_tests: bool = True + container: str = "" + cmake_build_type: str = "Release" + shell: str = "sh" + sudo: str = "sudo" + cmake_config_emulator: str = "" + apk_packages: list[str] = dataclasses.field(default_factory=list) + apt_packages: list[str] = dataclasses.field(default_factory=list) + brew_packages: list[str] = dataclasses.field(default_factory=list) + cmake_toolchain_file: str = "" + cmake_arguments: list[str] = dataclasses.field(default_factory=list) + cmake_build_arguments: list[str] = dataclasses.field(default_factory=list) + clang_tidy: bool = True + cppflags: list[str] = dataclasses.field(default_factory=list) + cc: str = "" + cxx: str = "" + cflags: list[str] = dataclasses.field(default_factory=list) + cxxflags: list[str] = dataclasses.field(default_factory=list) + ldflags: list[str] = dataclasses.field(default_factory=list) + pollute_directories: list[str] = dataclasses.field(default_factory=list) + use_cmake: bool = True + shared: bool = True + static: bool = True + run_tests: bool = True + test_pkg_config: bool = True + cc_from_cmake: bool = False + source_cmd: str = "" + java: bool = False + android_apks: list[str] = dataclasses.field(default_factory=list) + android_ndk: bool = False + android_mk: bool = False + android_gradle: bool = False + minidump: bool = False + intel: bool = False + msys2_msystem: str = "" + msys2_env: str = "" + msys2_no_perl: bool = False + werror: bool = True + msvc_vcvars: str = "" + msvc_project: str = "" + msvc_project_flags: list[str] = dataclasses.field(default_factory=list) + setup_ninja: bool = False + setup_libusb_arch: str = "" + xcode_sdk: str = "" + cpactions: bool = False + setup_gdk_folder: str = "" + cpactions_os: str = "" + cpactions_version: str = "" + cpactions_arch: str = "" + cpactions_setup_cmd: str = "" + cpactions_install_cmd: str = "" + setup_vita_gles_type: str = "" + + def to_workflow(self, enable_artifacts: bool) -> dict[str, str|bool]: + data = { + "name": self.name, + "os": self.os, + "container": self.container if self.container else "", + "platform": self.platform, + "artifact": self.artifact, + "enable-artifacts": enable_artifacts, + "shell": self.shell, + "msys2-msystem": self.msys2_msystem, + "msys2-env": self.msys2_env, + "msys2-no-perl": self.msys2_no_perl, + "android-ndk": self.android_ndk, + "java": self.java, + "intel": self.intel, + "apk-packages": my_shlex_join(self.apk_packages), + "apt-packages": my_shlex_join(self.apt_packages), + "test-pkg-config": self.test_pkg_config, + "brew-packages": my_shlex_join(self.brew_packages), + "pollute-directories": my_shlex_join(self.pollute_directories), + "no-cmake": self.no_cmake, + "build-tests": self.build_tests, + "source-cmd": self.source_cmd, + "cmake-config-emulator": self.cmake_config_emulator, + "cc": self.cc, + "cxx": self.cxx, + "cflags": my_shlex_join(self.cppflags + self.cflags), + "cxxflags": my_shlex_join(self.cppflags + self.cxxflags), + "ldflags": my_shlex_join(self.ldflags), + "cmake-toolchain-file": self.cmake_toolchain_file, + "clang-tidy": self.clang_tidy, + "cmake-arguments": my_shlex_join(self.cmake_arguments), + "cmake-build-arguments": my_shlex_join(self.cmake_build_arguments), + "shared": self.shared, + "static": self.static, + "cmake-build-type": self.cmake_build_type, + "run-tests": self.run_tests, + "android-apks": my_shlex_join(self.android_apks), + "android-gradle": self.android_gradle, + "android-mk": self.android_mk, + "werror": self.werror, + "sudo": self.sudo, + "msvc-vcvars": self.msvc_vcvars, + "msvc-project": self.msvc_project, + "msvc-project-flags": my_shlex_join(self.msvc_project_flags), + "setup-ninja": self.setup_ninja, + "setup-libusb-arch": self.setup_libusb_arch, + "cc-from-cmake": self.cc_from_cmake, + "xcode-sdk": self.xcode_sdk, + "cpactions": self.cpactions, + "cpactions-os": self.cpactions_os, + "cpactions-version": self.cpactions_version, + "cpactions-arch": self.cpactions_arch, + "cpactions-setup-cmd": self.cpactions_setup_cmd, + "cpactions-install-cmd": self.cpactions_install_cmd, + "setup-vita-gles-type": self.setup_vita_gles_type, + "setup-gdk-folder": self.setup_gdk_folder, + } + return {k: v for k, v in data.items() if v != ""} + + +def my_shlex_join(s): + def escape(s): + if s[:1] == "'" and s[-1:] == "'": + return s + if set(s).intersection(set("; \t")): + return f'"{s}"' + return s + + return " ".join(escape(s)) + + +def spec_to_job(spec: JobSpec) -> JobDetails: + job = JobDetails( + name=spec.name, + os=spec.os.value, + artifact=spec.artifact or "", + container=spec.container or "", + platform=spec.platform.value, + sudo="sudo", + no_cmake=spec.no_cmake, + ) + if job.os.startswith("ubuntu"): + job.apt_packages.extend([ + "ninja-build", + "pkg-config", + ]) + win32 = spec.platform in (SdlPlatform.Msys2, SdlPlatform.Msvc) + fpic = None + build_parallel = True + if spec.lean: + job.cppflags.append("-DSDL_LEAN_AND_MEAN=1") + if win32: + job.cmake_arguments.append("-DSDLTEST_PROCDUMP=ON") + job.minidump = True + if spec.intel is not None: + match spec.intel: + case IntelCompiler.Icx: + job.cc = "icx" + job.cxx = "icpx" + case IntelCompiler.Icc: + job.cc = "icc" + job.cxx = "icpc" + job.cppflags.append("-diag-disable=10441") + job.clang_tidy = False + case _: + raise ValueError(f"Invalid intel={spec.intel}") + job.source_cmd = f"source /opt/intel/oneapi/setvars.sh;" + job.intel = True + job.shell = "bash" + job.cmake_arguments.extend(( + f"-DCMAKE_C_COMPILER={job.cc}", + f"-DCMAKE_CXX_COMPILER={job.cxx}", + "-DCMAKE_SYSTEM_NAME=Linux", + )) + match spec.platform: + case SdlPlatform.Msvc: + job.setup_ninja = not spec.gdk + job.clang_tidy = False # complains about \threadsafety: "unknown command tag name [clang-diagnostic-documentation-unknown-command]" + job.msvc_project = spec.msvc_project if spec.msvc_project else "" + job.msvc_project_flags.append("-p:TreatWarningsAsError=true") + job.test_pkg_config = False + job.cmake_arguments.extend(( + "-DCMAKE_MSVC_DEBUG_INFORMATION_FORMAT=ProgramDatabase", + "-DCMAKE_EXE_LINKER_FLAGS=-DEBUG", + "-DCMAKE_SHARED_LINKER_FLAGS=-DEBUG", + )) + if spec.uwp: + job.cmake_arguments.append("'-DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreaded$<$:Debug>DLL'") + else: + job.cmake_arguments.append("'-DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreaded$<$:Debug>'") + if spec.clang_cl: + job.cmake_arguments.extend(( + "-DCMAKE_C_COMPILER=clang-cl", + "-DCMAKE_CXX_COMPILER=clang-cl", + )) + match spec.msvc_arch: + case MsvcArch.X86: + job.cflags.append("/clang:-m32") + job.ldflags.append("/MACHINE:X86") + case MsvcArch.X64: + job.cflags.append("/clang:-m64") + job.ldflags.append("/MACHINE:X64") + case _: + raise ValueError(f"Unsupported clang-cl architecture (arch={spec.msvc_arch})") + if spec.msvc_project: + match spec.msvc_arch: + case MsvcArch.X86: + msvc_platform = "Win32" + case MsvcArch.X64: + msvc_platform = "x64" + case _: + raise ValueError(f"Unsupported vcxproj architecture (arch={spec.msvc_arch})") + if spec.gdk: + msvc_platform = f"Gaming.Desktop.{msvc_platform}" + job.msvc_project_flags.append(f"-p:Platform={msvc_platform}") + match spec.msvc_arch: + case MsvcArch.X86: + job.msvc_vcvars = "x64_x86" + case MsvcArch.X64: + job.msvc_vcvars = "x64" + case MsvcArch.Arm32: + job.msvc_vcvars = "x64_arm" + job.run_tests = False + case MsvcArch.Arm64: + job.msvc_vcvars = "x64_arm64" + job.run_tests = False + if spec.uwp: + job.build_tests = False + job.cmake_arguments.extend(( + "-DCMAKE_SYSTEM_NAME=WindowsStore", + "-DCMAKE_SYSTEM_VERSION=10.0", + )) + job.msvc_project_flags.append("-p:WindowsTargetPlatformVersion=10.0.17763.0") + elif spec.gdk: + job.setup_gdk_folder = "VisualC-GDK" + else: + match spec.msvc_arch: + case MsvcArch.X86: + job.setup_libusb_arch = "x86" + case MsvcArch.X64: + job.setup_libusb_arch = "x64" + case SdlPlatform.Linux: + job.apt_packages.extend(( + "gnome-desktop-testing", + "libasound2-dev", + "libpulse-dev", + "libaudio-dev", + "libjack-dev", + "libsndio-dev", + "libusb-1.0-0-dev", + "libx11-dev", + "libxext-dev", + "libxrandr-dev", + "libxcursor-dev", + "libxfixes-dev", + "libxi-dev", + "libxss-dev", + "libwayland-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", + "fcitx-libs-dev", + )) + fpic = True + assert spec.os.value.startswith("ubuntu-") + ubuntu_year, ubuntu_month = [int(v) for v in spec.os.value.removeprefix("ubuntu-").split(".", 1)] + if ubuntu_year >= 22: + job.apt_packages.extend(("libpipewire-0.3-dev", "libdecor-0-dev")) + case SdlPlatform.Ios | SdlPlatform.Tvos: + match spec.platform: + case SdlPlatform.Ios: + job.xcode_sdk = 'iphoneos' + case SdlPlatform.Tvos: + job.xcode_sdk = 'appletvos' + case SdlPlatform.MacOS: + if spec.apple_framework: + job.static = False + job.clang_tidy = False + job.test_pkg_config = False + job.cmake_arguments.extend(( + "'-DCMAKE_OSX_ARCHITECTURES=x86_64;arm64'", + "-DSDL_FRAMEWORK=ON", + )) + else: + job.clang_tidy = True + job.cmake_arguments.extend(( + "-DCMAKE_OSX_ARCHITECTURES=arm64", + "-DCLANG_TIDY_BINARY=$(brew --prefix llvm)/bin/clang-tidy", + )) + job.apt_packages = [] + job.brew_packages.append("ninja") + if job.test_pkg_config: + job.brew_packages.append("pkg-config") + if job.clang_tidy: + job.brew_packages.append("llvm") + case SdlPlatform.Android: + job.android_gradle = spec.android_gradle + job.android_mk = spec.android_mk + job.run_tests = False + if spec.android_mk or not spec.no_cmake: + job.android_ndk = True + if spec.android_gradle or not spec.no_cmake: + job.java = True + if spec.android_mk or spec.android_gradle: + job.apt_packages = [] + if not spec.no_cmake: + job.cmake_arguments.extend(( + f"-DANDROID_PLATFORM={spec.android_platform}", + f"-DANDROID_ABI={spec.android_abi}", + )) + job.cmake_toolchain_file = "${ANDROID_NDK_HOME}/build/cmake/android.toolchain.cmake" + job.cc = f"${{ANDROID_NDK_HOME}}/toolchains/llvm/prebuilt/linux-x86_64/bin/clang --target={spec.android_arch}-none-linux-androideabi{spec.android_platform}" + + job.android_apks = [ + "testaudiorecording-apk", + "testautomation-apk", + "testcontroller-apk", + "testmultiaudio-apk", + "testsprite-apk", + ] + case SdlPlatform.Emscripten: + job.run_tests = False + job.shared = False + job.cmake_config_emulator = "emcmake" + job.cmake_build_type = "Debug" + job.test_pkg_config = False + case SdlPlatform.Ps2: + build_parallel = False + job.shared = False + job.sudo = "" + job.apt_packages = [] + job.apk_packages = ["cmake", "gmp", "mpc1", "mpfr4", "ninja", "pkgconf", "git", ] + job.cmake_toolchain_file = "${PS2DEV}/ps2sdk/ps2dev.cmake" + job.clang_tidy = False + job.run_tests = False + job.shared = False + job.cc = "mips64r5900el-ps2-elf-gcc" + job.ldflags = ["-L${PS2DEV}/ps2sdk/ee/lib", "-L${PS2DEV}/gsKit/lib", "-L${PS2DEV}/ps2sdk/ports/lib", ] + case SdlPlatform.Psp: + build_parallel = False + job.sudo = "" + job.apt_packages = [] + job.apk_packages = ["cmake", "gmp", "mpc1", "mpfr4", "ninja", "pkgconf", ] + job.cmake_toolchain_file = "${PSPDEV}/psp/share/pspdev.cmake" + job.clang_tidy = False + job.run_tests = False + job.shared = False + job.cc = "psp-gcc" + job.ldflags = ["-L${PSPDEV}/lib", "-L${PSPDEV}/psp/lib", "-L${PSPDEV}/psp/sdk/lib", ] + job.pollute_directories = ["${PSPDEV}/include", "${PSPDEV}/psp/include", "${PSPDEV}/psp/sdk/include", ] + case SdlPlatform.Vita: + job.sudo = "" + job.apt_packages = [] + job.apk_packages = ["cmake", "ninja", "pkgconf", "bash", "tar"] + job.cmake_toolchain_file = "${VITASDK}/share/vita.toolchain.cmake" + assert spec.vita_gles is not None + job.setup_vita_gles_type = { + VitaGLES.Pib: "pib", + VitaGLES.Pvr: "pvr", + }[spec.vita_gles] + job.cmake_arguments.extend(( + f"-DVIDEO_VITA_PIB={ 'true' if spec.vita_gles == VitaGLES.Pib else 'false' }", + f"-DVIDEO_VITA_PVR={ 'true' if spec.vita_gles == VitaGLES.Pvr else 'false' }", + "-DSDL_ARMNEON=ON", + "-DSDL_ARMSIMD=ON", + )) + # Fix vita.toolchain.cmake (https://github.com/vitasdk/vita-toolchain/pull/253) + job.source_cmd = r"""sed -i -E "s#set\\( PKG_CONFIG_EXECUTABLE \"\\$\\{VITASDK}/bin/arm-vita-eabi-pkg-config\" \\)#set\\( PKG_CONFIG_EXECUTABLE \"${VITASDK}/bin/arm-vita-eabi-pkg-config\" CACHE PATH \"Path of pkg-config executable\" \\)#" ${VITASDK}/share/vita.toolchain.cmake""" + job.clang_tidy = False + job.run_tests = False + job.shared = False + job.cc = "arm-vita-eabi-gcc" + case SdlPlatform.Haiku: + fpic = False + job.run_tests = False + job.cc = "x86_64-unknown-haiku-gcc" + job.cxx = "x86_64-unknown-haiku-g++" + job.sudo = "" + job.cmake_arguments.extend(( + f"-DCMAKE_C_COMPILER={job.cc}", + f"-DCMAKE_CXX_COMPILER={job.cxx}", + "-DCMAKE_SYSTEM_NAME=Haiku", + )) + case SdlPlatform.PowerPC64: + # FIXME: Enable SDL_WERROR + job.werror = False + job.clang_tidy = False + job.run_tests = False + job.sudo = "" + job.apt_packages = [] + case SdlPlatform.LoongArch64: + job.run_tests = False + job.cc = "${LOONGARCH64_CC}" + job.cxx = "${LOONGARCH64_CXX}" + job.cmake_arguments.extend(( + f"-DCMAKE_C_COMPILER={job.cc}", + f"-DCMAKE_CXX_COMPILER={job.cxx}", + "-DCMAKE_SYSTEM_NAME=Linux", + )) + case SdlPlatform.N3ds: + job.shared = False + job.apt_packages = ["ninja-build"] + job.clang_tidy = False + job.run_tests = False + job.cc_from_cmake = True + job.cmake_toolchain_file = "${DEVKITPRO}/cmake/3DS.cmake" + case SdlPlatform.Msys2: + job.shell = "msys2 {0}" + assert spec.msys2_platform + job.msys2_msystem = spec.msys2_platform.value + job.msys2_env = { + "mingw32": "mingw-w64-i686", + "mingw64": "mingw-w64-x86_64", + "clang32": "mingw-w64-clang-i686", + "clang64": "mingw-w64-clang-x86_64", + "ucrt64": "mingw-w64-ucrt-x86_64", + }[spec.msys2_platform.value] + job.msys2_no_perl = spec.msys2_platform in (Msys2Platform.Mingw32, Msys2Platform.Clang32) + case SdlPlatform.Riscos: + # FIXME: Enable SDL_WERROR + job.werror = False + job.apt_packages = ["cmake", "ninja-build"] + job.test_pkg_config = False + job.shared = False + job.run_tests = False + job.sudo = "" + job.cmake_arguments.extend(( + "-DRISCOS:BOOL=ON", + "-DCMAKE_DISABLE_PRECOMPILE_HEADERS:BOOL=ON", + "-DSDL_GCC_ATOMICS:BOOL=OFF", + )) + job.cmake_toolchain_file = "/home/riscos/env/toolchain-riscos.cmake" + case SdlPlatform.FreeBSD | SdlPlatform.NetBSD: + job.cpactions = True + job.no_cmake = True + job.run_tests = False + job.apt_packages = [] + match spec.platform: + case SdlPlatform.FreeBSD: + job.cpactions_os = "freebsd" + job.cpactions_version = "13.3" + job.cpactions_arch = "x86-64" + job.cpactions_setup_cmd = "sudo pkg update" + job.cpactions_install_cmd = "sudo pkg install -y cmake ninja pkgconf libXcursor libXext libXinerama libXi libXfixes libXrandr libXScrnSaver libXxf86vm wayland wayland-protocols libxkbcommon mesa-libs libglvnd evdev-proto libinotify alsa-lib jackit pipewire pulseaudio sndio dbus zh-fcitx ibus libudev-devd" + job.cmake_arguments.extend(( + "-DSDL_CHECK_REQUIRED_INCLUDES=/usr/local/include", + "-DSDL_CHECK_REQUIRED_LINK_OPTIONS=-L/usr/local/lib", + )) + case SdlPlatform.NetBSD: + job.cpactions_os = "netbsd" + job.cpactions_version = "10.0" + job.cpactions_arch = "x86-64" + job.cpactions_setup_cmd = "export PATH=\"/usr/pkg/sbin:/usr/pkg/bin:/sbin:$PATH\"; export PKG_CONFIG_PATH=\"/usr/pkg/lib/pkgconfig\";export PKG_PATH=\"https://cdn.netBSD.org/pub/pkgsrc/packages/NetBSD/$(uname -p)/$(uname -r|cut -f \"1 2\" -d.)/All/\";echo \"PKG_PATH=$PKG_PATH\";echo \"uname -a -> \"$(uname -a)\"\";sudo -E sysctl -w security.pax.aslr.enabled=0;sudo -E sysctl -w security.pax.aslr.global=0;sudo -E pkgin clean;sudo -E pkgin update" + job.cpactions_install_cmd = "sudo -E pkgin -y install cmake dbus pkgconf ninja-build pulseaudio libxkbcommon wayland wayland-protocols libinotify libusb1" + case _: + raise ValueError(f"Unsupported platform={spec.platform}") + + if not build_parallel: + job.cmake_build_arguments.append("-j1") + if job.cflags: + job.cmake_arguments.append(f"-DCMAKE_C_FLAGS={my_shlex_join(job.cflags)}") + if job.cxxflags: + job.cmake_arguments.append(f"-DCMAKE_CXX_FLAGS={my_shlex_join(job.cxxflags)}") + if job.ldflags: + job.cmake_arguments.append(f"-DCMAKE_SHARED_LINKER_FLAGS=\"{my_shlex_join(job.ldflags)}\"") + job.cmake_arguments.append(f"-DCMAKE_EXE_LINKER_FLAGS=\"{my_shlex_join(job.ldflags)}\"") + + def tf(b): + return "ON" if b else "OFF" + + if fpic is not None: + job.cmake_arguments.append(f"-DCMAKE_POSITION_INDEPENDENT_CODE={tf(fpic)}") + + if job.no_cmake: + job.cmake_arguments = [] + + return job + + +def spec_to_platform(spec: JobSpec, enable_artifacts: bool) -> dict[str, str|bool]: + logger.info("spec=%r", spec) + job = spec_to_job(spec) + logger.info("job=%r", job) + platform = job.to_workflow(enable_artifacts=enable_artifacts) + logger.info("platform=%r", platform) + return platform + + +def main(): + parser = argparse.ArgumentParser(allow_abbrev=False) + parser.add_argument("--github-variable-prefix", default="platforms") + parser.add_argument("--github-ci", action="store_true") + parser.add_argument("--verbose", action="store_true") + parser.add_argument("--commit-message-file") + parser.add_argument("--no-artifact", dest="enable_artifacts", action="store_false") + args = parser.parse_args() + + logging.basicConfig(level=logging.INFO if args.verbose else logging.WARNING) + + remaining_keys = set(JOB_SPECS.keys()) + + all_level_keys = ( + # Level 1 + ( + "haiku", + ), + ) + + filters = [] + if args.commit_message_file: + with open(args.commit_message_file, "r") as f: + commit_message = f.read() + for m in re.finditer(r"\[sdl-ci-filter (.*)]", commit_message, flags=re.M): + filters.append(m.group(1).strip(" \t\n\r\t'\"")) + + if re.search(r"\[sdl-ci-artifacts?]", commit_message, flags=re.M): + args.enable_artifacts = True + + if not filters: + filters.append("*") + + logger.info("filters: %r", filters) + + all_level_platforms = {} + + all_platforms = {k: spec_to_platform(spec, enable_artifacts=args.enable_artifacts) for k, spec in JOB_SPECS.items()} + + for level_i, level_keys in enumerate(all_level_keys, 1): + level_key = f"level{level_i}" + logger.info("Level %d: keys=%r", level_i, level_keys) + assert all(k in remaining_keys for k in level_keys) + level_platforms = tuple(all_platforms[key] for key in level_keys) + remaining_keys.difference_update(level_keys) + all_level_platforms[level_key] = level_platforms + logger.info("=" * 80) + + logger.info("Keys before filter: %r", remaining_keys) + + filtered_remaining_keys = set() + for filter in filters: + filtered_remaining_keys.update(fnmatch.filter(remaining_keys, filter)) + + logger.info("Keys after filter: %r", filtered_remaining_keys) + + remaining_keys = filtered_remaining_keys + + logger.info("Remaining: %r", remaining_keys) + all_level_platforms["others"] = tuple(all_platforms[key] for key in remaining_keys) + + if args.github_ci: + for level, platforms in all_level_platforms.items(): + platforms_json = json.dumps(platforms) + txt = f"{args.github_variable_prefix}-{level}={platforms_json}" + logger.info("%s", txt) + if "GITHUB_OUTPUT" in os.environ: + with open(os.environ["GITHUB_OUTPUT"], "a") as f: + f.write(txt) + f.write("\n") + else: + logger.warning("GITHUB_OUTPUT not defined") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/.github/workflows/generic.yml b/.github/workflows/generic.yml new file mode 100644 index 0000000000000..2798ec7cda98b --- /dev/null +++ b/.github/workflows/generic.yml @@ -0,0 +1,342 @@ +name: 'Build' +run-name: 'Configure, Build and Test SDL' + +on: + workflow_call: + inputs: + platforms: + description: 'JSON-encoded test properties' + type: string + required: true + +jobs: + build: + name: ${{ matrix.platform.name }} + runs-on: ${{ matrix.platform.os }} + container: ${{ matrix.platform.container }} + defaults: + run: + shell: ${{ matrix.platform.shell }} + strategy: + fail-fast: false + matrix: + platform: ${{ fromJSON(inputs.platforms) }} + steps: + - name: 'Set up MSYS2' + if: ${{ matrix.platform.platform == 'msys2' }} + uses: msys2/setup-msys2@v2 + with: + msystem: ${{ matrix.platform.msys2-msystem }} + install: >- + ${{ matrix.platform.msys2-env }}-cc + ${{ matrix.platform.msys2-env }}-cmake + ${{ matrix.platform.msys2-env }}-ninja + ${{ (!matrix.platform.msys2-no-perl && format('{0}-perl', matrix.platform.msys2-env)) || '' }} + ${{ matrix.platform.msys2-env }}-pkg-config + ${{ matrix.platform.msys2-env }}-clang-tools-extra + - uses: actions/checkout@v4 + - name: 'Set up ninja' + if: ${{ matrix.platform.setup-ninja }} + uses: ./.github/actions/setup-ninja + - name: 'Set up libusb for MSVC' + if: ${{ matrix.platform.setup-libusb-arch != '' }} + uses: ./.github/actions/setup-msvc-libusb + with: + arch: ${{ matrix.platform.setup-libusb-arch }} + - uses: mymindstorm/setup-emsdk@v14 + if: ${{ matrix.platform.platform == 'emscripten' }} + with: + version: 3.1.35 + - uses: nttld/setup-ndk@v1 + if: ${{ matrix.platform.android-ndk }} + id: setup-ndk + with: + local-cache: true + ndk-version: r21e + - name: 'Configure Android NDK variables' + if: ${{ matrix.platform.android-ndk }} + shell: sh + 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 + if: ${{ matrix.platform.java }} + with: + distribution: 'temurin' + java-version: '17' + - uses: ilammy/msvc-dev-cmd@v1 + if: ${{ matrix.platform.platform == 'msvc' }} + with: + arch: ${{ matrix.platform.msvc-vcvars }} + - name: 'Set up Windows GDK Desktop' + uses: ./.github/actions/setup-gdk-desktop + if: ${{ matrix.platform.setup-gdk-folder != '' }} + with: + folder: '${{ matrix.platform.setup-gdk-folder }}' + - name: 'Set up LoongArch64 toolchain' + uses: ./.github/actions/setup-loongarch64-toolchain + id: setup-loongarch64-toolchain + if: ${{ matrix.platform.platform == 'loongarch64' }} + - name: 'Setup Intel oneAPI toolchain' + id: intel + if: ${{ matrix.platform.intel }} + run: | + # Download the key to system keyring + wget -O- https://apt.repos.intel.com/intel-gpg-keys/GPG-PUB-KEY-INTEL-SW-PRODUCTS.PUB \ + | gpg --dearmor | sudo tee /usr/share/keyrings/oneapi-archive-keyring.gpg > /dev/null + + # Add signed entry to apt sources and configure the APT client to use Intel repository: + echo "deb [signed-by=/usr/share/keyrings/oneapi-archive-keyring.gpg] https://apt.repos.intel.com/oneapi all main" | sudo tee /etc/apt/sources.list.d/oneAPI.list + + # Update package list + sudo apt-get update -y + + # Install oneAPI + sudo apt-get install -y intel-oneapi-compiler-dpcpp-cpp-and-cpp-classic + - name: 'Install apk packages' + if: ${{ matrix.platform.apk-packages != '' }} + run: | + ${{ matrix.platform.sudo }} apk update + ${{ matrix.platform.sudo }} apk add ${{ matrix.platform.apk-packages }} + - name: 'Install apt packages' + if: ${{ matrix.platform.apt-packages != '' }} + run: | + ${{ matrix.platform.sudo }} apt-get update + ${{ matrix.platform.sudo }} apt-get install -y ${{ matrix.platform.apt-packages }} + - name: 'Install brew packages' + if: ${{ matrix.platform.brew-packages != '' }} + run: | + export HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK=1 + brew update + brew install ${{ matrix.platform.brew-packages }} + - name: 'Set up GLES for VITA' # Must be after apk + if: ${{ matrix.platform.setup-vita-gles-type != '' }} + uses: ./.github/actions/setup-vita-gles + with: + type: ${{ matrix.platform.setup-vita-gles-type }} + + - name: 'Pollute toolchain with "bad SDL headers' + if: ${{ matrix.platform.pollute-directories != '' }} + #shell: ${{ matrix.platform.shell }} + run: | + # Create "bad" SDL headers in the toolchain. + # SDL sources should not use these. + for include in ${{ matrix.platform.pollute-directories }}; do + toolchain_directory="${include}/SDL3" + echo "Creating directory ${toolchain_directory}" + mkdir -p "${toolchain_directory}/SDL3" + for header in include/SDL3/*.h; do + dest="${toolchain_directory}/SDL3/$(basename "${header}")" + echo "Creating ${dest}" + echo '#error "System SDL headers must not be used by build system"' >"$dest" + done + done + + - name: 'Configure (CMake)' + if: ${{ !matrix.platform.no-cmake }} + #shell: ${{ matrix.platform.shell }} + run: | + ${{ matrix.platform.source-cmd }} + ${{ matrix.platform.cmake-config-emulator }} cmake -S . -B build -GNinja \ + -Wdeprecated -Wdev -Werror \ + ${{ matrix.platform.cmake-toolchain-file != '' && format('-DCMAKE_TOOLCHAIN_FILE={0}', matrix.platform.cmake-toolchain-file) || '' }} \ + -DSDL_WERROR=${{ matrix.platform.werror }} \ + -DSDL_EXAMPLES=${{ matrix.platform.build-tests }} \ + -DSDL_TESTS=${{ matrix.platform.build-tests }} \ + -DSDLTEST_TRACKMEM=ON \ + -DSDL_INSTALL_TESTS=${{ matrix.platform.build-tests }} \ + -DSDL_CLANG_TIDY=${{ matrix.platform.clang-tidy }} \ + -DSDL_DISABLE_INSTALL_DOCS=OFF \ + -DSDL_DISABLE_INSTALL_CPACK=OFF \ + -DSDL_DISABLE_INSTALL_DOCS=OFF \ + ${{ matrix.platform.cmake-arguments }} \ + -DSDL_SHARED=${{ matrix.platform.shared }} \ + -DSDL_STATIC=${{ matrix.platform.static }} \ + -DSDL_VENDOR_INFO="Github Workflow" \ + -DCMAKE_INSTALL_PREFIX=prefix \ + -DCMAKE_INSTALL_LIBDIR=lib \ + -DCMAKE_BUILD_TYPE=${{ matrix.platform.cmake-build-type }} + - name: 'Build (CMake)' + id: build + if: ${{ !matrix.platform.no-cmake }} +# shell: ${{ matrix.platform.shell }} + run: | + ${{ matrix.platform.source-cmd }} + cmake --build build --config ${{ matrix.platform.cmake-build-type }} --verbose -- ${{ matrix.platform.cmake-build-arguments }} + - name: 'Run build-time tests (CMake)' + id: tests + if: ${{ !matrix.platform.no-cmake && matrix.platform.run-tests }} +# shell: ${{ matrix.platform.shell }} + run: | + ${{ matrix.platform.source-cmd }} + set -eu + export SDL_TESTS_QUICK=1 + ctest -VV --test-dir build/ -j2 + if test "${{ runner.os }}" = "Linux"; then + # This should show us the SDL_REVISION + strings build/libSDL3.so.0 | grep SDL- + fi + - name: "Build test apk's (CMake)" + id: apks + if: ${{ steps.build.outcome == 'success' && matrix.platform.android-apks != '' }} +# shell: ${{ matrix.platform.shell }} + run: | + ${{ matrix.platform.source-cmd }} + cmake --build build --config ${{ matrix.platform.cmake-build-type }} \ + --target \ + ${{ matrix.platform.android-apks }} \ + --verbose \ + -- ${{ matrix.platform.cmake-build-arguments }} + - name: 'Install (CMake)' + id: install + if: ${{ steps.build.outcome == 'success' }} +# shell: ${{ matrix.platform.shell }} + run: | + ${{ matrix.platform.source-cmd }} + cmake --install build --config ${{ matrix.platform.cmake-build-type }} + echo "prefix=$(pwd)/prefix" >> $GITHUB_OUTPUT + ( cd prefix; find . ) | LC_ALL=C sort -u + - name: 'Package (CPack)' + id: package + if: ${{ steps.build.outcome == 'success' }} +# shell: ${{ matrix.platform.shell }} + run: | + # DMG creation on macOS occasionally fails, so try multiple times + # https://gitlab.kitware.com/cmake/cmake/-/issues/25671 + success=0 + max_tries=10 + for i in $(seq $max_tries); do + cmake --build build/ --config ${{ matrix.platform.cmake-build-type }} --target package -- ${{ matrix.platform.cmake-build-arguments }} && success=1 + if test $success = 1; then + break + fi + echo "Package creation failed. Sleep 1 second and try again." + sleep 1 + done + if test $success = 0; then + echo "Package creation failed after $max_tries attempts." + exit 1 + fi + - name: 'Verify CMake configuration files' + if: ${{ steps.install.outcome == 'success' }} +# shell: ${{ matrix.platform.shell }} + run: | + ${{ matrix.platform.source-cmd }} + ${{ matrix.platform.cmake-config-emulator }} cmake -S cmake/test -B cmake_test_build -GNinja \ + ${{ matrix.platform.cmake-toolchain-file != '' && format('-DCMAKE_TOOLCHAIN_FILE={0}', matrix.platform.cmake-toolchain-file) || '' }} \ + -DTEST_SHARED=${{ matrix.platform.shared }} \ + -DTEST_STATIC=${{ matrix.platform.static }} \ + ${{ matrix.platform.cmake-arguments }} \ + -DCMAKE_BUILD_TYPE=${{ matrix.platform.cmake-build-type }} \ + -DCMAKE_PREFIX_PATH="${{ steps.install.outputs.prefix }}" + cmake --build cmake_test_build --verbose --config ${{ matrix.platform.cmake-build-type }} -- ${{ matrix.platform.cmake-build-arguments }} + - name: 'Extract CC/CXX/CFLAGS/CXXFLAGS from CMake toolchain' + if: ${{ steps.install.outcome == 'success' && matrix.platform.cc-from-cmake }} +# shell: ${{ matrix.platform.shell }} + run: | + cmake -S .github/cmake -B /tmp/cmake_extract \ + ${{ matrix.platform.cmake-toolchain-file != '' && format('-DCMAKE_TOOLCHAIN_FILE={0}', matrix.platform.cmake-toolchain-file) || '' }} \ + -DCMAKE_BUILD_TYPE=${{ matrix.platform.cmake-build-type }} \ + -DVAR_PATH=/tmp/env.txt + cat /tmp/env.txt >> $GITHUB_ENV + - name: 'Verify sdl3.pc' +# shell: ${{ matrix.platform.shell }} + if: ${{ steps.install.outcome == 'success' && matrix.platform.test-pkg-config }} + run: | + ${{ matrix.platform.source-cmd }} + ${{ matrix.platform.cc && format('export CC="{0}"', matrix.platform.cc) || '' }} + ${{ matrix.platform.cflags && format('export CFLAGS="{0}"', matrix.platform.cflags) || '' }} + ${{ matrix.platform.ldflags && format('export LDFLAGS="{0}"', matrix.platform.ldflags) || '' }} + export PKG_CONFIG_PATH=${{ steps.install.outputs.prefix }}/lib/pkgconfig + cmake/test/test_pkgconfig.sh + - name: 'Build (cross-platform-actions, BSD)' + id: cpactions + if: ${{ matrix.platform.cpactions }} + uses: cross-platform-actions/action@v0.25.0 + with: + operating_system: ${{ matrix.platform.cpactions-os }} + architecture: ${{ matrix.platform.cpactions-arch }} + version: ${{ matrix.platform.cpactions-version }} + run: | + ${{ matrix.platform.cpactions-setup-cmd }} + ${{ matrix.platform.cpactions-install-cmd }} + cmake -S . -B build -GNinja \ + ${{ matrix.platform.cmake-toolchain-file != '' && format('-DCMAKE_TOOLCHAIN_FILE={0}', matrix.platform.cmake-toolchain-file) || '' }} \ + -Wdeprecated -Wdev -Werror \ + -DSDL_WERROR=${{ matrix.platform.werror }} \ + -DSDL_DISABLE_INSTALL_DOCS=OFF \ + ${{ matrix.platform.cmake-arguments }} \ + -DSDL_SHARED=${{ matrix.platform.shared }} \ + -DSDL_STATIC=${{ matrix.platform.static }} \ + -DSDL_VENDOR_INFO="Github Workflow" \ + -DCMAKE_INSTALL_PREFIX=prefix \ + -DCMAKE_INSTALL_LIBDIR=lib \ + -DCMAKE_BUILD_TYPE=${{ matrix.platform.cmake-build-type }} + cmake --build build/ --config ${{ matrix.platform.cmake-build-type }} --verbose + cmake --build build/ --config ${{ matrix.platform.cmake-build-type }} --target package + + cmake --build build/ --config ${{ matrix.platform.cmake-build-type }} --target clean + rm -rf build/dist/_CPack_Packages + rm -rf build/CMakeFiles + rm -rf build/docs + - name: Add msbuild to PATH + id: setup-msbuild + if: ${{ matrix.platform.msvc-project != '' }} + uses: microsoft/setup-msbuild@v2 + - name: Build msbuild + if: ${{ matrix.platform.msvc-project != '' }} + run: | + "$(cygpath -u '${{ steps.setup-msbuild.outputs.msbuildPath }}\msbuild.exe')" ${{ matrix.platform.msvc-project }} -m -p:BuildInParallel=true -p:Configuration=Release ${{ matrix.platform.msvc-project-flags }} + - name: 'Build (Android.mk)' + if: ${{ matrix.platform.android-mk }} + run: | + ./build-scripts/androidbuildlibs.sh + - name: 'Create Gradle project (Android)' + if: ${{ matrix.platform.android-gradle }} + run: | + for folder in build-ndk-build build-cmake; do + python build-scripts/create-android-project.py \ + --output "${folder}" \ + --variant copy \ + org.libsdl.testspriteminimal \ + test/testspriteminimal.c test/icon.h + done + echo "" + echo "Project contents:" + echo "" + find "build-ndk-build/org.libsdl.testspriteminimal" + - name: 'Build Android app (Gradle & ndk-build)' + if: ${{ matrix.platform.android-gradle }} + run: | + cd build-ndk-build/org.libsdl.testspriteminimal + ./gradlew -i assembleRelease + - name: 'Build Android app (Gradle & CMake)' + if: ${{ matrix.platform.android-gradle }} + run: | + cd build-cmake/org.libsdl.testspriteminimal + ./gradlew -i assembleRelease -PBUILD_WITH_CMAKE=1 + - name: 'Build (xcode)' + if: ${{ matrix.platform.xcode-sdk != '' }} + run: | + xcodebuild -project Xcode/SDL/SDL.xcodeproj -target SDL3 -configuration Release -sdk ${{ matrix.platform.xcode-sdk }} clean build + - name: 'Upload binary package' + uses: actions/upload-artifact@v4 + 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 + name: '${{ matrix.platform.artifact }}' + path: build/dist/SDL3* + - name: 'Upload minidumps' + uses: actions/upload-artifact@v4 + 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 + if: ${{ matrix.platform.enable-artifacts && always() && matrix.platform.artifact != '' && steps.apks.outcome == 'success' }} + with: + if-no-files-found: error + name: '${{ matrix.platform.artifact }}-apks' + path: build/test/*.apk \ No newline at end of file diff --git a/.gitignore b/.gitignore index 1bbf37de7ac73..0482346c382a6 100644 --- a/.gitignore +++ b/.gitignore @@ -84,6 +84,7 @@ VisualC/tests/testyuv/testyuv.bmp VisualC-GDK/**/Layout src/render/direct3d12/D3D12_*_One.h src/render/direct3d12/D3D12_*_Series.h +Directory.Build.props # for Android android-project/local.properties diff --git a/CMakeLists.txt b/CMakeLists.txt index 1989bfae73823..dd8ce5dba838c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -327,7 +327,9 @@ dep_option(SDL_COCOA "Use Cocoa video driver" ON "APPLE" OFF) dep_option(SDL_DIRECTX "Use DirectX for Windows audio/video" ON "SDL_AUDIO OR SDL_VIDEO;WINDOWS" OFF) dep_option(SDL_XINPUT "Use Xinput for Windows" ON "WINDOWS" OFF) dep_option(SDL_WASAPI "Use the Windows WASAPI audio driver" ON "WINDOWS;SDL_AUDIO" OFF) -dep_option(SDL_RENDER_D3D "Enable the Direct3D render driver" ON "SDL_RENDER" OFF) +dep_option(SDL_RENDER_D3D "Enable the Direct3D 9 render driver" ON "SDL_RENDER;SDL_DIRECTX" OFF) +dep_option(SDL_RENDER_D3D11 "Enable the Direct3D 11 render driver" ON "SDL_RENDER;SDL_DIRECTX" OFF) +dep_option(SDL_RENDER_D3D12 "Enable the Direct3D 12 render driver" ON "SDL_RENDER;SDL_DIRECTX" OFF) dep_option(SDL_RENDER_METAL "Enable the Metal render driver" ON "SDL_RENDER;${APPLE}" OFF) dep_option(SDL_VIVANTE "Use Vivante EGL video driver" ON "${UNIX_SYS};SDL_CPU_ARM32" OFF) dep_option(SDL_VULKAN "Enable Vulkan support" ON "SDL_VIDEO;ANDROID OR APPLE OR LINUX OR FREEBSD OR WINDOWS" OFF) @@ -1246,9 +1248,6 @@ if(ANDROID) endif() if(SDL_AUDIO) - set(SDL_AUDIO_DRIVER_ANDROID 1) - sdl_glob_sources("${SDL3_SOURCE_DIR}/src/audio/android/*.c") - set(SDL_AUDIO_DRIVER_OPENSLES 1) sdl_glob_sources("${SDL3_SOURCE_DIR}/src/audio/openslES/*.c") @@ -1841,14 +1840,8 @@ elseif(WINDOWS) string(APPEND CMAKE_REQUIRED_FLAGS " /I\"$ENV{DXSDK_DIR}\\Include\"") endif() - check_include_file(d3d9.h HAVE_D3D_H) + check_include_file(d3d9.h HAVE_D3D9_H) check_include_file(d3d11_1.h HAVE_D3D11_H) - check_c_source_compiles(" - #include - #include - ID3D12Device1 *device; - int main(int argc, char **argv) { return 0; } - " HAVE_D3D12_H) check_include_file(ddraw.h HAVE_DDRAW_H) check_include_file(dsound.h HAVE_DSOUND_H) check_include_file(dinput.h HAVE_DINPUT_H) @@ -1857,7 +1850,7 @@ elseif(WINDOWS) endif() check_include_file(dxgi.h HAVE_DXGI_H) cmake_pop_check_state() - if(HAVE_D3D_H OR HAVE_D3D11_H OR HAVE_D3D12_H OR HAVE_DDRAW_H OR HAVE_DSOUND_H OR HAVE_DINPUT_H) + if(HAVE_D3D9_H OR HAVE_D3D11_H OR HAVE_DDRAW_H OR HAVE_DSOUND_H OR HAVE_DINPUT_H) set(HAVE_DIRECTX TRUE) if(NOT MINGW AND NOT USE_WINSDK_DIRECTX) if(CMAKE_SIZEOF_VOID_P EQUAL 8) @@ -1877,14 +1870,21 @@ elseif(WINDOWS) #include #include int main(int argc, char **argv) { return 0; }" HAVE_XINPUT_H) - check_c_source_compiles(" - #define COBJMACROS - #include - __x_ABI_CWindows_CGaming_CInput_CIGamepadStatics2 *s2; - int main(int argc, char **argv) { return 0; }" HAVE_WINDOWS_GAMING_INPUT_H) endif() # headers needed elsewhere + check_c_source_compiles(" + #define COBJMACROS + #include + static __x_ABI_CWindows_CGaming_CInput_CIGamepadStatics2 *s2; + int main(int argc, char **argv) { return 0; }" HAVE_WINDOWS_GAMING_INPUT_H + ) + check_c_source_compiles(" + #include + #define COBJMACROS + #include + int main(int argc, char **argv) { return 0; }" HAVE_GAMEINPUT_H + ) check_include_file(dxgi1_6.h HAVE_DXGI1_6_H) check_include_file(tpcshrd.h HAVE_TPCSHRD_H) check_include_file(roapi.h HAVE_ROAPI_H) @@ -1932,17 +1932,17 @@ elseif(WINDOWS) sdl_glob_sources("${SDL3_SOURCE_DIR}/src/video/windows/*.c") endif() - if(SDL_RENDER_D3D AND HAVE_D3D_H AND NOT WINDOWS_STORE) + if(SDL_RENDER_D3D AND HAVE_D3D9_H AND NOT WINDOWS_STORE) set(SDL_VIDEO_RENDER_D3D 1) set(HAVE_RENDER_D3D TRUE) endif() - if(SDL_RENDER_D3D AND HAVE_D3D11_H) + if(SDL_RENDER_D3D11 AND HAVE_D3D11_H) set(SDL_VIDEO_RENDER_D3D11 1) - set(HAVE_RENDER_D3D TRUE) + set(HAVE_RENDER_D3D11 TRUE) endif() - if(SDL_RENDER_D3D AND HAVE_D3D12_H AND NOT WINDOWS_STORE) + if(SDL_RENDER_D3D12 AND NOT WINDOWS_STORE) set(SDL_VIDEO_RENDER_D3D12 1) - set(HAVE_RENDER_D3D TRUE) + set(HAVE_RENDER_D3D12 TRUE) endif() set(HAVE_SDL_VIDEO TRUE) endif() @@ -2091,9 +2091,13 @@ elseif(WINDOWS) set(SDL_JOYSTICK_XINPUT 1) set(HAVE_XINPUT TRUE) endif() - if(HAVE_WINDOWS_GAMING_INPUT_H) - set(SDL_JOYSTICK_WGI 1) - endif() + endif() + if(HAVE_WINDOWS_GAMING_INPUT_H) + set(SDL_JOYSTICK_WGI 1) + endif() + if(HAVE_GAMEINPUT_H) + sdl_glob_sources("${SDL3_SOURCE_DIR}/src/joystick/gdk/*.c") + set(SDL_JOYSTICK_GAMEINPUT 1) endif() set(HAVE_SDL_JOYSTICK TRUE) @@ -2347,77 +2351,80 @@ elseif(APPLE) endif() endif() + # Minimum version for $ + cmake_minimum_required(VERSION 3.24) + # Actually load the frameworks at the end so we don't duplicate include. if(SDL_FRAMEWORK_COREVIDEO) find_library(COREMEDIA CoreMedia) if(COREMEDIA) - sdl_link_dependency(corevideo LINK_OPTIONS "-Wl,-framework,CoreMedia") + sdl_link_dependency(corevideo LIBS "$" PKG_CONFIG_LINK_OPTIONS "-Wl,-framework,CoreMedia") endif() - sdl_link_dependency(corevideo LINK_OPTIONS "-Wl,-framework,CoreVideo") + sdl_link_dependency(corevideo LIBS "$" PKG_CONFIG_LINK_OPTIONS "-Wl,-framework,CoreVideo") endif() if(SDL_FRAMEWORK_COCOA) - sdl_link_dependency(cocoa LINK_OPTIONS "-Wl,-framework,Cocoa") + sdl_link_dependency(cocoa LIBS "$" PKG_CONFIG_LINK_OPTIONS "-Wl,-framework,Cocoa") endif() if(SDL_FRAMEWORK_IOKIT) - sdl_link_dependency(iokit LINK_OPTIONS "-Wl,-framework,IOKit") + sdl_link_dependency(iokit LIBS "$" PKG_CONFIG_LINK_OPTIONS "-Wl,-framework,IOKit") endif() if(SDL_FRAMEWORK_FF) - sdl_link_dependency(ff LINK_OPTIONS "-Wl,-framework,ForceFeedback") + sdl_link_dependency(ff LIBS "$" PKG_CONFIG_LINK_OPTIONS "-Wl,-framework,ForceFeedback") endif() if(SDL_FRAMEWORK_CARBON) - sdl_link_dependency(carbon LINK_OPTIONS "-Wl,-framework,Carbon") + sdl_link_dependency(carbon LIBS "$" PKG_CONFIG_LINK_OPTIONS "-Wl,-framework,Carbon") endif() if(SDL_FRAMEWORK_COREAUDIO) - sdl_link_dependency(core_audio LINK_OPTIONS "-Wl,-framework,CoreAudio") + sdl_link_dependency(core_audio LIBS "$" PKG_CONFIG_LINK_OPTIONS "-Wl,-framework,CoreAudio") endif() if(SDL_FRAMEWORK_AUDIOTOOLBOX) - sdl_link_dependency(audio_toolbox LINK_OPTIONS "-Wl,-framework,AudioToolbox") + sdl_link_dependency(audio_toolbox LIBS "$" PKG_CONFIG_LINK_OPTIONS "-Wl,-framework,AudioToolbox") endif() if(SDL_FRAMEWORK_AVFOUNDATION) - sdl_link_dependency(av_foundation LINK_OPTIONS "-Wl,-framework,AVFoundation") + sdl_link_dependency(av_foundation LIBS "$" PKG_CONFIG_LINK_OPTIONS "-Wl,-framework,AVFoundation") endif() if(SDL_FRAMEWORK_COREBLUETOOTH) - sdl_link_dependency(core_bluetooth LINK_OPTIONS "-Wl,-framework,CoreBluetooth") + sdl_link_dependency(core_bluetooth LIBS "$" PKG_CONFIG_LINK_OPTIONS "-Wl,-framework,CoreBluetooth") endif() if(SDL_FRAMEWORK_COREGRAPHICS) - sdl_link_dependency(core_graphics LINK_OPTIONS "-Wl,-framework,CoreGraphics") + sdl_link_dependency(core_graphics LIBS "$" PKG_CONFIG_LINK_OPTIONS "-Wl,-framework,CoreGraphics") endif() if(SDL_FRAMEWORK_COREMOTION) - sdl_link_dependency(core_motion LINK_OPTIONS "-Wl,-framework,CoreMotion") + sdl_link_dependency(core_motion LIBS "$" PKG_CONFIG_LINK_OPTIONS "-Wl,-framework,CoreMotion") endif() if(SDL_FRAMEWORK_FOUNDATION) - sdl_link_dependency(foundation LINK_OPTIONS "-Wl,-framework,Foundation") + sdl_link_dependency(foundation LIBS "$" PKG_CONFIG_LINK_OPTIONS "-Wl,-framework,Foundation") endif() if(SDL_FRAMEWORK_GAMECONTROLLER) find_library(GAMECONTROLLER GameController) if(GAMECONTROLLER) - sdl_link_dependency(game_controller LINK_OPTIONS "-Wl,-weak_framework,GameController") + sdl_link_dependency(game_controller LIBS "$" PKG_CONFIG_LINK_OPTIONS "-Wl,-weak_framework,GameController") endif() endif() if(SDL_FRAMEWORK_METAL) if(IOS OR TVOS OR VISIONOS OR WATCHOS) - sdl_link_dependency(metal LINK_OPTIONS "-Wl,-framework,Metal") + sdl_link_dependency(metal LIBS "$" PKG_CONFIG_LINK_OPTIONS "-Wl,-framework,Metal") else() - sdl_link_dependency(metal LINK_OPTIONS "-Wl,-weak_framework,Metal") + sdl_link_dependency(metal LIBS "$" PKG_CONFIG_LINK_OPTIONS "-Wl,-weak_framework,Metal") endif() endif() if(SDL_FRAMEWORK_OPENGLES) - sdl_link_dependency(opengles LINK_OPTIONS "-Wl,-framework,OpenGLES") + sdl_link_dependency(opengles LIBS "$" PKG_CONFIG_LINK_OPTIONS "-Wl,-framework,OpenGLES") endif() if(SDL_FRAMEWORK_QUARTZCORE) if(IOS OR TVOS OR VISIONOS OR WATCHOS) - sdl_link_dependency(quartz_core LINK_OPTIONS "-Wl,-framework,QuartzCore") + sdl_link_dependency(quartz_core LIBS "$" PKG_CONFIG_LINK_OPTIONS "-Wl,-framework,QuartzCore") else() - sdl_link_dependency(metal LINK_OPTIONS "-Wl,-weak_framework,QuartzCore") + sdl_link_dependency(metal LIBS "$" PKG_CONFIG_LINK_OPTIONS "-Wl,-weak_framework,QuartzCore") endif() endif() if(SDL_FRAMEWORK_UIKIT) - sdl_link_dependency(ui_kit LINK_OPTIONS "-Wl,-framework,UIKit") + sdl_link_dependency(ui_kit LIBS "$" PKG_CONFIG_LINK_OPTIONS "-Wl,-framework,UIKit") endif() if(SDL_FRAMEWORK_COREHAPTICS) find_library(COREHAPTICS CoreHaptics) if(COREHAPTICS) - sdl_link_dependency(core_haptics LINK_OPTIONS "-Wl,-framework,CoreHaptics") + sdl_link_dependency(core_haptics LIBS "$" PKG_CONFIG_LINK_OPTIONS "-Wl,-framework,CoreHaptics") endif() endif() @@ -2885,7 +2892,7 @@ if (SDL_DIALOG) set(HAVE_SDL_DIALOG TRUE) elseif(MACOS) sdl_sources(${SDL3_SOURCE_DIR}/src/dialog/cocoa/SDL_cocoadialog.m) - sdl_link_dependency(uniformtypeidentifiers LINK_OPTIONS "-Wl,-weak_framework,UniformTypeIdentifiers") + sdl_link_dependency(uniformtypeidentifiers LIBS "$" PKG_CONFIG_LINK_OPTIONS "-Wl,-weak_framework,UniformTypeIdentifiers") set(HAVE_SDL_DIALOG TRUE) endif() endif() @@ -3583,8 +3590,11 @@ if(NOT SDL_DISABLE_INSTALL) if(ANDROID) if(TARGET SDL3-jar) set(SDL_INSTALL_JAVADIR "${CMAKE_INSTALL_DATAROOTDIR}/java" CACHE PATH "Path where to install java clases + java sources") + set(PROGUARD_RULES_PATH "${CMAKE_CURRENT_SOURCE_DIR}/android-project/app/proguard-rules.pro") install(FILES $ DESTINATION "${SDL_INSTALL_JAVADIR}/SDL3") + install(FILES "${PROGUARD_RULES_PATH}" RENAME "proguard.txt" + DESTINATION "${SDL_INSTALL_JAVADIR}/SDL3") configure_package_config_file(cmake/SDL3jarTargets.cmake.in SDL3jarTargets.cmake INSTALL_DESTINATION "${SDL_SDL_INSTALL_CMAKEDIR}" PATH_VARS SDL_INSTALL_JAVADIR diff --git a/VisualC-GDK/SDL/SDL.vcxproj b/VisualC-GDK/SDL/SDL.vcxproj index dd9a4600b0ae5..8d038f6d3153c 100644 --- a/VisualC-GDK/SDL/SDL.vcxproj +++ b/VisualC-GDK/SDL/SDL.vcxproj @@ -306,6 +306,11 @@ Building shader blobs (Xbox One) + + + $(TreatWarningsAsError) + + @@ -408,6 +413,7 @@ + @@ -567,6 +573,7 @@ + @@ -631,6 +638,7 @@ NotUsing NotUsing + @@ -843,6 +851,7 @@ + diff --git a/VisualC-GDK/SDL/SDL.vcxproj.filters b/VisualC-GDK/SDL/SDL.vcxproj.filters index 4260bfcd900a9..fbd091f59066e 100644 --- a/VisualC-GDK/SDL/SDL.vcxproj.filters +++ b/VisualC-GDK/SDL/SDL.vcxproj.filters @@ -43,6 +43,7 @@ + @@ -216,6 +217,7 @@ + @@ -326,6 +328,7 @@ + @@ -445,6 +448,7 @@ + diff --git a/VisualC-GDK/SDL_test/SDL_test.vcxproj b/VisualC-GDK/SDL_test/SDL_test.vcxproj index 2c7ada3ca0148..7505baf8602c6 100644 --- a/VisualC-GDK/SDL_test/SDL_test.vcxproj +++ b/VisualC-GDK/SDL_test/SDL_test.vcxproj @@ -184,6 +184,11 @@ true + + + $(TreatWarningsAsError) + + diff --git a/VisualC-GDK/tests/testcontroller/testcontroller.vcxproj b/VisualC-GDK/tests/testcontroller/testcontroller.vcxproj index 51f1d92cdb548..d7ad95cf244c7 100644 --- a/VisualC-GDK/tests/testcontroller/testcontroller.vcxproj +++ b/VisualC-GDK/tests/testcontroller/testcontroller.vcxproj @@ -260,6 +260,11 @@ xgameruntime.lib;%(AdditionalDependencies) + + + $(TreatWarningsAsError) + + {81ce8daf-ebb2-4761-8e45-b71abcca8c68} diff --git a/VisualC-GDK/tests/testgdk/testgdk.vcxproj b/VisualC-GDK/tests/testgdk/testgdk.vcxproj index 27b9662d677b2..41293cadae55e 100644 --- a/VisualC-GDK/tests/testgdk/testgdk.vcxproj +++ b/VisualC-GDK/tests/testgdk/testgdk.vcxproj @@ -284,6 +284,11 @@ + + + $(TreatWarningsAsError) + + {81ce8daf-ebb2-4761-8e45-b71abcca8c68} diff --git a/VisualC-GDK/tests/testsprite/testsprite.vcxproj b/VisualC-GDK/tests/testsprite/testsprite.vcxproj index a3af667e76335..3885aec28aa3f 100644 --- a/VisualC-GDK/tests/testsprite/testsprite.vcxproj +++ b/VisualC-GDK/tests/testsprite/testsprite.vcxproj @@ -284,6 +284,11 @@ + + + $(TreatWarningsAsError) + + {81ce8daf-ebb2-4761-8e45-b71abcca8c68} diff --git a/VisualC-WinRT/SDL-UWP.vcxproj b/VisualC-WinRT/SDL-UWP.vcxproj index a80d0924bab2a..7a5c81952e71e 100644 --- a/VisualC-WinRT/SDL-UWP.vcxproj +++ b/VisualC-WinRT/SDL-UWP.vcxproj @@ -114,6 +114,7 @@ + @@ -316,6 +317,7 @@ NotUsing + @@ -914,6 +916,11 @@ /nodefaultlib:vccorlib /nodefaultlib:msvcrt vccorlib.lib msvcrt.lib %(AdditionalOptions) + + + $(TreatWarningsAsError) + + diff --git a/VisualC-WinRT/testdraw/testdraw.vcxproj b/VisualC-WinRT/testdraw/testdraw.vcxproj index eea5a6ccd31eb..86d985dba8b18 100644 --- a/VisualC-WinRT/testdraw/testdraw.vcxproj +++ b/VisualC-WinRT/testdraw/testdraw.vcxproj @@ -264,6 +264,11 @@ NotUsing + + + $(TreatWarningsAsError) + + diff --git a/VisualC/SDL.sln b/VisualC/SDL.sln index 67484805ddbe6..f0492dfb18d94 100644 --- a/VisualC/SDL.sln +++ b/VisualC/SDL.sln @@ -12,6 +12,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "testatomic", "tests\testato EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "testautomation", "tests\testautomation\testautomation.vcxproj", "{9C7E8C03-3130-436D-A97E-E8F8ED1AC4EA}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "testdialog", "tests\testdialog\testdialog.vcxproj", "{97A3A89b-E023-48CD-905F-CDBDE8D951DE}" +EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "testdraw", "tests\testdraw\testdraw.vcxproj", "{8682FE1E-0CF6-4EDD-9BB5-1733D8C8B4DF}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "testfile", "tests\testfile\testfile.vcxproj", "{CAE4F1D0-314F-4B10-805B-0EFD670133A0}" @@ -100,6 +102,14 @@ Global {9C7E8C03-3130-436D-A97E-E8F8ED1AC4EA}.Release|Win32.Build.0 = Release|Win32 {9C7E8C03-3130-436D-A97E-E8F8ED1AC4EA}.Release|x64.ActiveCfg = Release|x64 {9C7E8C03-3130-436D-A97E-E8F8ED1AC4EA}.Release|x64.Build.0 = Release|x64 + {97A3A89b-E023-48CD-905F-CDBDE8D951DE}.Debug|Win32.ActiveCfg = Debug|Win32 + {97A3A89b-E023-48CD-905F-CDBDE8D951DE}.Debug|Win32.Build.0 = Debug|Win32 + {97A3A89b-E023-48CD-905F-CDBDE8D951DE}.Debug|x64.ActiveCfg = Debug|x64 + {97A3A89b-E023-48CD-905F-CDBDE8D951DE}.Debug|x64.Build.0 = Debug|x64 + {97A3A89b-E023-48CD-905F-CDBDE8D951DE}.Release|Win32.ActiveCfg = Release|Win32 + {97A3A89b-E023-48CD-905F-CDBDE8D951DE}.Release|Win32.Build.0 = Release|Win32 + {97A3A89b-E023-48CD-905F-CDBDE8D951DE}.Release|x64.ActiveCfg = Release|x64 + {97A3A89b-E023-48CD-905F-CDBDE8D951DE}.Release|x64.Build.0 = Release|x64 {8682FE1E-0CF6-4EDD-9BB5-1733D8C8B4DF}.Debug|Win32.ActiveCfg = Debug|Win32 {8682FE1E-0CF6-4EDD-9BB5-1733D8C8B4DF}.Debug|Win32.Build.0 = Debug|Win32 {8682FE1E-0CF6-4EDD-9BB5-1733D8C8B4DF}.Debug|x64.ActiveCfg = Debug|x64 @@ -269,6 +279,7 @@ Global {AAAD1CB5-7ADA-47AE-85A0-08A6EC48FAFB} = {D69D5741-611F-4E14-8541-1FEE94F50B5A} {66B32F7E-5716-48D0-B5B9-D832FD052DD5} = {D69D5741-611F-4E14-8541-1FEE94F50B5A} {9C7E8C03-3130-436D-A97E-E8F8ED1AC4EA} = {D69D5741-611F-4E14-8541-1FEE94F50B5A} + {97A3A89b-E023-48CD-905F-CDBDE8D951DE} = {D69D5741-611F-4E14-8541-1FEE94F50B5A} {8682FE1E-0CF6-4EDD-9BB5-1733D8C8B4DF} = {D69D5741-611F-4E14-8541-1FEE94F50B5A} {CAE4F1D0-314F-4B10-805B-0EFD670133A0} = {D69D5741-611F-4E14-8541-1FEE94F50B5A} {8B5CFB38-CCBA-40A8-AD7A-89C57B070884} = {D69D5741-611F-4E14-8541-1FEE94F50B5A} diff --git a/VisualC/SDL/SDL.vcxproj b/VisualC/SDL/SDL.vcxproj index 3a7f523d9f907..f8111cf344d9d 100644 --- a/VisualC/SDL/SDL.vcxproj +++ b/VisualC/SDL/SDL.vcxproj @@ -230,6 +230,11 @@ true + + + $(TreatWarningsAsError) + + @@ -332,6 +337,7 @@ + @@ -473,6 +479,7 @@ + @@ -521,6 +528,7 @@ NotUsing NotUsing + @@ -701,6 +709,7 @@ + diff --git a/VisualC/SDL/SDL.vcxproj.filters b/VisualC/SDL/SDL.vcxproj.filters index 24579a58ebcb0..57b5a527a7050 100644 --- a/VisualC/SDL/SDL.vcxproj.filters +++ b/VisualC/SDL/SDL.vcxproj.filters @@ -495,6 +495,9 @@ dynapi + + events + events @@ -681,6 +684,9 @@ video\windows + + video\windows + video\windows @@ -998,6 +1004,9 @@ dynapi + + events + events @@ -1334,6 +1343,9 @@ video\windows + + video\windows + video\windows diff --git a/VisualC/SDL_test/SDL_test.vcxproj b/VisualC/SDL_test/SDL_test.vcxproj index 90b52243ca199..541f7cb0cf6f6 100644 --- a/VisualC/SDL_test/SDL_test.vcxproj +++ b/VisualC/SDL_test/SDL_test.vcxproj @@ -152,6 +152,11 @@ true + + + $(TreatWarningsAsError) + + diff --git a/VisualC/tests/checkkeys/checkkeys.vcxproj b/VisualC/tests/checkkeys/checkkeys.vcxproj index 7d35d4bcefbc4..cc263df07d9bd 100644 --- a/VisualC/tests/checkkeys/checkkeys.vcxproj +++ b/VisualC/tests/checkkeys/checkkeys.vcxproj @@ -183,6 +183,11 @@ Windows + + + $(TreatWarningsAsError) + + {81ce8daf-ebb2-4761-8e45-b71abcca8c68} diff --git a/VisualC/tests/loopwave/loopwave.vcxproj b/VisualC/tests/loopwave/loopwave.vcxproj index 13d37fdfb2c3f..a5912a9a8c5a0 100644 --- a/VisualC/tests/loopwave/loopwave.vcxproj +++ b/VisualC/tests/loopwave/loopwave.vcxproj @@ -183,6 +183,11 @@ Windows + + + $(TreatWarningsAsError) + + {81ce8daf-ebb2-4761-8e45-b71abcca8c68} diff --git a/VisualC/tests/testatomic/testatomic.vcxproj b/VisualC/tests/testatomic/testatomic.vcxproj index c9ab811a8d545..5a1efef2dacf4 100644 --- a/VisualC/tests/testatomic/testatomic.vcxproj +++ b/VisualC/tests/testatomic/testatomic.vcxproj @@ -177,6 +177,11 @@ Windows + + + $(TreatWarningsAsError) + + {81ce8daf-ebb2-4761-8e45-b71abcca8c68} diff --git a/VisualC/tests/testautomation/testautomation.vcxproj b/VisualC/tests/testautomation/testautomation.vcxproj index 666c7155bfedf..8dd3dfc44151b 100644 --- a/VisualC/tests/testautomation/testautomation.vcxproj +++ b/VisualC/tests/testautomation/testautomation.vcxproj @@ -177,6 +177,11 @@ Windows + + + $(TreatWarningsAsError) + + {81ce8daf-ebb2-4761-8e45-b71abcca8c68} @@ -207,12 +212,6 @@ - - $(ProjectDir)\..\..\..\src;%(AdditionalIncludeDirectories) - $(ProjectDir)\..\..\..\src;%(AdditionalIncludeDirectories) - $(ProjectDir)\..\..\..\src;%(AdditionalIncludeDirectories) - $(ProjectDir)\..\..\..\src;%(AdditionalIncludeDirectories) - diff --git a/VisualC/tests/testcontroller/testcontroller.vcxproj b/VisualC/tests/testcontroller/testcontroller.vcxproj index 8c0854b145efc..94c8629a1a266 100644 --- a/VisualC/tests/testcontroller/testcontroller.vcxproj +++ b/VisualC/tests/testcontroller/testcontroller.vcxproj @@ -177,6 +177,11 @@ Windows + + + $(TreatWarningsAsError) + + {81ce8daf-ebb2-4761-8e45-b71abcca8c68} diff --git a/VisualC/tests/testdialog/testdialog.vcxproj b/VisualC/tests/testdialog/testdialog.vcxproj new file mode 100644 index 0000000000000..ed773c472c23b --- /dev/null +++ b/VisualC/tests/testdialog/testdialog.vcxproj @@ -0,0 +1,205 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {97A3A89B-E023-48CD-905F-CDBDE8D951DE} + testdialog + 10.0 + + + + Application + $(DefaultPlatformToolset) + + + Application + $(DefaultPlatformToolset) + + + Application + $(DefaultPlatformToolset) + + + Application + $(DefaultPlatformToolset) + + + + + + + + + + + + + + + + + + + <_ProjectFileVersion>10.0.40219.1 + $(SolutionDir)$(Platform)\$(Configuration)\ + $(Platform)\$(Configuration)\ + $(SolutionDir)$(Platform)\$(Configuration)\ + $(Platform)\$(Configuration)\ + $(SolutionDir)$(Platform)\$(Configuration)\ + $(Platform)\$(Configuration)\ + $(SolutionDir)$(Platform)\$(Configuration)\ + $(Platform)\$(Configuration)\ + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + + + + _DEBUG;%(PreprocessorDefinitions) + true + true + Win32 + .\Debug/testdialog.tlb + + + Disabled + $(SolutionDir)/../include;%(AdditionalIncludeDirectories) + %(AdditionalUsingDirectories) + WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) + MultiThreadedDLL + Level3 + OldStyle + + + _DEBUG;%(PreprocessorDefinitions) + 0x0409 + + + true + Windows + + + + + _DEBUG;%(PreprocessorDefinitions) + true + true + X64 + .\Debug/testdialog.tlb + + + Disabled + $(SolutionDir)/../include;%(AdditionalIncludeDirectories) + %(AdditionalUsingDirectories) + WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) + MultiThreadedDebugDLL + Level3 + OldStyle + + + _DEBUG;%(PreprocessorDefinitions) + 0x0409 + + + true + Windows + + + + + NDEBUG;%(PreprocessorDefinitions) + true + true + Win32 + .\Release/testdialog.tlb + + + $(SolutionDir)/../include;%(AdditionalIncludeDirectories) + %(AdditionalUsingDirectories) + WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) + MultiThreadedDLL + Level3 + + + NDEBUG;%(PreprocessorDefinitions) + 0x0409 + + + Windows + + + + + NDEBUG;%(PreprocessorDefinitions) + true + true + X64 + .\Release/testdialog.tlb + + + $(SolutionDir)/../include;%(AdditionalIncludeDirectories) + %(AdditionalUsingDirectories) + WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) + MultiThreadedDLL + Level3 + + + NDEBUG;%(PreprocessorDefinitions) + 0x0409 + + + Windows + + + + + $(TreatWarningsAsError) + + + + + {81ce8daf-ebb2-4761-8e45-b71abcca8c68} + false + false + true + + + {da956fd3-e143-46f2-9fe5-c77bebc56b1a} + false + false + true + + + + + + + + + \ No newline at end of file diff --git a/VisualC/tests/testdraw/testdraw.vcxproj b/VisualC/tests/testdraw/testdraw.vcxproj index e3e232a7d429d..5ca57bf082bc0 100644 --- a/VisualC/tests/testdraw/testdraw.vcxproj +++ b/VisualC/tests/testdraw/testdraw.vcxproj @@ -177,6 +177,11 @@ Windows + + + $(TreatWarningsAsError) + + {81ce8daf-ebb2-4761-8e45-b71abcca8c68} diff --git a/VisualC/tests/testfile/testfile.vcxproj b/VisualC/tests/testfile/testfile.vcxproj index 6d9c371b1a57e..a38880d5e3a0e 100644 --- a/VisualC/tests/testfile/testfile.vcxproj +++ b/VisualC/tests/testfile/testfile.vcxproj @@ -177,6 +177,11 @@ Windows + + + $(TreatWarningsAsError) + + {81ce8daf-ebb2-4761-8e45-b71abcca8c68} diff --git a/VisualC/tests/testgl/testgl.vcxproj b/VisualC/tests/testgl/testgl.vcxproj index 1e32a90494e6d..b8bc234ec1faf 100644 --- a/VisualC/tests/testgl/testgl.vcxproj +++ b/VisualC/tests/testgl/testgl.vcxproj @@ -181,6 +181,11 @@ Windows + + + $(TreatWarningsAsError) + + {81ce8daf-ebb2-4761-8e45-b71abcca8c68} diff --git a/VisualC/tests/testgles2/testgles2.vcxproj b/VisualC/tests/testgles2/testgles2.vcxproj index 743af4b8c90bc..13d0df290306a 100644 --- a/VisualC/tests/testgles2/testgles2.vcxproj +++ b/VisualC/tests/testgles2/testgles2.vcxproj @@ -177,6 +177,11 @@ Windows + + + $(TreatWarningsAsError) + + {81ce8daf-ebb2-4761-8e45-b71abcca8c68} diff --git a/VisualC/tests/testoverlay/testoverlay.vcxproj b/VisualC/tests/testoverlay/testoverlay.vcxproj index 5272c28d22cea..80c8b03040026 100644 --- a/VisualC/tests/testoverlay/testoverlay.vcxproj +++ b/VisualC/tests/testoverlay/testoverlay.vcxproj @@ -177,6 +177,11 @@ Windows + + + $(TreatWarningsAsError) + + {81ce8daf-ebb2-4761-8e45-b71abcca8c68} diff --git a/VisualC/tests/testpen/testpen.vcxproj b/VisualC/tests/testpen/testpen.vcxproj index 79f2526273fa8..fdef64f234051 100644 --- a/VisualC/tests/testpen/testpen.vcxproj +++ b/VisualC/tests/testpen/testpen.vcxproj @@ -177,6 +177,11 @@ Windows + + + $(TreatWarningsAsError) + + {81ce8daf-ebb2-4761-8e45-b71abcca8c68} diff --git a/VisualC/tests/testplatform/testplatform.vcxproj b/VisualC/tests/testplatform/testplatform.vcxproj index 5ef911d1db7f5..7752f733f78d3 100644 --- a/VisualC/tests/testplatform/testplatform.vcxproj +++ b/VisualC/tests/testplatform/testplatform.vcxproj @@ -107,10 +107,6 @@ true Windows - - true - .\Debug/testplatform.bsc - @@ -140,10 +136,6 @@ true Windows - - true - .\Debug/testplatform.bsc - @@ -170,10 +162,6 @@ Windows - - true - .\Release/testplatform.bsc - @@ -200,10 +188,11 @@ Windows - - true - .\Release/testplatform.bsc - + + + + $(TreatWarningsAsError) + diff --git a/VisualC/tests/testpower/testpower.vcxproj b/VisualC/tests/testpower/testpower.vcxproj index abf6b8c6b3e0c..98ca2b53a2c67 100644 --- a/VisualC/tests/testpower/testpower.vcxproj +++ b/VisualC/tests/testpower/testpower.vcxproj @@ -177,6 +177,11 @@ Windows + + + $(TreatWarningsAsError) + + {81ce8daf-ebb2-4761-8e45-b71abcca8c68} diff --git a/VisualC/tests/testrendertarget/testrendertarget.vcxproj b/VisualC/tests/testrendertarget/testrendertarget.vcxproj index 8582f01dd7f6f..eaf719bb291aa 100644 --- a/VisualC/tests/testrendertarget/testrendertarget.vcxproj +++ b/VisualC/tests/testrendertarget/testrendertarget.vcxproj @@ -177,6 +177,11 @@ Windows + + + $(TreatWarningsAsError) + + {81ce8daf-ebb2-4761-8e45-b71abcca8c68} diff --git a/VisualC/tests/testrumble/testrumble.vcxproj b/VisualC/tests/testrumble/testrumble.vcxproj index c50bd4523ea76..c0cb1090b64ff 100644 --- a/VisualC/tests/testrumble/testrumble.vcxproj +++ b/VisualC/tests/testrumble/testrumble.vcxproj @@ -177,6 +177,11 @@ Windows + + + $(TreatWarningsAsError) + + {81ce8daf-ebb2-4761-8e45-b71abcca8c68} diff --git a/VisualC/tests/testscale/testscale.vcxproj b/VisualC/tests/testscale/testscale.vcxproj index 957f8b5575c11..3843e57ae34aa 100644 --- a/VisualC/tests/testscale/testscale.vcxproj +++ b/VisualC/tests/testscale/testscale.vcxproj @@ -177,6 +177,11 @@ Windows + + + $(TreatWarningsAsError) + + {81ce8daf-ebb2-4761-8e45-b71abcca8c68} diff --git a/VisualC/tests/testsensor/testsensor.vcxproj b/VisualC/tests/testsensor/testsensor.vcxproj index bc8495e578588..3f034ad396dc6 100644 --- a/VisualC/tests/testsensor/testsensor.vcxproj +++ b/VisualC/tests/testsensor/testsensor.vcxproj @@ -177,6 +177,11 @@ Windows + + + $(TreatWarningsAsError) + + {81ce8daf-ebb2-4761-8e45-b71abcca8c68} diff --git a/VisualC/tests/testshape/testshape.vcxproj b/VisualC/tests/testshape/testshape.vcxproj index 0dc50470e5c48..33521f68443c5 100644 --- a/VisualC/tests/testshape/testshape.vcxproj +++ b/VisualC/tests/testshape/testshape.vcxproj @@ -177,6 +177,11 @@ Windows + + + $(TreatWarningsAsError) + + {81ce8daf-ebb2-4761-8e45-b71abcca8c68} diff --git a/VisualC/tests/testsprite/testsprite.vcxproj b/VisualC/tests/testsprite/testsprite.vcxproj index d9011921f933d..f53408ff57239 100644 --- a/VisualC/tests/testsprite/testsprite.vcxproj +++ b/VisualC/tests/testsprite/testsprite.vcxproj @@ -177,6 +177,11 @@ Windows + + + $(TreatWarningsAsError) + + {81ce8daf-ebb2-4761-8e45-b71abcca8c68} diff --git a/VisualC/tests/testsurround/testsurround.vcxproj b/VisualC/tests/testsurround/testsurround.vcxproj index 950f22e9f5c48..657dd1f3d7468 100644 --- a/VisualC/tests/testsurround/testsurround.vcxproj +++ b/VisualC/tests/testsurround/testsurround.vcxproj @@ -183,6 +183,11 @@ Console + + + $(TreatWarningsAsError) + + {81ce8daf-ebb2-4761-8e45-b71abcca8c68} diff --git a/VisualC/tests/testvulkan/testvulkan.vcxproj b/VisualC/tests/testvulkan/testvulkan.vcxproj index 158a4253f8c62..800aa50ff4747 100644 --- a/VisualC/tests/testvulkan/testvulkan.vcxproj +++ b/VisualC/tests/testvulkan/testvulkan.vcxproj @@ -177,6 +177,11 @@ Windows + + + $(TreatWarningsAsError) + + {da956fd3-e143-46f2-9fe5-c77bebc56b1a} diff --git a/VisualC/tests/testwm/testwm.vcxproj b/VisualC/tests/testwm/testwm.vcxproj index b88dd5356fe0c..b16df24343c5b 100644 --- a/VisualC/tests/testwm/testwm.vcxproj +++ b/VisualC/tests/testwm/testwm.vcxproj @@ -177,6 +177,11 @@ Windows + + + $(TreatWarningsAsError) + + {81ce8daf-ebb2-4761-8e45-b71abcca8c68} diff --git a/VisualC/tests/testyuv/testyuv.vcxproj b/VisualC/tests/testyuv/testyuv.vcxproj index 14e5e778006e6..e0eefbd742f39 100644 --- a/VisualC/tests/testyuv/testyuv.vcxproj +++ b/VisualC/tests/testyuv/testyuv.vcxproj @@ -177,6 +177,11 @@ Windows + + + $(TreatWarningsAsError) + + {81ce8daf-ebb2-4761-8e45-b71abcca8c68} diff --git a/Xcode/SDL/SDL.xcodeproj/project.pbxproj b/Xcode/SDL/SDL.xcodeproj/project.pbxproj index f13182b501b4a..3fa652201da23 100644 --- a/Xcode/SDL/SDL.xcodeproj/project.pbxproj +++ b/Xcode/SDL/SDL.xcodeproj/project.pbxproj @@ -229,8 +229,6 @@ A7D8B58723E2514300DCD162 /* SDL_joystick_c.h in Headers */ = {isa = PBXBuildFile; fileRef = A7D8A7D023E2513E00DCD162 /* SDL_joystick_c.h */; }; A7D8B5B723E2514300DCD162 /* controller_type.h in Headers */ = {isa = PBXBuildFile; fileRef = A7D8A7D923E2513E00DCD162 /* controller_type.h */; }; A7D8B5BD23E2514300DCD162 /* SDL_iostream.c in Sources */ = {isa = PBXBuildFile; fileRef = A7D8A7DB23E2513F00DCD162 /* SDL_iostream.c */; }; - A7D8B5C323E2514300DCD162 /* SDL_iostreambundlesupport.h in Headers */ = {isa = PBXBuildFile; fileRef = A7D8A7DD23E2513F00DCD162 /* SDL_iostreambundlesupport.h */; }; - A7D8B5C923E2514300DCD162 /* SDL_iostreambundlesupport.m in Sources */ = {isa = PBXBuildFile; fileRef = A7D8A7DE23E2513F00DCD162 /* SDL_iostreambundlesupport.m */; }; A7D8B5CF23E2514300DCD162 /* SDL_syspower.m in Sources */ = {isa = PBXBuildFile; fileRef = A7D8A7E123E2513F00DCD162 /* SDL_syspower.m */; }; A7D8B5D523E2514300DCD162 /* SDL_syspower.h in Headers */ = {isa = PBXBuildFile; fileRef = A7D8A7E223E2513F00DCD162 /* SDL_syspower.h */; }; A7D8B5E723E2514300DCD162 /* SDL_power.c in Sources */ = {isa = PBXBuildFile; fileRef = A7D8A7E723E2513F00DCD162 /* SDL_power.c */; }; @@ -443,6 +441,8 @@ F3B38CD7296E2E52005DA6D3 /* SDL_init.h in Headers */ = {isa = PBXBuildFile; fileRef = F3B38CCC296E2E52005DA6D3 /* SDL_init.h */; settings = {ATTRIBUTES = (Public, ); }; }; F3B38CDB296E2E52005DA6D3 /* SDL_oldnames.h in Headers */ = {isa = PBXBuildFile; fileRef = F3B38CCD296E2E52005DA6D3 /* SDL_oldnames.h */; settings = {ATTRIBUTES = (Public, ); }; }; F3B38CDF296E2E52005DA6D3 /* SDL_intrin.h in Headers */ = {isa = PBXBuildFile; fileRef = F3B38CCE296E2E52005DA6D3 /* SDL_intrin.h */; settings = {ATTRIBUTES = (Public, ); }; }; + F3C2CB222C5DDDB2004D7998 /* SDL_categories_c.h in Headers */ = {isa = PBXBuildFile; fileRef = F3C2CB202C5DDDB2004D7998 /* SDL_categories_c.h */; }; + F3C2CB232C5DDDB2004D7998 /* SDL_categories.c in Sources */ = {isa = PBXBuildFile; fileRef = F3C2CB212C5DDDB2004D7998 /* SDL_categories.c */; }; F3D60A8328C16A1900788A3A /* SDL_hidapi_wii.c in Sources */ = {isa = PBXBuildFile; fileRef = F3D60A8228C16A1800788A3A /* SDL_hidapi_wii.c */; }; F3DDCC562AFD42B600B0842B /* SDL_clipboard_c.h in Headers */ = {isa = PBXBuildFile; fileRef = F3DDCC4D2AFD42B500B0842B /* SDL_clipboard_c.h */; }; F3DDCC5B2AFD42B600B0842B /* SDL_video_c.h in Headers */ = {isa = PBXBuildFile; fileRef = F3DDCC522AFD42B600B0842B /* SDL_video_c.h */; }; @@ -524,6 +524,8 @@ F3FA5A242B59ACE000FEAD97 /* yuv_rgb_lsx.h in Headers */ = {isa = PBXBuildFile; fileRef = F3FA5A1B2B59ACE000FEAD97 /* yuv_rgb_lsx.h */; }; F3FA5A252B59ACE000FEAD97 /* yuv_rgb_common.h in Headers */ = {isa = PBXBuildFile; fileRef = F3FA5A1C2B59ACE000FEAD97 /* yuv_rgb_common.h */; }; FA73671D19A540EF004122E4 /* CoreVideo.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FA73671C19A540EF004122E4 /* CoreVideo.framework */; platformFilters = (ios, maccatalyst, macos, tvos, watchos, ); }; + 0000E5D7110DFF81FF660000 /* SDL_cocoapen.h in Headers */ = {isa = PBXBuildFile; fileRef = 00002F2F5496FA184A0F0000 /* SDL_cocoapen.h */; }; + 0000D5B526B85DE7AB1C0000 /* SDL_cocoapen.m in Sources */ = {isa = PBXBuildFile; fileRef = 0000CCA310B73A7B59910000 /* SDL_cocoapen.m */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -789,8 +791,6 @@ A7D8A7D023E2513E00DCD162 /* SDL_joystick_c.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_joystick_c.h; sourceTree = ""; }; A7D8A7D923E2513E00DCD162 /* controller_type.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = controller_type.h; sourceTree = ""; }; A7D8A7DB23E2513F00DCD162 /* SDL_iostream.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_iostream.c; sourceTree = ""; }; - A7D8A7DD23E2513F00DCD162 /* SDL_iostreambundlesupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_iostreambundlesupport.h; sourceTree = ""; }; - A7D8A7DE23E2513F00DCD162 /* SDL_iostreambundlesupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDL_iostreambundlesupport.m; sourceTree = ""; }; A7D8A7E123E2513F00DCD162 /* SDL_syspower.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDL_syspower.m; sourceTree = ""; }; A7D8A7E223E2513F00DCD162 /* SDL_syspower.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_syspower.h; sourceTree = ""; }; A7D8A7E723E2513F00DCD162 /* SDL_power.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_power.c; sourceTree = ""; }; @@ -992,6 +992,8 @@ F3B38CCC296E2E52005DA6D3 /* SDL_init.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SDL_init.h; path = SDL3/SDL_init.h; sourceTree = ""; }; F3B38CCD296E2E52005DA6D3 /* SDL_oldnames.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SDL_oldnames.h; path = SDL3/SDL_oldnames.h; sourceTree = ""; }; F3B38CCE296E2E52005DA6D3 /* SDL_intrin.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SDL_intrin.h; path = SDL3/SDL_intrin.h; sourceTree = ""; }; + F3C2CB202C5DDDB2004D7998 /* SDL_categories_c.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_categories_c.h; sourceTree = ""; }; + F3C2CB212C5DDDB2004D7998 /* SDL_categories.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_categories.c; sourceTree = ""; }; F3D60A8228C16A1800788A3A /* SDL_hidapi_wii.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_hidapi_wii.c; sourceTree = ""; }; F3DDCC4D2AFD42B500B0842B /* SDL_clipboard_c.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_clipboard_c.h; sourceTree = ""; }; F3DDCC522AFD42B600B0842B /* SDL_video_c.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_video_c.h; sourceTree = ""; }; @@ -1076,6 +1078,8 @@ F59C710600D5CB5801000001 /* SDL.info */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text; path = SDL.info; sourceTree = ""; }; F5A2EF3900C6A39A01000001 /* BUGS.txt */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text; name = BUGS.txt; path = ../../BUGS.txt; sourceTree = SOURCE_ROOT; }; FA73671C19A540EF004122E4 /* CoreVideo.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreVideo.framework; path = System/Library/Frameworks/CoreVideo.framework; sourceTree = SDKROOT; }; + 00002F2F5496FA184A0F0000 /* SDL_cocoapen.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SDL_cocoapen.h; path = SDL_cocoapen.h; sourceTree = ""; }; + 0000CCA310B73A7B59910000 /* SDL_cocoapen.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SDL_cocoapen.m; path = SDL_cocoapen.m; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -1676,6 +1680,8 @@ A7D8A68323E2513E00DCD162 /* SDL_cocoavulkan.m */, A7D8A69223E2513E00DCD162 /* SDL_cocoawindow.h */, A7D8A68423E2513E00DCD162 /* SDL_cocoawindow.m */, + 00002F2F5496FA184A0F0000 /* SDL_cocoapen.h */, + 0000CCA310B73A7B59910000 /* SDL_cocoapen.m */, ); path = cocoa; sourceTree = ""; @@ -1884,21 +1890,11 @@ A7D8A7DA23E2513E00DCD162 /* file */ = { isa = PBXGroup; children = ( - A7D8A7DC23E2513F00DCD162 /* cocoa */, A7D8A7DB23E2513F00DCD162 /* SDL_iostream.c */, ); path = file; sourceTree = ""; }; - A7D8A7DC23E2513F00DCD162 /* cocoa */ = { - isa = PBXGroup; - children = ( - A7D8A7DD23E2513F00DCD162 /* SDL_iostreambundlesupport.h */, - A7D8A7DE23E2513F00DCD162 /* SDL_iostreambundlesupport.m */, - ); - path = cocoa; - sourceTree = ""; - }; A7D8A7DF23E2513F00DCD162 /* power */ = { isa = PBXGroup; children = ( @@ -2193,6 +2189,8 @@ A7D8A93623E2514000DCD162 /* scancodes_linux.h */, A7D8A92C23E2514000DCD162 /* scancodes_windows.h */, A7D8A94123E2514000DCD162 /* scancodes_xfree86.h */, + F3C2CB202C5DDDB2004D7998 /* SDL_categories_c.h */, + F3C2CB212C5DDDB2004D7998 /* SDL_categories.c */, A7D8A93923E2514000DCD162 /* SDL_clipboardevents_c.h */, A7D8A93A23E2514000DCD162 /* SDL_clipboardevents.c */, A7D8A93123E2514000DCD162 /* SDL_displayevents_c.h */, @@ -2368,6 +2366,7 @@ A7D8B8A223E2514400DCD162 /* SDL_diskaudio.h in Headers */, A7D8BB3F23E2514500DCD162 /* SDL_displayevents_c.h in Headers */, A7D8BA1923E2514400DCD162 /* SDL_draw.h in Headers */, + F3C2CB222C5DDDB2004D7998 /* SDL_categories_c.h in Headers */, E479118E2BA9555500CE3B7F /* SDL_sysstorage.h in Headers */, A7D8BA0723E2514400DCD162 /* SDL_drawline.h in Headers */, A7D8B9EF23E2514400DCD162 /* SDL_drawpoint.h in Headers */, @@ -2462,7 +2461,6 @@ F3F7D9152933074E00816151 /* SDL_revision.h in Headers */, A7D8BA3123E2514400DCD162 /* SDL_rotate.h in Headers */, F3F7D9652933074E00816151 /* SDL_iostream.h in Headers */, - A7D8B5C323E2514300DCD162 /* SDL_iostreambundlesupport.h in Headers */, F3F7D9492933074E00816151 /* SDL_scancode.h in Headers */, F3F7D94D2933074E00816151 /* SDL_sensor.h in Headers */, A7D8A98D23E2514000DCD162 /* SDL_sensor_c.h in Headers */, @@ -2854,6 +2852,7 @@ A7D8AE9A23E2514100DCD162 /* SDL_cocoaopengles.m in Sources */, A7D8B96823E2514400DCD162 /* SDL_qsort.c in Sources */, F3FA5A222B59ACE000FEAD97 /* yuv_rgb_sse.c in Sources */, + F3C2CB232C5DDDB2004D7998 /* SDL_categories.c in Sources */, A7D8B55123E2514300DCD162 /* SDL_hidapi_switch.c in Sources */, A7D8B96223E2514400DCD162 /* SDL_strtokr.c in Sources */, A7D8BB7523E2514500DCD162 /* SDL_clipboardevents.c in Sources */, @@ -2877,7 +2876,6 @@ F3F528CD2C29E1C300E6CC26 /* s_isnan.c in Sources */, F3F07D5A269640160074468B /* SDL_hidapi_luna.c in Sources */, A7D8BBD523E2574800DCD162 /* SDL_uikitclipboard.m in Sources */, - A7D8B5C923E2514300DCD162 /* SDL_iostreambundlesupport.m in Sources */, F386F6F92884663E001840AA /* SDL_utils.c in Sources */, E4F7981E2AD8D86A00669F54 /* SDL_render_unsupported.c in Sources */, A7D8AC0F23E2514100DCD162 /* SDL_video.c in Sources */, @@ -2916,6 +2914,7 @@ 0000494CC93F3E624D3C0000 /* SDL_systime.c in Sources */, 000095FA1BDE436CF3AF0000 /* SDL_time.c in Sources */, 0000140640E77F73F1DF0000 /* SDL_dialog_utils.c in Sources */, + 0000D5B526B85DE7AB1C0000 /* SDL_cocoapen.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Xcode/SDLTest/SDLTest.xcodeproj/project.pbxproj b/Xcode/SDLTest/SDLTest.xcodeproj/project.pbxproj index e40038e4c9931..b1b49715a5521 100644 --- a/Xcode/SDLTest/SDLTest.xcodeproj/project.pbxproj +++ b/Xcode/SDLTest/SDLTest.xcodeproj/project.pbxproj @@ -184,6 +184,8 @@ F3C17CEC28E417EB00E1A26D /* testutils.c in Sources */ = {isa = PBXBuildFile; fileRef = F3C17C7328E40ADE00E1A26D /* testutils.c */; }; F3C17D3928E424B800E1A26D /* sample.wav in Resources */ = {isa = PBXBuildFile; fileRef = 00794E6209D20839003FC8A1 /* sample.wav */; }; F3C17D3B28E4252900E1A26D /* icon.bmp in Resources */ = {isa = PBXBuildFile; fileRef = 00794E5D09D20839003FC8A1 /* icon.bmp */; }; + F3C2CAC62C5C8BD6004D7998 /* unifont-15.1.05.hex in Resources */ = {isa = PBXBuildFile; fileRef = F3C2CAC52C5C8BD6004D7998 /* unifont-15.1.05.hex */; }; + F3C2CB072C5D3FB2004D7998 /* icon.bmp in Resources */ = {isa = PBXBuildFile; fileRef = 00794E5D09D20839003FC8A1 /* icon.bmp */; }; F3CB56892A7895F800766177 /* SDL3.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 003FA643093FFD41000C53B3 /* SDL3.framework */; }; F3CB568A2A7895F800766177 /* SDL3.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 003FA643093FFD41000C53B3 /* SDL3.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; F3CB568C2A7896BF00766177 /* SDL3.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 003FA643093FFD41000C53B3 /* SDL3.framework */; }; @@ -1272,7 +1274,6 @@ 4537749212091504002F0F45 /* testshape.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = testshape.app; sourceTree = BUILT_PRODUCTS_DIR; }; 453774A4120915E3002F0F45 /* testshape.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = testshape.c; sourceTree = ""; }; 66E88E8A203B778F0004D44E /* testyuv_cvt.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = testyuv_cvt.c; sourceTree = ""; }; - A1A859442BC72FC20045DD6C /* testautomation_pen.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = testautomation_pen.c; sourceTree = ""; }; A1A859482BC72FC20045DD6C /* testautomation_properties.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = testautomation_properties.c; sourceTree = ""; }; A1A859492BC72FC20045DD6C /* testautomation_subsystems.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = testautomation_subsystems.c; sourceTree = ""; }; A1A8594A2BC72FC20045DD6C /* testautomation_log.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = testautomation_log.c; sourceTree = ""; }; @@ -1359,6 +1360,7 @@ F3C17C7328E40ADE00E1A26D /* testutils.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = testutils.c; sourceTree = ""; }; F3C17CD628E416AC00E1A26D /* testgeometry.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = testgeometry.c; sourceTree = ""; }; F3C17CDC28E416CF00E1A26D /* testgeometry.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = testgeometry.app; sourceTree = BUILT_PRODUCTS_DIR; }; + F3C2CAC52C5C8BD6004D7998 /* unifont-15.1.05.hex */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "unifont-15.1.05.hex"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -1757,6 +1759,7 @@ 00794E5F09D20839003FC8A1 /* picture.xbm */, 00794E6109D20839003FC8A1 /* sample.bmp */, 00794E6209D20839003FC8A1 /* sample.wav */, + F3C2CAC52C5C8BD6004D7998 /* unifont-15.1.05.hex */, 00794E6309D20839003FC8A1 /* utf8.txt */, ); name = Resources; @@ -1800,7 +1803,6 @@ F35E56B62983130A00A43A5F /* testautomation_main.c */, F35E56BA2983130B00A43A5F /* testautomation_math.c */, F35E56CD2983130F00A43A5F /* testautomation_mouse.c */, - A1A859442BC72FC20045DD6C /* testautomation_pen.c */, F35E56C02983130C00A43A5F /* testautomation_pixels.c */, F35E56C32983130D00A43A5F /* testautomation_platform.c */, A1A859482BC72FC20045DD6C /* testautomation_properties.c */, @@ -2051,6 +2053,7 @@ 0017977B107432AE00F5D044 /* Sources */, 0017977C107432AE00F5D044 /* Frameworks */, F3CB56C22A78979600766177 /* Embed Frameworks */, + F3C2CAC42C5C8BAF004D7998 /* Resources */, ); buildRules = ( ); @@ -3018,6 +3021,15 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + F3C2CAC42C5C8BAF004D7998 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F3C2CB072C5D3FB2004D7998 /* icon.bmp in Resources */, + F3C2CAC62C5C8BD6004D7998 /* unifont-15.1.05.hex in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ diff --git a/android-project/app/build.gradle b/android-project/app/build.gradle index d34ba31457712..514e594a4c1f2 100644 --- a/android-project/app/build.gradle +++ b/android-project/app/build.gradle @@ -1,17 +1,11 @@ -def buildWithCMake = project.hasProperty('BUILD_WITH_CMAKE'); -def buildAsLibrary = project.hasProperty('BUILD_AS_LIBRARY'); -def buildAsApplication = !buildAsLibrary -if (buildAsApplication) { - apply plugin: 'com.android.application' -} -else { - apply plugin: 'com.android.library' +plugins { + id 'com.android.application' } +def buildWithCMake = project.hasProperty('BUILD_WITH_CMAKE'); + android { - if (buildAsApplication) { - namespace "org.libsdl.app" - } + namespace "org.libsdl.app" compileSdkVersion 34 defaultConfig { minSdkVersion 19 @@ -61,18 +55,6 @@ android { lint { abortOnError false } - - if (buildAsLibrary) { - libraryVariants.all { variant -> - variant.outputs.each { output -> - def outputFile = output.outputFile - if (outputFile != null && outputFile.name.endsWith(".aar")) { - def fileName = "org.libsdl.app.aar"; - output.outputFile = new File(outputFile.parent, fileName); - } - } - } - } } dependencies { diff --git a/android-project/app/proguard-rules.pro b/android-project/app/proguard-rules.pro index 9a28afe743428..9985625dc3958 100644 --- a/android-project/app/proguard-rules.pro +++ b/android-project/app/proguard-rules.pro @@ -5,7 +5,7 @@ # directive in build.gradle. # # For more details, see -# http://developer.android.com/guide/developing/tools/proguard.html +# https://developer.android.com/build/shrink-code # Add any project specific keep options here: @@ -17,74 +17,58 @@ #} -keep,includedescriptorclasses,allowoptimization class org.libsdl.app.SDLActivity { - void manualBackButton(); - boolean setActivityTitle(java.lang.String); - void setWindowStyle(boolean); - void setOrientation(int, int, boolean, java.lang.String); - void minimizeWindow(); - boolean shouldMinimizeOnFocusLoss(); - boolean isScreenKeyboardShown(); - boolean supportsRelativeMouse(); - boolean setRelativeMouseEnabled(boolean); - boolean sendMessage(int, int); - android.content.Context getContext(); - boolean isAndroidTV(); - boolean isTablet(); - boolean isChromebook(); - boolean isDeXMode(); - boolean getManifestEnvironmentVariables(); - boolean showTextInput(int, int, int, int); - android.view.Surface getNativeSurface(); - void initTouch(); - int messageboxShowMessageBox(int, java.lang.String, java.lang.String, int[], int[], java.lang.String[], int[]); - boolean clipboardHasText(); + java.lang.String nativeGetHint(java.lang.String); # Java-side doesn't use this, so it gets minified, but C-side still tries to register it java.lang.String clipboardGetText(); + boolean clipboardHasText(); void clipboardSetText(java.lang.String); int createCustomCursor(int[], int, int, int, int); void destroyCustomCursor(int); - boolean setCustomCursor(int); - boolean setSystemCursor(int); - void requestPermission(java.lang.String, int); + android.content.Context getContext(); + boolean getManifestEnvironmentVariables(); + android.view.Surface getNativeSurface(); + void initTouch(); + boolean isAndroidTV(); + boolean isChromebook(); + boolean isDeXMode(); + boolean isScreenKeyboardShown(); + boolean isTablet(); + void manualBackButton(); + void minimizeWindow(); int openURL(java.lang.String); + void requestPermission(java.lang.String, int); int showToast(java.lang.String, int, int, int, int); - java.lang.String nativeGetHint(java.lang.String); + boolean sendMessage(int, int); + boolean setActivityTitle(java.lang.String); + boolean setCustomCursor(int); + void setOrientation(int, int, boolean, java.lang.String); + boolean setRelativeMouseEnabled(boolean); + boolean setSystemCursor(int); + void setWindowStyle(boolean); + boolean shouldMinimizeOnFocusLoss(); + 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); - void onNativeFileDialog(int, java.lang.String[], int); - void onNativeInsetsChanged(int, int, int, int); } -keep,includedescriptorclasses,allowoptimization class org.libsdl.app.HIDDeviceManager { + void closeDevice(int); boolean initialize(boolean, boolean); boolean openDevice(int); - int writeReport(int, byte[], boolean); boolean readReport(int, byte[], boolean); - void closeDevice(int); + int writeReport(int, byte[], boolean); } -keep,includedescriptorclasses,allowoptimization class org.libsdl.app.SDLAudioManager { void registerAudioDeviceCallback(); void unregisterAudioDeviceCallback(); - int[] audioOpen(int, int, int, int, int); - void audioWriteFloatBuffer(float[]); - void audioWriteShortBuffer(short[]); - void audioWriteByteBuffer(byte[]); - int[] recordingOpen(int, int, int, int, int); - int recordingReadFloatBuffer(float[], boolean); - int recordingReadShortBuffer(short[], boolean); - int recordingReadByteBuffer(byte[], boolean); - void audioClose(); - void recordingClose(); void audioSetThreadPriority(boolean, int); - int nativeSetupJNI(); - void removeAudioDevice(boolean, int); - void addAudioDevice(boolean, java.lang.String, int); } -keep,includedescriptorclasses,allowoptimization class org.libsdl.app.SDLControllerManager { void pollInputDevices(); void pollHapticDevices(); void hapticRun(int, float, int); + void hapticRumble(int, float, float, int); void hapticStop(int); - void hapticRumble(int, float , float, int); } diff --git a/android-project/app/src/main/java/org/libsdl/app/SDLActivity.java b/android-project/app/src/main/java/org/libsdl/app/SDLActivity.java index df82f457dffd8..b7d4bde0eee8c 100644 --- a/android-project/app/src/main/java/org/libsdl/app/SDLActivity.java +++ b/android-project/app/src/main/java/org/libsdl/app/SDLActivity.java @@ -247,12 +247,19 @@ protected static SDLGenericMotionListener_API12 getMotionListener() { } /** - * This method creates a Runnable which invokes SDL_main. The default implementation - * uses the getMainSharedObject() and getMainFunction() methods to invoke native - * code from the specified shared library. + * The application entry point, called on a dedicated thread (SDLThread). + * The default implementation uses the getMainSharedObject() and getMainFunction() methods + * to invoke native code from the specified shared library. + * It can be overridden by derived classes. */ - protected Runnable createSDLMainRunnable() { - return new SDLMain(); + protected void main() { + String library = SDLActivity.mSingleton.getMainSharedObject(); + String function = SDLActivity.mSingleton.getMainFunction(); + String[] arguments = SDLActivity.mSingleton.getArguments(); + + Log.v("SDL", "Running main function " + function + " from library " + library); + SDLActivity.nativeRunMain(library, function, arguments); + Log.v("SDL", "Finished main function"); } /** @@ -838,7 +845,7 @@ public static void handleNativeState() { // Start up the C app thread and enable sensor input for the first time // FIXME: Why aren't we enabling sensor input at start? - mSDLThread = new Thread(SDLActivity.mSingleton.createSDLMainRunnable(), "SDLThread"); + mSDLThread = new Thread(new SDLMain(), "SDLThread"); mSurface.enableSensor(Sensor.TYPE_ACCELEROMETER, true); mSDLThread.start(); @@ -1032,6 +1039,8 @@ protected boolean sendCommand(int command, Object data) { // C functions we call public static native String nativeGetVersion(); public static native int nativeSetupJNI(); + public static native void nativeInitMainThread(); + public static native void nativeCleanupMainThread(); public static native int nativeRunMain(String library, String function, Object arguments); public static native void nativeLowMemory(); public static native void nativeSendQuit(); @@ -1107,7 +1116,7 @@ public void setOrientationBis(int w, int h, boolean resizable, String hint) /* If set, hint "explicitly controls which UI orientations are allowed". */ if (hint.contains("LandscapeRight") && hint.contains("LandscapeLeft")) { - orientation_landscape = ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE; + orientation_landscape = ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE; } else if (hint.contains("LandscapeLeft")) { orientation_landscape = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; } else if (hint.contains("LandscapeRight")) { @@ -1118,7 +1127,7 @@ public void setOrientationBis(int w, int h, boolean resizable, String hint) boolean contains_Portrait = hint.contains("Portrait ") || hint.endsWith("Portrait"); if (contains_Portrait && hint.contains("PortraitUpsideDown")) { - orientation_portrait = ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT; + orientation_portrait = ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT; } else if (contains_Portrait) { orientation_portrait = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; } else if (hint.contains("PortraitUpsideDown")) { @@ -1375,9 +1384,11 @@ static class ShowTextInputTask implements Runnable { */ static final int HEIGHT_PADDING = 15; + public int input_type; public int x, y, w, h; - public ShowTextInputTask(int x, int y, int w, int h) { + public ShowTextInputTask(int input_type, int x, int y, int w, int h) { + this.input_type = input_type; this.x = x; this.y = y; this.w = w; @@ -1405,6 +1416,7 @@ public void run() { } else { mTextEdit.setLayoutParams(params); } + mTextEdit.setInputType(input_type); mTextEdit.setVisibility(View.VISIBLE); mTextEdit.requestFocus(); @@ -1419,9 +1431,9 @@ public void run() { /** * This method is called by SDL using JNI. */ - public static boolean showTextInput(int x, int y, int w, int h) { + public static boolean showTextInput(int input_type, int x, int y, int w, int h) { // Transfer the task to the main thread as a Runnable - return mSingleton.commandHandler.post(new ShowTextInputTask(x, y, w, h)); + return mSingleton.commandHandler.post(new ShowTextInputTask(input_type, x, y, w, h)); } public static boolean isTextInputEvent(KeyEvent event) { @@ -2099,10 +2111,7 @@ static class SDLFileDialogState { class SDLMain implements Runnable { @Override public void run() { - // Runs SDL_main() - String library = SDLActivity.mSingleton.getMainSharedObject(); - String function = SDLActivity.mSingleton.getMainFunction(); - String[] arguments = SDLActivity.mSingleton.getArguments(); + // Runs SDLActivity.main() try { android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_DISPLAY); @@ -2110,11 +2119,9 @@ public void run() { Log.v("SDL", "modify thread properties failed " + e.toString()); } - Log.v("SDL", "Running main function " + function + " from library " + library); - - SDLActivity.nativeRunMain(library, function, arguments); - - Log.v("SDL", "Finished main function"); + SDLActivity.nativeInitMainThread(); + SDLActivity.mSingleton.main(); + SDLActivity.nativeCleanupMainThread(); if (SDLActivity.mSingleton != null && !SDLActivity.mSingleton.isFinishing()) { // Let's finish the Activity diff --git a/android-project/app/src/main/java/org/libsdl/app/SDLAudioManager.java b/android-project/app/src/main/java/org/libsdl/app/SDLAudioManager.java index ed9c959d45a52..6ad2f543bbb1d 100644 --- a/android-project/app/src/main/java/org/libsdl/app/SDLAudioManager.java +++ b/android-project/app/src/main/java/org/libsdl/app/SDLAudioManager.java @@ -3,11 +3,7 @@ import android.content.Context; import android.media.AudioDeviceCallback; import android.media.AudioDeviceInfo; -import android.media.AudioFormat; import android.media.AudioManager; -import android.media.AudioRecord; -import android.media.AudioTrack; -import android.media.MediaRecorder; import android.os.Build; import android.util.Log; @@ -17,15 +13,11 @@ public class SDLAudioManager { protected static final String TAG = "SDLAudio"; - protected static AudioTrack mAudioTrack; - protected static AudioRecord mAudioRecord; protected static Context mContext; private static AudioDeviceCallback mAudioDeviceCallback; public static void initialize() { - mAudioTrack = null; - mAudioRecord = null; mAudioDeviceCallback = null; if(Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */) @@ -58,230 +50,6 @@ public static void release(Context context) { // Audio - protected static String getAudioFormatString(int audioFormat) { - switch (audioFormat) { - case AudioFormat.ENCODING_PCM_8BIT: - return "8-bit"; - case AudioFormat.ENCODING_PCM_16BIT: - return "16-bit"; - case AudioFormat.ENCODING_PCM_FLOAT: - return "float"; - default: - return Integer.toString(audioFormat); - } - } - - protected static int[] open(boolean recording, int sampleRate, int audioFormat, int desiredChannels, int desiredFrames, int deviceId) { - int channelConfig; - int sampleSize; - int frameSize; - - Log.v(TAG, "Opening " + (recording ? "recording" : "playback") + ", requested " + desiredFrames + " frames of " + desiredChannels + " channel " + getAudioFormatString(audioFormat) + " audio at " + sampleRate + " Hz"); - - /* On older devices let's use known good settings */ - if (Build.VERSION.SDK_INT < 21 /* Android 5.0 (LOLLIPOP) */) { - if (desiredChannels > 2) { - desiredChannels = 2; - } - } - - /* AudioTrack has sample rate limitation of 48000 (fixed in 5.0.2) */ - if (Build.VERSION.SDK_INT < 22 /* Android 5.1 (LOLLIPOP_MR1) */) { - if (sampleRate < 8000) { - sampleRate = 8000; - } else if (sampleRate > 48000) { - sampleRate = 48000; - } - } - - if (audioFormat == AudioFormat.ENCODING_PCM_FLOAT) { - int minSDKVersion = (recording ? 23 /* Android 6.0 (M) */ : 21 /* Android 5.0 (LOLLIPOP) */); - if (Build.VERSION.SDK_INT < minSDKVersion) { - audioFormat = AudioFormat.ENCODING_PCM_16BIT; - } - } - switch (audioFormat) - { - case AudioFormat.ENCODING_PCM_8BIT: - sampleSize = 1; - break; - case AudioFormat.ENCODING_PCM_16BIT: - sampleSize = 2; - break; - case AudioFormat.ENCODING_PCM_FLOAT: - sampleSize = 4; - break; - default: - Log.v(TAG, "Requested format " + audioFormat + ", getting ENCODING_PCM_16BIT"); - audioFormat = AudioFormat.ENCODING_PCM_16BIT; - sampleSize = 2; - break; - } - - if (recording) { - switch (desiredChannels) { - case 1: - channelConfig = AudioFormat.CHANNEL_IN_MONO; - break; - case 2: - channelConfig = AudioFormat.CHANNEL_IN_STEREO; - break; - default: - Log.v(TAG, "Requested " + desiredChannels + " channels, getting stereo"); - desiredChannels = 2; - channelConfig = AudioFormat.CHANNEL_IN_STEREO; - break; - } - } else { - switch (desiredChannels) { - case 1: - channelConfig = AudioFormat.CHANNEL_OUT_MONO; - break; - case 2: - channelConfig = AudioFormat.CHANNEL_OUT_STEREO; - break; - case 3: - channelConfig = AudioFormat.CHANNEL_OUT_STEREO | AudioFormat.CHANNEL_OUT_FRONT_CENTER; - break; - case 4: - channelConfig = AudioFormat.CHANNEL_OUT_QUAD; - break; - case 5: - channelConfig = AudioFormat.CHANNEL_OUT_QUAD | AudioFormat.CHANNEL_OUT_FRONT_CENTER; - break; - case 6: - channelConfig = AudioFormat.CHANNEL_OUT_5POINT1; - break; - case 7: - channelConfig = AudioFormat.CHANNEL_OUT_5POINT1 | AudioFormat.CHANNEL_OUT_BACK_CENTER; - break; - case 8: - if (Build.VERSION.SDK_INT >= 23 /* Android 6.0 (M) */) { - channelConfig = AudioFormat.CHANNEL_OUT_7POINT1_SURROUND; - } else { - Log.v(TAG, "Requested " + desiredChannels + " channels, getting 5.1 surround"); - desiredChannels = 6; - channelConfig = AudioFormat.CHANNEL_OUT_5POINT1; - } - break; - default: - Log.v(TAG, "Requested " + desiredChannels + " channels, getting stereo"); - desiredChannels = 2; - channelConfig = AudioFormat.CHANNEL_OUT_STEREO; - break; - } - -/* - Log.v(TAG, "Speaker configuration (and order of channels):"); - - if ((channelConfig & 0x00000004) != 0) { - Log.v(TAG, " CHANNEL_OUT_FRONT_LEFT"); - } - if ((channelConfig & 0x00000008) != 0) { - Log.v(TAG, " CHANNEL_OUT_FRONT_RIGHT"); - } - if ((channelConfig & 0x00000010) != 0) { - Log.v(TAG, " CHANNEL_OUT_FRONT_CENTER"); - } - if ((channelConfig & 0x00000020) != 0) { - Log.v(TAG, " CHANNEL_OUT_LOW_FREQUENCY"); - } - if ((channelConfig & 0x00000040) != 0) { - Log.v(TAG, " CHANNEL_OUT_BACK_LEFT"); - } - if ((channelConfig & 0x00000080) != 0) { - Log.v(TAG, " CHANNEL_OUT_BACK_RIGHT"); - } - if ((channelConfig & 0x00000100) != 0) { - Log.v(TAG, " CHANNEL_OUT_FRONT_LEFT_OF_CENTER"); - } - if ((channelConfig & 0x00000200) != 0) { - Log.v(TAG, " CHANNEL_OUT_FRONT_RIGHT_OF_CENTER"); - } - if ((channelConfig & 0x00000400) != 0) { - Log.v(TAG, " CHANNEL_OUT_BACK_CENTER"); - } - if ((channelConfig & 0x00000800) != 0) { - Log.v(TAG, " CHANNEL_OUT_SIDE_LEFT"); - } - if ((channelConfig & 0x00001000) != 0) { - Log.v(TAG, " CHANNEL_OUT_SIDE_RIGHT"); - } -*/ - } - frameSize = (sampleSize * desiredChannels); - - // Let the user pick a larger buffer if they really want -- but ye - // gods they probably shouldn't, the minimums are horrifyingly high - // latency already - int minBufferSize; - if (recording) { - minBufferSize = AudioRecord.getMinBufferSize(sampleRate, channelConfig, audioFormat); - } else { - minBufferSize = AudioTrack.getMinBufferSize(sampleRate, channelConfig, audioFormat); - } - desiredFrames = Math.max(desiredFrames, (minBufferSize + frameSize - 1) / frameSize); - - int[] results = new int[4]; - - if (recording) { - if (mAudioRecord == null) { - mAudioRecord = new AudioRecord(MediaRecorder.AudioSource.DEFAULT, sampleRate, - channelConfig, audioFormat, desiredFrames * frameSize); - - // see notes about AudioTrack state in audioOpen(), above. Probably also applies here. - if (mAudioRecord.getState() != AudioRecord.STATE_INITIALIZED) { - Log.e(TAG, "Failed during initialization of AudioRecord"); - mAudioRecord.release(); - mAudioRecord = null; - return null; - } - - if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */ && deviceId != 0) { - mAudioRecord.setPreferredDevice(getPlaybackAudioDeviceInfo(deviceId)); - } - - mAudioRecord.startRecording(); - } - - results[0] = mAudioRecord.getSampleRate(); - results[1] = mAudioRecord.getAudioFormat(); - results[2] = mAudioRecord.getChannelCount(); - - } else { - if (mAudioTrack == null) { - mAudioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, sampleRate, channelConfig, audioFormat, desiredFrames * frameSize, AudioTrack.MODE_STREAM); - - // Instantiating AudioTrack can "succeed" without an exception and the track may still be invalid - // Ref: https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/media/java/android/media/AudioTrack.java - // Ref: http://developer.android.com/reference/android/media/AudioTrack.html#getState() - if (mAudioTrack.getState() != AudioTrack.STATE_INITIALIZED) { - /* Try again, with safer values */ - - Log.e(TAG, "Failed during initialization of Audio Track"); - mAudioTrack.release(); - mAudioTrack = null; - return null; - } - - if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */ && deviceId != 0) { - mAudioTrack.setPreferredDevice(getInputAudioDeviceInfo(deviceId)); - } - - mAudioTrack.play(); - } - - results[0] = mAudioTrack.getSampleRate(); - results[1] = mAudioTrack.getAudioFormat(); - results[2] = mAudioTrack.getChannelCount(); - } - results[3] = desiredFrames; - - Log.v(TAG, "Opening " + (recording ? "recording" : "playback") + ", got " + results[3] + " frames of " + results[2] + " channel " + getAudioFormatString(results[1]) + " audio at " + results[0] + " Hz"); - - return results; - } - private static AudioDeviceInfo getInputAudioDeviceInfo(int deviceId) { if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */) { AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); @@ -330,148 +98,6 @@ public static void unregisterAudioDeviceCallback() { } } - /** - * This method is called by SDL using JNI. - */ - public static int[] audioOpen(int sampleRate, int audioFormat, int desiredChannels, int desiredFrames, int deviceId) { - return open(false, sampleRate, audioFormat, desiredChannels, desiredFrames, deviceId); - } - - /** - * This method is called by SDL using JNI. - */ - public static void audioWriteFloatBuffer(float[] buffer) { - if (mAudioTrack == null) { - Log.e(TAG, "Attempted to make audio call with uninitialized audio!"); - return; - } - - if (android.os.Build.VERSION.SDK_INT < 21 /* Android 5.0 (LOLLIPOP) */) { - Log.e(TAG, "Attempted to make an incompatible audio call with uninitialized audio! (floating-point output is supported since Android 5.0 Lollipop)"); - return; - } - - for (int i = 0; i < buffer.length;) { - int result = mAudioTrack.write(buffer, i, buffer.length - i, AudioTrack.WRITE_BLOCKING); - if (result > 0) { - i += result; - } else if (result == 0) { - try { - Thread.sleep(1); - } catch(InterruptedException e) { - // Nom nom - } - } else { - Log.w(TAG, "SDL audio: error return from write(float)"); - return; - } - } - } - - /** - * This method is called by SDL using JNI. - */ - public static void audioWriteShortBuffer(short[] buffer) { - if (mAudioTrack == null) { - Log.e(TAG, "Attempted to make audio call with uninitialized audio!"); - return; - } - - for (int i = 0; i < buffer.length;) { - int result = mAudioTrack.write(buffer, i, buffer.length - i); - if (result > 0) { - i += result; - } else if (result == 0) { - try { - Thread.sleep(1); - } catch(InterruptedException e) { - // Nom nom - } - } else { - Log.w(TAG, "SDL audio: error return from write(short)"); - return; - } - } - } - - /** - * This method is called by SDL using JNI. - */ - public static void audioWriteByteBuffer(byte[] buffer) { - if (mAudioTrack == null) { - Log.e(TAG, "Attempted to make audio call with uninitialized audio!"); - return; - } - - for (int i = 0; i < buffer.length; ) { - int result = mAudioTrack.write(buffer, i, buffer.length - i); - if (result > 0) { - i += result; - } else if (result == 0) { - try { - Thread.sleep(1); - } catch(InterruptedException e) { - // Nom nom - } - } else { - Log.w(TAG, "SDL audio: error return from write(byte)"); - return; - } - } - } - - /** - * This method is called by SDL using JNI. - */ - public static int[] recordingOpen(int sampleRate, int audioFormat, int desiredChannels, int desiredFrames, int deviceId) { - return open(true, sampleRate, audioFormat, desiredChannels, desiredFrames, deviceId); - } - - /** This method is called by SDL using JNI. */ - public static int recordingReadFloatBuffer(float[] buffer, boolean blocking) { - if (Build.VERSION.SDK_INT < 23 /* Android 6.0 (M) */) { - return 0; - } else { - return mAudioRecord.read(buffer, 0, buffer.length, blocking ? AudioRecord.READ_BLOCKING : AudioRecord.READ_NON_BLOCKING); - } - } - - /** This method is called by SDL using JNI. */ - public static int recordingReadShortBuffer(short[] buffer, boolean blocking) { - if (Build.VERSION.SDK_INT < 23 /* Android 6.0 (M) */) { - return mAudioRecord.read(buffer, 0, buffer.length); - } else { - return mAudioRecord.read(buffer, 0, buffer.length, blocking ? AudioRecord.READ_BLOCKING : AudioRecord.READ_NON_BLOCKING); - } - } - - /** This method is called by SDL using JNI. */ - public static int recordingReadByteBuffer(byte[] buffer, boolean blocking) { - if (Build.VERSION.SDK_INT < 23 /* Android 6.0 (M) */) { - return mAudioRecord.read(buffer, 0, buffer.length); - } else { - return mAudioRecord.read(buffer, 0, buffer.length, blocking ? AudioRecord.READ_BLOCKING : AudioRecord.READ_NON_BLOCKING); - } - } - - /** This method is called by SDL using JNI. */ - public static void audioClose() { - if (mAudioTrack != null) { - mAudioTrack.stop(); - mAudioTrack.release(); - mAudioTrack = null; - } - } - - /** This method is called by SDL using JNI. */ - public static void recordingClose() { - if (mAudioRecord != null) { - mAudioRecord.stop(); - mAudioRecord.release(); - mAudioRecord = null; - } - } - /** This method is called by SDL using JNI. */ public static void audioSetThreadPriority(boolean recording, int device_id) { try { diff --git a/android-project/app/src/main/java/org/libsdl/app/SDLDummyEdit.java b/android-project/app/src/main/java/org/libsdl/app/SDLDummyEdit.java index dca28145ec19c..40e556ff41066 100644 --- a/android-project/app/src/main/java/org/libsdl/app/SDLDummyEdit.java +++ b/android-project/app/src/main/java/org/libsdl/app/SDLDummyEdit.java @@ -12,6 +12,7 @@ public class SDLDummyEdit extends View implements View.OnKeyListener { InputConnection ic; + int input_type; public SDLDummyEdit(Context context) { super(context); @@ -20,6 +21,10 @@ public SDLDummyEdit(Context context) { setOnKeyListener(this); } + public void setInputType(int input_type) { + this.input_type = input_type; + } + @Override public boolean onCheckIsTextEditor() { return true; @@ -51,8 +56,7 @@ public boolean onKeyPreIme (int keyCode, KeyEvent event) { public InputConnection onCreateInputConnection(EditorInfo outAttrs) { ic = new SDLInputConnection(this, true); - outAttrs.inputType = InputType.TYPE_CLASS_TEXT | - InputType.TYPE_TEXT_FLAG_MULTI_LINE; + outAttrs.inputType = input_type; outAttrs.imeOptions = EditorInfo.IME_FLAG_NO_EXTRACT_UI | EditorInfo.IME_FLAG_NO_FULLSCREEN /* API 11 */; diff --git a/build-scripts/build-release.py b/build-scripts/build-release.py index 735890005ba9d..8224fbdf01acd 100755 --- a/build-scripts/build-release.py +++ b/build-scripts/build-release.py @@ -82,7 +82,7 @@ def __init__(self, executer: Executer, year: typing.Optional[str]=None): self.msbuild = self.find_msbuild() @property - def dry(self): + def dry(self) -> bool: return self.executer.dry VS_YEAR_TO_VERSION = { @@ -97,7 +97,7 @@ def find_vsdevcmd(self, year: typing.Optional[str]=None) -> typing.Optional[Path vswhere_spec = ["-latest"] if year is not None: try: - version = cls.VS_YEAR_TO_VERSION[year] + version = self.VS_YEAR_TO_VERSION[year] except KeyError: logger.error("Invalid Visual Studio year") return None @@ -115,7 +115,7 @@ def find_vsdevcmd(self, year: typing.Optional[str]=None) -> typing.Optional[Path return vsdevcmd_path def find_msbuild(self) -> typing.Optional[Path]: - vswhere_cmd = ["vswhere", "-latest", "-requires", "Microsoft.Component.MSBuild", "-find", "MSBuild\**\Bin\MSBuild.exe"] + vswhere_cmd = ["vswhere", "-latest", "-requires", "Microsoft.Component.MSBuild", "-find", r"MSBuild\**\Bin\MSBuild.exe"] msbuild_path = Path(self.executer.run(vswhere_cmd, stdout=True, dry_out="/tmp/MSBuild.exe").stdout.strip()) logger.info("MSBuild path = %s", msbuild_path) if self.dry: @@ -150,10 +150,10 @@ def __init__(self, project: str, commit: str, root: Path, dist_path: Path, secti self.executer = executer self.cmake_generator = cmake_generator - self.artifacts = {} + self.artifacts: dict[str, Path] = {} @property - def dry(self): + def dry(self) -> bool: return self.executer.dry def prepare(self): @@ -161,7 +161,7 @@ def prepare(self): self.dist_path.mkdir(parents=True, exist_ok=True) TreeItem = collections.namedtuple("TreeItem", ("path", "mode", "data", "time")) - def _get_file_times(self, paths: tuple[str]) -> dict[str, datetime.datetime]: + def _get_file_times(self, paths: tuple[str, ...]) -> dict[str, datetime.datetime]: dry_out = textwrap.dedent("""\ time=2024-03-14T15:40:25-07:00 @@ -170,18 +170,18 @@ def _get_file_times(self, paths: tuple[str]) -> dict[str, datetime.datetime]: git_log_out = self.executer.run(["git", "log", "--name-status", '--pretty=time=%cI', self.commit], stdout=True, dry_out=dry_out).stdout.splitlines(keepends=False) current_time = None set_paths = set(paths) - path_times = {} + path_times: dict[str, datetime.datetime] = {} for line in git_log_out: if not line: continue if line.startswith("time="): current_time = datetime.datetime.fromisoformat(line.removeprefix("time=")) continue - mod_type, paths = line.split(maxsplit=1) + mod_type, file_paths = line.split(maxsplit=1) assert current_time is not None - for path in paths.split(): - if path in set_paths and path not in path_times: - path_times[path] = current_time + for file_path in file_paths.split(): + if file_path in set_paths and file_path not in path_times: + path_times[file_path] = current_time assert set(path_times.keys()) == set_paths return path_times @@ -191,19 +191,25 @@ def _path_filter(path: str): return False return True - def _get_git_contents(self) -> dict[str, (TreeItem, bytes, datetime.datetime)]: + def _get_git_contents(self) -> dict[str, TreeItem]: contents_tgz = subprocess.check_output(["git", "archive", "--format=tar.gz", self.commit, "-o", "/dev/stdout"], text=False) contents = tarfile.open(fileobj=io.BytesIO(contents_tgz), mode="r:gz") filenames = tuple(m.name for m in contents if m.isfile()) assert "src/SDL.c" in filenames assert "include/SDL3/SDL.h" in filenames file_times = self._get_file_times(filenames) - git_contents = { - ti.name: self.TreeItem(path=ti.name, mode=ti.mode, data=contents.extractfile(ti.name).read(), time=file_times[ti.name]) for ti in contents if ti.isfile() and self._path_filter(ti.name) - } + git_contents = {} + for ti in contents: + if not ti.isfile(): + continue + if not self._path_filter(ti.name): + continue + contents_file = contents.extractfile(ti.name) + assert contents_file, f"{ti.name} is not a file" + git_contents[ti.name] = self.TreeItem(path=ti.name, mode=ti.mode, data=contents_file.read(), time=file_times[ti.name]) return git_contents - def create_source_archives(self): + def create_source_archives(self) -> None: archive_base = f"{self.project}-{self.version}" git_contents = self._get_git_contents() @@ -257,7 +263,7 @@ def create_source_archives(self): self.artifacts[f"src-tar-{comp}"] = tar_path - def create_xcframework(self, configuration: str="Release"): + def create_xcframework(self, configuration: str="Release") -> None: dmg_in = self.root / f"Xcode/SDL/build/SDL3.dmg" dmg_in.unlink(missing_ok=True) self.executer.run(["xcodebuild", "-project", str(self.root / "Xcode/SDL/SDL.xcodeproj"), "-target", "SDL3.dmg", "-configuration", configuration]) @@ -272,7 +278,7 @@ def create_xcframework(self, configuration: str="Release"): self.artifacts["dmg"] = dmg_out @property - def git_hash_data(self): + def git_hash_data(self) -> bytes: return f"{self.commit}\n".encode() def _tar_add_git_hash(self, tar_object: tarfile.TarFile, root: typing.Optional[str]=None, time: typing.Optional[datetime.datetime]=None): @@ -285,7 +291,7 @@ def _tar_add_git_hash(self, tar_object: tarfile.TarFile, root: typing.Optional[s tar_info = tarfile.TarInfo(path) tar_info.mode = 0o100644 tar_info.size = len(self.git_hash_data) - tar_info.mtime = time.timestamp() + tar_info.mtime = int(time.timestamp()) tar_object.addfile(tar_info, fileobj=io.BytesIO(self.git_hash_data)) def _zip_add_git_hash(self, zip_file: zipfile.ZipFile, root: typing.Optional[str]=None, time: typing.Optional[datetime.datetime]=None): @@ -301,7 +307,7 @@ def _zip_add_git_hash(self, zip_file: zipfile.ZipFile, root: typing.Optional[str zip_info.compress_type = zipfile.ZIP_DEFLATED zip_file.writestr(zip_info, data=self.git_hash_data) - def create_mingw_archives(self): + def create_mingw_archives(self) -> None: build_type = "Release" mingw_archs = ("i686", "x86_64") build_parent_dir = self.root / "build-mingw" @@ -345,18 +351,18 @@ def create_mingw_archives(self): self.executer.run(["cmake", "--install", str(build_path), "--strip", "--config", build_type]) arch_files[arch] = list(Path(r) / f for r, _, files in os.walk(install_path) for f in files) - extra_files = [ - ("mingw/pkg-support/INSTALL.txt", ""), - ("mingw/pkg-support/Makefile", ""), - ("mingw/pkg-support/cmake/sdl3-config.cmake", "cmake/"), - ("mingw/pkg-support/cmake/sdl3-config-version.cmake", "cmake/"), + extra_files = ( + ("build-scripts/pkg-support/mingw/INSTALL.txt", ""), + ("build-scripts/pkg-support/mingw/Makefile", ""), + ("build-scripts/pkg-support/mingw/cmake/SDL3Config.cmake", "cmake/"), + ("build-scripts/pkg-support/mingw/cmake/SDL3ConfigVersion.cmake", "cmake/"), ("BUGS.txt", ""), ("CREDITS.md", ""), ("README-SDL.txt", ""), ("WhatsNew.txt", ""), ("LICENSE.txt", ""), ("README.md", ""), - ] + ) test_files = list(Path(r) / f for r, _, files in os.walk(self.root / "test") for f in files) # FIXME: split SDL3.dll debug information into debug library @@ -385,7 +391,7 @@ def create_mingw_archives(self): self.artifacts[f"mingw-devel-tar-{comp}"] = tar_paths[comp] - def build_vs(self, arch: str, platform: str, vs: VisualStudio, configuration: str="Release"): + def build_vs(self, arch: str, platform: str, vs: VisualStudio, configuration: str="Release") -> VcArchDevel: dll_path = self.root / f"VisualC/SDL/{platform}/{configuration}/{self.project}.dll" pdb_path = self.root / f"VisualC/SDL/{platform}/{configuration}/{self.project}.pdb" imp_path = self.root / f"VisualC/SDL/{platform}/{configuration}/{self.project}.lib" @@ -430,7 +436,7 @@ def build_vs(self, arch: str, platform: str, vs: VisualStudio, configuration: st return VcArchDevel(dll=dll_path, pdb=pdb_path, imp=imp_path, test=test_path) - def build_vs_cmake(self, arch: str, arch_cmake: str): + def build_vs_cmake(self, arch: str, arch_cmake: str) -> VcArchDevel: build_path = self.root / f"build-vs-{arch}" install_path = build_path / "prefix" dll_path = install_path / f"bin/{self.project}.dll" @@ -500,7 +506,7 @@ def build_vs_cmake(self, arch: str, arch_cmake: str): return VcArchDevel(dll=dll_path, pdb=pdb_path, imp=imp_path, test=test_path) - def build_vs_devel(self, arch_vc: dict[str, VcArchDevel]): + def build_vs_devel(self, arch_vc: dict[str, VcArchDevel]) -> None: zip_path = self.dist_path / f"{self.project}-devel-{self.version}-VC.zip" archive_prefix = f"{self.project}-{self.version}" @@ -554,7 +560,7 @@ def detect_android_api(self, android_home: str) -> typing.Optional[int]: logger.info("Selected API version %d", android_api) return android_api - def get_prefab_json_text(self): + def get_prefab_json_text(self) -> str: return textwrap.dedent(f"""\ {{ "schema_version": 2, @@ -564,7 +570,7 @@ def get_prefab_json_text(self): }} """) - def get_prefab_module_json_text(self, library_name: str, extra_libs: list[str]): + def get_prefab_module_json_text(self, library_name: str, extra_libs: list[str]) -> str: export_libraries_str = ", ".join(f"\"-l{lib}\"" for lib in extra_libs) return textwrap.dedent(f"""\ {{ @@ -573,7 +579,7 @@ def get_prefab_module_json_text(self, library_name: str, extra_libs: list[str]): }} """) - def get_prefab_abi_json_text(self, abi: str, cpp: bool, shared: bool): + def get_prefab_abi_json_text(self, abi: str, cpp: bool, shared: bool) -> str: return textwrap.dedent(f"""\ {{ "abi": "{abi}", @@ -584,7 +590,7 @@ def get_prefab_abi_json_text(self, abi: str, cpp: bool, shared: bool): }} """) - def get_android_manifest_text(self): + def get_android_manifest_text(self) -> str: return textwrap.dedent(f"""\ """) - def create_android_archives(self, android_api: int, android_home: Path, android_ndk_home: Path, android_abis: list[str]): + def create_android_archives(self, android_api: int, android_home: Path, android_ndk_home: Path, android_abis: list[str]) -> None: cmake_toolchain_file = Path(android_ndk_home) / "build/cmake/android.toolchain.cmake" if not cmake_toolchain_file.exists(): logger.error("CMake toolchain file does not exist (%s)", cmake_toolchain_file) @@ -603,9 +609,23 @@ def create_android_archives(self, android_api: int, android_home: Path, android_ aar_path = self.dist_path / f"{self.project}-{self.version}.aar" added_global_files = False with zipfile.ZipFile(aar_path, "w", compression=zipfile.ZIP_DEFLATED) as zip_object: + install_txt = (self.root / "build-scripts/pkg-support/android/INSTALL.md.in").read_text() + install_txt = install_txt.replace("@PROJECT_VERSION@", self.version) + install_txt = install_txt.replace("@PROJECT_NAME@", self.project) + zip_object.writestr("INSTALL.md", install_txt) + project_description = { + "name": self.project, + "version": self.version, + "git-hash": self.commit, + } + zip_object.writestr("description.json", json.dumps(project_description, indent=0)) zip_object.writestr("AndroidManifest.xml", self.get_android_manifest_text()) zip_object.write(self.root / "android-project/app/proguard-rules.pro", arcname="proguard.txt") zip_object.write(self.root / "LICENSE.txt", arcname="META-INF/LICENSE.txt") + zip_object.write(self.root / "cmake/sdlcpu.cmake", arcname="cmake/sdlcpu.cmake") + zip_object.write(self.root / "build-scripts/pkg-support/android/__main__.py", arcname="__main__.py") + zip_object.write(self.root / "build-scripts/pkg-support/android/cmake/SDL3Config.cmake", arcname="cmake/SDL3Config.cmake") + zip_object.write(self.root / "build-scripts/pkg-support/android/cmake/SDL3ConfigVersion.cmake", arcname="cmake/SDL3ConfigVersion.cmake") zip_object.writestr("prefab/prefab.json", self.get_prefab_json_text()) self._zip_add_git_hash(zip_file=zip_object) @@ -701,7 +721,7 @@ def create_android_archives(self, android_api: int, android_home: Path, android_ self.artifacts[f"android-aar"] = aar_path @classmethod - def extract_sdl_version(cls, root: Path, project: str): + def extract_sdl_version(cls, root: Path, project: str) -> str: with open(root / f"include/{project}/SDL_version.h", "r") as f: text = f.read() major = next(re.finditer(r"^#define SDL_MAJOR_VERSION\s+([0-9]+)$", text, flags=re.M)).group(1) @@ -710,7 +730,7 @@ def extract_sdl_version(cls, root: Path, project: str): return f"{major}.{minor}.{micro}" -def main(argv=None): +def main(argv=None) -> int: parser = argparse.ArgumentParser(allow_abbrev=False, description="Create SDL release artifacts") parser.add_argument("--root", metavar="DIR", type=Path, default=Path(__file__).absolute().parents[1], help="Root of SDL") parser.add_argument("--out", "-o", metavar="DIR", dest="dist_path", type=Path, default="dist", help="Output directory") @@ -739,7 +759,7 @@ def main(argv=None): args.dist_path = args.dist_path / "dry" if args.github: - section_printer = GitHubSectionPrinter() + section_printer: SectionPrinter = GitHubSectionPrinter() else: section_printer = SectionPrinter() diff --git a/build-scripts/build-web-examples.pl b/build-scripts/build-web-examples.pl index ebed3fab7e121..c11589e73a398 100755 --- a/build-scripts/build-web-examples.pl +++ b/build-scripts/build-web-examples.pl @@ -80,12 +80,13 @@ sub handle_example_dir { opendir(my $dh, "$examples_dir/$category/$example") or die("Couldn't opendir '$examples_dir/$category/$example': $!\n"); my $spc = ''; while (readdir($dh)) { - next if not /\.c\Z/; # only care about .c files. my $path = "$examples_dir/$category/$example/$_"; next if not -f $path; # only care about files. - push @files, $path; - $files_str .= "$spc$path"; - $spc = ' '; + push @files, $path if /\.[ch]\Z/; # add .c and .h files to source code. + if (/\.c\Z/) { # only care about .c files for compiling. + $files_str .= "$spc$path"; + $spc = ' '; + } } closedir($dh); @@ -103,6 +104,17 @@ sub handle_example_dir { my $jsdst = "$dst/$jsfname"; my $wasmdst = "$dst/$wasmfname"; + my $description = ''; + if (open(my $readmetxth, '<', "$examples_dir/$category/$example/README.txt")) { + while (<$readmetxth>) { + chomp; + s/\A\s+//; + s/\s+\Z//; + $description .= "$_
"; + } + close($readmetxth); + } + do_mkdir($dst); do_copy($jssrc, $jsdst); do_copy($wasmsrc, $wasmdst); @@ -112,7 +124,7 @@ sub handle_example_dir { my $pid = open2(my $child_out, my $child_in, $highlight_cmd); my $htmlified_source_code = ''; - foreach (@files) { + foreach (sort(@files)) { my $path = $_; open my $srccode, '<', $path or die("Couldn't open '$path': $!\n"); my $fname = "$path"; @@ -121,6 +133,7 @@ sub handle_example_dir { while (<$srccode>) { print $child_in $_; } + print $child_in "\n\n\n"; close($srccode); } @@ -142,6 +155,7 @@ sub handle_example_dir { s/\@example_name\@/$example/g; s/\@javascript_file\@/$jsfname/g; s/\@htmlified_source_code\@/$htmlified_source_code/g; + s/\@description\@/$description/g; $html .= $_; } close($htmltemplate); diff --git a/build-scripts/create-android-project.py b/build-scripts/create-android-project.py index 84dbf9b1ac98c..76ad853d29e6e 100755 --- a/build-scripts/create-android-project.py +++ b/build-scripts/create-android-project.py @@ -10,7 +10,7 @@ SDL_ROOT = Path(__file__).resolve().parents[1] -def extract_sdl_version(): +def extract_sdl_version() -> str: """ Extract SDL version from SDL3/SDL_version.h """ @@ -23,8 +23,8 @@ def extract_sdl_version(): micro = int(next(re.finditer(r"#define\s+SDL_MICRO_VERSION\s+([0-9]+)", data)).group(1)) return f"{major}.{minor}.{micro}" -def replace_in_file(path, regex_what, replace_with): - with open(path, "r") as f: +def replace_in_file(path: Path, regex_what: str, replace_with: str) -> None: + with path.open("r") as f: data = f.read() new_data, count = re.subn(regex_what, replace_with, data) @@ -35,12 +35,12 @@ def replace_in_file(path, regex_what, replace_with): f.write(new_data) -def android_mk_use_prefab(path): +def android_mk_use_prefab(path: Path) -> None: """ Replace relative SDL inclusion with dependency on prefab package """ - with open(path) as f: + with path.open() as f: data = "".join(line for line in f.readlines() if "# SDL" not in line) data, _ = re.subn("[\n]{3,}", "\n\n", data) @@ -55,18 +55,19 @@ def android_mk_use_prefab(path): $(call import-module,prefab/SDL3) """) - with open(path, "w") as f: + with path.open("w") as f: f.write(newdata) -def cmake_mk_no_sdl(path): + +def cmake_mk_no_sdl(path: Path) -> None: """ Don't add the source directories of SDL/SDL_image/SDL_mixer/... """ - with open(path) as f: + with path.open() as f: lines = f.readlines() - newlines = [] + newlines: list[str] = [] for line in lines: if "add_subdirectory(SDL" in line: while newlines[-1].startswith("#"): @@ -76,11 +77,12 @@ def cmake_mk_no_sdl(path): newdata, _ = re.subn("[\n]{3,}", "\n\n", "".join(newlines)) - with open(path, "w") as f: + with path.open("w") as f: f.write(newdata) -def gradle_add_prefab_and_aar(path, aar): - with open(path) as f: + +def gradle_add_prefab_and_aar(path: Path, aar: str) -> None: + with path.open() as f: data = f.read() data, count = re.subn("android {", textwrap.dedent(""" @@ -95,25 +97,30 @@ def gradle_add_prefab_and_aar(path, aar): implementation files('libs/{aar}')"""), data) assert count == 1 - with open(path, "w") as f: + with path.open("w") as f: f.write(data) -def gradle_add_package_name(path, package_name): - with open(path) as f: + +def gradle_add_package_name(path: Path, package_name: str) -> None: + with path.open() as f: data = f.read() data, count = re.subn("org.libsdl.app", package_name, data) assert count >= 1 - with open(path, "w") as f: + with path.open("w") as f: f.write(data) -def main(): +def main() -> int: description = "Create a simple Android gradle project from input sources." - epilog = "You need to manually copy a prebuilt SDL3 Android archive into the project tree when using the aar variant." - parser = ArgumentParser(description=description, allow_abbrev=False) - parser.add_argument("package_name", metavar="PACKAGENAME", help="Android package name e.g. com.yourcompany.yourapp") + epilog = textwrap.dedent("""\ + You need to manually copy a prebuilt SDL3 Android archive into the project tree when using the aar variant. + + Any changes you have done to the sources in the Android project will be lost + """) + parser = ArgumentParser(description=description, epilog=epilog, allow_abbrev=False) + parser.add_argument("package_name", metavar="PACKAGENAME", help="Android package name (e.g. com.yourcompany.yourapp)") parser.add_argument("sources", metavar="SOURCE", nargs="*", help="Source code of your application. The files are copied to the output directory.") parser.add_argument("--variant", choices=["copy", "symlink", "aar"], default="copy", help="Choose variant of SDL project (copy: copy SDL sources, symlink: symlink SDL sources, aar: use Android aar archive)") parser.add_argument("--output", "-o", default=SDL_ROOT / "build", type=Path, help="Location where to store the Android project") @@ -225,6 +232,7 @@ def main(): print("To build and install to a device for testing, run the following:") print(f"cd {build_path}") print("./gradlew installDebug") + return 0 if __name__ == "__main__": raise SystemExit(main()) diff --git a/build-scripts/pkg-support/android/INSTALL.md.in b/build-scripts/pkg-support/android/INSTALL.md.in new file mode 100644 index 0000000000000..9d633f4a2d00c --- /dev/null +++ b/build-scripts/pkg-support/android/INSTALL.md.in @@ -0,0 +1,58 @@ + +This Android archive allows use of @PROJECT_NAME@ in your Android project, without needing to copy any SDL source. +For integration with CMake/ndk-build, it uses [prefab](https://google.github.io/prefab/). + +Copy this archive (@PROJECT_NAME@-@PROJECT_VERSION@.aar) to a `app/libs` directory of your project. + +In `app/gradle.build` of your Android project, add: +``` +android { + /* ... */ + buildFeatures { + prefab true + } +} +dependencies { + implementation files('libs/@PROJECT_NAME@-@PROJECT_VERSION@.aar') + /* ... */ +} +``` + +If you're using CMake, add the following to your CMakeLists.txt: +``` +find_package(@PROJECT_NAME@ REQUIRED CONFIG) +target_link_libraries(yourgame PRIVATE @PROJECT_NAME@::@PROJECT_NAME@) +``` + +If you're using ndk-build, add the following somewhere after `LOCAL_MODULE := yourgame` to your `Android.mk` or `Application.mk`: +``` +# https://google.github.io/prefab/build-systems.html + +# Add the prefab modules to the import path. +$(call import-add-path,/out) + +# Import @PROJECT_NAME@ so we can depend on it. +$(call import-module,prefab/@PROJECT_NAME@) +``` + +--- + +For advanced users: + +If you want to build a 3rd party library outside Gradle, +running the following command will extract the Android archive into a more common directory structure. +``` +python @PROJECT_NAME@-@PROJECT_VERSION@.aar -o android_prefix +``` +Add `--help` for a list of all available options. + + +Look at the example programs in ./test (of the source archive), and check out online documentation: + https://wiki.libsdl.org/SDL3/FrontPage + +Join the SDL discourse server if you want to join the community: + https://discourse.libsdl.org/ + + +That's it! +Sam Lantinga diff --git a/build-scripts/pkg-support/android/__main__.py b/build-scripts/pkg-support/android/__main__.py new file mode 100755 index 0000000000000..c27a63316186b --- /dev/null +++ b/build-scripts/pkg-support/android/__main__.py @@ -0,0 +1,104 @@ +#!/usr/bin/env python + +""" +Create a SDL SDK prefix from an Android archive +This file is meant to be placed in a the root of an android .aar archive + +Example usage: +```sh +python SDL3-3.2.0.aar -o /usr/opt/android-sdks +cmake -S my-project \ + -DCMAKE_PREFIX_PATH=/usr/opt/android-sdks \ + -DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK_HOME/build/cmake/android.toolchain.cmake \ + -B build-arm64 -DANDROID_ABI=arm64-v8a \ + -DCMAKE_BUILD_TYPE=Releaase +cmake --build build-arm64 +``` +""" +import argparse +import io +import json +import os +import pathlib +import re +import stat +import zipfile + + +AAR_PATH = pathlib.Path(__file__).resolve().parent +ANDROID_ARCHS = { "armeabi-v7a", "arm64-v8a", "x86", "x86_64" } + + +def main(): + parser = argparse.ArgumentParser( + description="Convert an Android .aar archive into a SDK", + allow_abbrev=False, + ) + parser.add_argument("-o", dest="output", type=pathlib.Path, required=True, help="Folder where to store the SDK") + args = parser.parse_args() + + print(f"Creating a SDK at {args.output}...") + + prefix = args.output + incdir = prefix / "include" + libdir = prefix / "lib" + + RE_LIB_MODULE_ARCH = re.compile(r"prefab/modules/(?P[A-Za-z0-9_-]+)/libs/android\.(?P[a-zA-Z0-9_-]+)/(?Plib[A-Za-z0-9_]+\.(?:so|a))") + RE_INC_MODULE_ARCH = re.compile(r"prefab/modules/(?P[A-Za-z0-9_-]+)/include/(?P
[a-zA-Z0-9_./-]+)") + RE_LICENSE = re.compile(r"(?:.*/)?(?P(?:license|copying)(?:\.md|\.txt)?)", flags=re.I) + RE_PROGUARD = re.compile(r"(?:.*/)?(?Pproguard.*\.(?:pro|txt))", flags=re.I) + RE_CMAKE = re.compile(r"(?:.*/)?(?P.*\.cmake)", flags=re.I) + + with zipfile.ZipFile(AAR_PATH) as zf: + project_description = json.loads(zf.read("description.json")) + project_name = project_description["name"] + project_version = project_description["version"] + licensedir = prefix / "share/licenses" / project_name + cmakedir = libdir / "cmake" / project_name + javadir = prefix / "share/java" / project_name + javadocdir = prefix / "share/javadoc" / project_name + + def read_zipfile_and_write(path: pathlib.Path, zippath: str): + data = zf.read(zippath) + path.parent.mkdir(parents=True, exist_ok=True) + path.write_bytes(data) + + for zip_info in zf.infolist(): + zippath = zip_info.filename + if m := RE_LIB_MODULE_ARCH.match(zippath): + lib_path = libdir / m["arch"] / m["filename"] + read_zipfile_and_write(lib_path, zippath) + if m["filename"].endswith(".so"): + os.chmod(lib_path, stat.S_IRWXU | stat.S_IRGRP | stat.S_IXGRP | stat.S_IROTH | stat.S_IXOTH) + + elif m := RE_INC_MODULE_ARCH.match(zippath): + header_path = incdir / m["header"] + read_zipfile_and_write(header_path, zippath) + elif m:= RE_LICENSE.match(zippath): + license_path = licensedir / m["filename"] + read_zipfile_and_write(license_path, zippath) + elif m:= RE_PROGUARD.match(zippath): + proguard_path = javadir / m["filename"] + read_zipfile_and_write(proguard_path, zippath) + elif m:= RE_CMAKE.match(zippath): + cmake_path = cmakedir / m["filename"] + read_zipfile_and_write(cmake_path, zippath) + elif zippath == "classes.jar": + versioned_jar_path = javadir / f"{project_name}-{project_version}.jar" + unversioned_jar_path = javadir / f"{project_name}.jar" + read_zipfile_and_write(versioned_jar_path, zippath) + os.symlink(src=versioned_jar_path.name, dst=unversioned_jar_path) + elif zippath == "classes-sources.jar": + jarpath = javadir / f"{project_name}-{project_version}-sources.jar" + read_zipfile_and_write(jarpath, zippath) + elif zippath == "classes-doc.jar": + data = zf.read(zippath) + with zipfile.ZipFile(io.BytesIO(data)) as doc_zf: + doc_zf.extractall(javadocdir) + + print("... done") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/build-scripts/pkg-support/android/cmake/SDL3Config.cmake b/build-scripts/pkg-support/android/cmake/SDL3Config.cmake new file mode 100644 index 0000000000000..fb914701ff940 --- /dev/null +++ b/build-scripts/pkg-support/android/cmake/SDL3Config.cmake @@ -0,0 +1,148 @@ +# SDL CMake configuration file: +# This file is meant to be placed in lib/cmake/SDL3 subfolder of a reconstructed Android SDL3 SDK + +cmake_minimum_required(VERSION 3.0...3.5) + +include(FeatureSummary) +set_package_properties(SDL3 PROPERTIES + URL "https://www.libsdl.org/" + DESCRIPTION "low level access to audio, keyboard, mouse, joystick, and graphics hardware" +) + +# Copied from `configure_package_config_file` +macro(set_and_check _var _file) + set(${_var} "${_file}") + if(NOT EXISTS "${_file}") + message(FATAL_ERROR "File or directory ${_file} referenced by variable ${_var} does not exist !") + endif() +endmacro() + +# Copied from `configure_package_config_file` +macro(check_required_components _NAME) + foreach(comp ${${_NAME}_FIND_COMPONENTS}) + if(NOT ${_NAME}_${comp}_FOUND) + if(${_NAME}_FIND_REQUIRED_${comp}) + set(${_NAME}_FOUND FALSE) + endif() + endif() + endforeach() +endmacro() + +set(SDL3_FOUND TRUE) + +if(SDL_CPU_X86) + set(_sdl_arch_subdir "x86") +elseif(SDL_CPU_X64) + set(_sdl_arch_subdir "x86_64") +elseif(SDL_CPU_ARM32) + set(_sdl_arch_subdir "armeabi-v7a") +elseif(SDL_CPU_ARM64) + set(_sdl_arch_subdir "arm64-v8a") +else() + set(SDL3_FOUND FALSE) + return() +endif() + +get_filename_component(_sdl3_prefix "${CMAKE_CURRENT_LIST_DIR}/.." ABSOLUTE) +get_filename_component(_sdl3_prefix "${_sdl3_prefix}/.." ABSOLUTE) +get_filename_component(_sdl3_prefix "${_sdl3_prefix}/.." ABSOLUTE) +set_and_check(_sdl3_prefix "${_sdl3_prefix}") +set(_sdl3_include_dirs "${_sdl3_prefix}/include") + +set(_sdl3_lib "${_sdl3_prefix}/lib/${_sdl_arch_subdir}/libSDL3.so") +set(_sdl3test_lib "${_sdl3_prefix}/lib/${_sdl_arch_subdir}/libSDL3_test.a") +set(_sdl3_jar "${_sdl3_prefix}/share/java/SDL3/SDL3-${SDL3_VERSION}.jar") + +unset(_sdl_arch_subdir) +unset(_sdl3_prefix) + +# All targets are created, even when some might not be requested though COMPONENTS. +# This is done for compatibility with CMake generated SDL3-target.cmake files. + +if(NOT TARGET SDL3::Headers) + add_library(SDL3::Headers INTERFACE IMPORTED) + set_target_properties(SDL3::Headers + PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES "${_sdl3_include_dirs}" + ) +endif() +set(SDL3_Headers_FOUND TRUE) +unset(_sdl3_include_dirs) + +if(EXISTS "${_sdl3_lib}") + if(NOT TARGET SDL3::SDL3-shared) + add_library(SDL3::SDL3-shared SHARED IMPORTED) + set_target_properties(SDL3::SDL3-shared + PROPERTIES + INTERFACE_LINK_LIBRARIES "SDL3::Headers" + IMPORTED_LOCATION "${_sdl3_lib}" + COMPATIBLE_INTERFACE_BOOL "SDL3_SHARED" + INTERFACE_SDL3_SHARED "ON" + COMPATIBLE_INTERFACE_STRING "SDL_VERSION" + INTERFACE_SDL_VERSION "SDL3" + ) + endif() + set(SDL3_SDL3-shared_FOUND TRUE) +else() + set(SDL3_SDL3-shared_FOUND FALSE) +endif() +unset(_sdl3_lib) + +set(SDL3_SDL3-static_FOUND FALSE) + +if(EXISTS "${_sdl3test_lib}") + if(NOT TARGET SDL3::SDL3_test) + add_library(SDL3::SDL3_test STATIC IMPORTED) + set_target_properties(SDL3::SDL3_test + PROPERTIES + INTERFACE_LINK_LIBRARIES "SDL3::Headers" + IMPORTED_LOCATION "${_sdl3test_lib}" + COMPATIBLE_INTERFACE_STRING "SDL_VERSION" + INTERFACE_SDL_VERSION "SDL3" + ) + endif() + set(SDL3_SDL3_test_FOUND TRUE) +else() + set(SDL3_SDL3_test_FOUND FALSE) +endif() +unset(_sdl3test_lib) + +if(SDL3_SDL3-shared_FOUND) + set(SDL3_SDL3_FOUND TRUE) +endif() + +function(_sdl_create_target_alias_compat NEW_TARGET TARGET) + if(CMAKE_VERSION VERSION_LESS "3.18") + # Aliasing local targets is not supported on CMake < 3.18, so make it global. + add_library(${NEW_TARGET} INTERFACE IMPORTED) + set_target_properties(${NEW_TARGET} PROPERTIES INTERFACE_LINK_LIBRARIES "${TARGET}") + else() + add_library(${NEW_TARGET} ALIAS ${TARGET}) + endif() +endfunction() + +# Make sure SDL3::SDL3 always exists +if(NOT TARGET SDL3::SDL3) + if(TARGET SDL3::SDL3-shared) + _sdl_create_target_alias_compat(SDL3::SDL3 SDL3::SDL3-shared) + endif() +endif() + +if(EXISTS "${_sdl3_jar}") + if(NOT TARGET SDL3::Jar) + add_library(SDL3::Jar INTERFACE IMPORTED) + set_property(TARGET SDL3::Jar PROPERTY JAR_FILE "${_sdl3_jar}") + endif() + set(SDL3_Jar_FOUND TRUE) +else() + set(SDL3_Jar_FOUND FALSE) +endif() +unset(_sdl3_jar) + +check_required_components(SDL3) + +set(SDL3_LIBRARIES SDL3::SDL3) +set(SDL3_STATIC_LIBRARIES SDL3::SDL3-static) +set(SDL3_STATIC_PRIVATE_LIBS) + +set(SDL3TEST_LIBRARY SDL3::SDL3_test) diff --git a/build-scripts/pkg-support/android/cmake/SDL3ConfigVersion.cmake b/build-scripts/pkg-support/android/cmake/SDL3ConfigVersion.cmake new file mode 100644 index 0000000000000..d2663c35ee8a1 --- /dev/null +++ b/build-scripts/pkg-support/android/cmake/SDL3ConfigVersion.cmake @@ -0,0 +1,57 @@ +# based on the files generated by CMake's write_basic_package_version_file + +# SDL CMake version configuration file: +# This file is meant to be placed in a lib/cmake/SDL3 subfolder of a reconstructed Android SDL3 SDK + +if(NOT EXISTS "${CMAKE_CURRENT_LIST_DIR}/../../../include/SDL3/SDL_version.h") + message(AUTHOR_WARNING "Could not find SDL3/SDL_version.h. This script is meant to be placed in the root of SDL3-devel-3.x.y-VC") + return() +endif() + +file(READ "${CMAKE_CURRENT_LIST_DIR}/../../../include/SDL3/SDL_version.h" _sdl_version_h) +string(REGEX MATCH "#define[ \t]+SDL_MAJOR_VERSION[ \t]+([0-9]+)" _sdl_major_re "${_sdl_version_h}") +set(_sdl_major "${CMAKE_MATCH_1}") +string(REGEX MATCH "#define[ \t]+SDL_MINOR_VERSION[ \t]+([0-9]+)" _sdl_minor_re "${_sdl_version_h}") +set(_sdl_minor "${CMAKE_MATCH_1}") +string(REGEX MATCH "#define[ \t]+SDL_MICRO_VERSION[ \t]+([0-9]+)" _sdl_micro_re "${_sdl_version_h}") +set(_sdl_micro "${CMAKE_MATCH_1}") +if(_sdl_major_re AND _sdl_minor_re AND _sdl_micro_re) + set(PACKAGE_VERSION "${_sdl_major}.${_sdl_minor}.${_sdl_micro}") +else() + message(AUTHOR_WARNING "Could not extract version from SDL3/SDL_version.h.") + return() +endif() + +if(PACKAGE_FIND_VERSION_RANGE) + # Package version must be in the requested version range + if ((PACKAGE_FIND_VERSION_RANGE_MIN STREQUAL "INCLUDE" AND PACKAGE_VERSION VERSION_LESS PACKAGE_FIND_VERSION_MIN) + OR ((PACKAGE_FIND_VERSION_RANGE_MAX STREQUAL "INCLUDE" AND PACKAGE_VERSION VERSION_GREATER PACKAGE_FIND_VERSION_MAX) + OR (PACKAGE_FIND_VERSION_RANGE_MAX STREQUAL "EXCLUDE" AND PACKAGE_VERSION VERSION_GREATER_EQUAL PACKAGE_FIND_VERSION_MAX))) + set(PACKAGE_VERSION_COMPATIBLE FALSE) + else() + set(PACKAGE_VERSION_COMPATIBLE TRUE) + endif() +else() + if(PACKAGE_VERSION VERSION_LESS PACKAGE_FIND_VERSION) + set(PACKAGE_VERSION_COMPATIBLE FALSE) + else() + set(PACKAGE_VERSION_COMPATIBLE TRUE) + if(PACKAGE_FIND_VERSION STREQUAL PACKAGE_VERSION) + set(PACKAGE_VERSION_EXACT TRUE) + endif() + endif() +endif() + +# if the using project doesn't have CMAKE_SIZEOF_VOID_P set, fail. +if("${CMAKE_SIZEOF_VOID_P}" STREQUAL "") + set(PACKAGE_VERSION_UNSUITABLE TRUE) +endif() + +include("${CMAKE_CURRENT_LIST_DIR}/sdlcpu.cmake") +SDL_DetectTargetCPUArchitectures(_detected_archs) + +# check that the installed version has a compatible architecture as the one which is currently searching: +if(NOT(SDL_CPU_X86 OR SDL_CPU_X64 OR SDL_CPU_ARM32 OR SDL_CPU_ARM64)) + set(PACKAGE_VERSION "${PACKAGE_VERSION} (X86,X64,ARM32,ARM64)") + set(PACKAGE_VERSION_UNSUITABLE TRUE) +endif() diff --git a/mingw/pkg-support/INSTALL.txt b/build-scripts/pkg-support/mingw/INSTALL.txt similarity index 100% rename from mingw/pkg-support/INSTALL.txt rename to build-scripts/pkg-support/mingw/INSTALL.txt diff --git a/mingw/pkg-support/Makefile b/build-scripts/pkg-support/mingw/Makefile similarity index 100% rename from mingw/pkg-support/Makefile rename to build-scripts/pkg-support/mingw/Makefile diff --git a/mingw/pkg-support/cmake/sdl3-config.cmake b/build-scripts/pkg-support/mingw/cmake/SDL3Config.cmake similarity index 100% rename from mingw/pkg-support/cmake/sdl3-config.cmake rename to build-scripts/pkg-support/mingw/cmake/SDL3Config.cmake diff --git a/mingw/pkg-support/cmake/sdl3-config-version.cmake b/build-scripts/pkg-support/mingw/cmake/SDL3ConfigVersion.cmake similarity index 100% rename from mingw/pkg-support/cmake/sdl3-config-version.cmake rename to build-scripts/pkg-support/mingw/cmake/SDL3ConfigVersion.cmake diff --git a/build-scripts/setup-gdk-desktop.py b/build-scripts/setup-gdk-desktop.py new file mode 100644 index 0000000000000..d2309a0e3119e --- /dev/null +++ b/build-scripts/setup-gdk-desktop.py @@ -0,0 +1,303 @@ +#!/usr/bin/env python + +import argparse +import functools +import logging +import os +from pathlib import Path +import re +import shutil +import subprocess +import tempfile +import textwrap +import urllib.request +import zipfile + +# Update both variables when updating the GDK +GIT_REF = "June_2024_Update_1" +GDK_EDITION = "240601" # YYMMUU + +logger = logging.getLogger(__name__) + +class GdDesktopConfigurator: + def __init__(self, gdk_path, arch, vs_folder, vs_version=None, vs_toolset=None, temp_folder=None, git_ref=None, gdk_edition=None): + self.git_ref = git_ref or GIT_REF + self.gdk_edition = gdk_edition or GDK_EDITION + self.gdk_path = gdk_path + self.temp_folder = temp_folder or Path(tempfile.gettempdir()) + self.dl_archive_path = Path(self.temp_folder) / f"{ self.git_ref }.zip" + self.gdk_extract_path = Path(self.temp_folder) / f"GDK-{ self.git_ref }" + self.arch = arch + self.vs_folder = vs_folder + self._vs_version = vs_version + self._vs_toolset = vs_toolset + + def download_archive(self) -> None: + gdk_url = f"https://github.com/microsoft/GDK/archive/refs/tags/{ GIT_REF }.zip" + logger.info("Downloading %s to %s", gdk_url, self.dl_archive_path) + urllib.request.urlretrieve(gdk_url, self.dl_archive_path) + assert self.dl_archive_path.is_file() + + def extract_zip_archive(self) -> None: + extract_path = self.gdk_extract_path.parent + assert self.dl_archive_path.is_file() + logger.info("Extracting %s to %s", self.dl_archive_path, extract_path) + with zipfile.ZipFile(self.dl_archive_path) as zf: + zf.extractall(extract_path) + assert self.gdk_extract_path.is_dir(), f"{self.gdk_extract_path} must exist" + + def extract_development_kit(self) -> None: + extract_dks_cmd = self.gdk_extract_path / "SetupScripts/ExtractXboxOneDKs.cmd" + assert extract_dks_cmd.is_file() + logger.info("Extracting GDK Development Kit: running %s", extract_dks_cmd) + cmd = ["cmd.exe", "/C", str(extract_dks_cmd), str(self.gdk_extract_path), str(self.gdk_path)] + logger.debug("Running %r", cmd) + subprocess.check_call(cmd) + + def detect_vs_version(self) -> str: + vs_regex = re.compile("VS([0-9]{4})") + supported_vs_versions = [] + for p in self.gaming_grdk_build_path.iterdir(): + if not p.is_dir(): + continue + if m := vs_regex.match(p.name): + supported_vs_versions.append(m.group(1)) + logger.info(f"Supported Visual Studio versions: {supported_vs_versions}") + vs_versions = set(self.vs_folder.parts).intersection(set(supported_vs_versions)) + if not vs_versions: + raise RuntimeError("Visual Studio version is incompatible") + if len(vs_versions) > 1: + raise RuntimeError(f"Too many compatible VS versions found ({vs_versions})") + vs_version = vs_versions.pop() + logger.info(f"Used Visual Studio version: {vs_version}") + return vs_version + + def detect_vs_toolset(self) -> str: + toolset_paths = [] + for ts_path in self.gdk_toolset_parent_path.iterdir(): + if not ts_path.is_dir(): + continue + ms_props = ts_path / "Microsoft.Cpp.props" + if not ms_props.is_file(): + continue + toolset_paths.append(ts_path.name) + logger.info("Detected Visual Studio toolsets: %s", toolset_paths) + assert toolset_paths, "Have we detected at least one toolset?" + + def toolset_number(toolset: str) -> int: + if m:= re.match("[^0-9]*([0-9]+).*", toolset): + return int(m.group(1)) + return -9 + + return max(toolset_paths, key=toolset_number) + + @property + def vs_version(self) -> str: + if self._vs_version is None: + self._vs_version = self.detect_vs_version() + return self._vs_version + + @property + def vs_toolset(self) -> str: + if self._vs_toolset is None: + self._vs_toolset = self.detect_vs_toolset() + return self._vs_toolset + + @staticmethod + def copy_files_and_merge_into(srcdir: Path, dstdir: Path) -> None: + logger.info(f"Copy {srcdir} to {dstdir}") + for root, _, files in os.walk(srcdir): + dest_root = dstdir / Path(root).relative_to(srcdir) + if not dest_root.is_dir(): + dest_root.mkdir() + for file in files: + srcfile = Path(root) / file + dstfile = dest_root / file + shutil.copy(srcfile, dstfile) + + def copy_msbuild(self) -> None: + vc_toolset_parent_path = self.vs_folder / "MSBuild/Microsoft/VC" + if 1: + logger.info(f"Detected compatible Visual Studio version: {self.vs_version}") + srcdir = vc_toolset_parent_path + dstdir = self.gdk_toolset_parent_path + assert srcdir.is_dir(), "Source directory must exist" + assert dstdir.is_dir(), "Destination directory must exist" + + self.copy_files_and_merge_into(srcdir=srcdir, dstdir=dstdir) + + @property + def game_dk_path(self) -> Path: + return self.gdk_path / "Microsoft GDK" + + @property + def game_dk_latest_path(self) -> Path: + return self.game_dk_path / self.gdk_edition + + @property + def windows_sdk_path(self) -> Path: + return self.gdk_path / "Windows Kits/10" + + @property + def gaming_grdk_build_path(self) -> Path: + return self.game_dk_latest_path / "GRDK" + + @property + def gdk_toolset_parent_path(self) -> Path: + return self.gaming_grdk_build_path / f"VS{self.vs_version}/flatDeployment/MSBuild/Microsoft/VC" + + @property + def env(self) -> dict[str, str]: + game_dk = self.game_dk_path + game_dk_latest = self.game_dk_latest_path + windows_sdk_dir = self.windows_sdk_path + gaming_grdk_build = self.gaming_grdk_build_path + + return { + "GRDKEDITION": f"{self.gdk_edition}", + "GameDK": f"{game_dk}\\", + "GameDKLatest": f"{ game_dk_latest }\\", + "WindowsSdkDir": f"{ windows_sdk_dir }\\", + "GamingGRDKBuild": f"{ gaming_grdk_build }\\", + "VSInstallDir": f"{ self.vs_folder }\\", + } + + def create_user_props(self, path: Path) -> None: + vc_targets_path = self.gaming_grdk_build_path / f"VS{ self.vs_version }/flatDeployment/MSBuild/Microsoft/VC/{ self.vs_toolset }" + vc_targets_path16 = self.gaming_grdk_build_path / f"VS2019/flatDeployment/MSBuild/Microsoft/VC/{ self.vs_toolset }" + vc_targets_path17 = self.gaming_grdk_build_path / f"VS2022/flatDeployment/MSBuild/Microsoft/VC/{ self.vs_toolset }" + additional_include_directories = ";".join(str(p) for p in self.gdk_include_paths) + additional_library_directories = ";".join(str(p) for p in self.gdk_library_paths) + durango_xdk_install_path = self.gdk_path / "Microsoft GDK" + with path.open("w") as f: + f.write(textwrap.dedent(f"""\ + + + + { vc_targets_path }\\ + { vc_targets_path16 }\\ + { vc_targets_path17 }\\ + { self.gaming_grdk_build_path }\\ + Gaming.Desktop.x64 + Debug + { self.gdk_edition } + { durango_xdk_install_path } + + $(DurangoXdkInstallPath)\\{self.gdk_edition}\\GRDK\\VS2019\\flatDeployment\\MSBuild\\Microsoft\\VC\\{self.vs_toolset}\\Platforms\\$(Platform)\\ + $(DurangoXdkInstallPath)\\{self.gdk_edition}\\GRDK\\VS2019\\flatDeployment\\MSBuild\\Microsoft\\VC\\{self.vs_toolset}\\Platforms\\$(Platform)\\ + $(DurangoXdkInstallPath)\\{self.gdk_edition}\\GRDK\\VS2022\\flatDeployment\\MSBuild\\Microsoft\\VC\\{self.vs_toolset}\\Platforms\\$(Platform)\\ + $(DurangoXdkInstallPath)\\{self.gdk_edition}\\GRDK\\VS2022\\flatDeployment\\MSBuild\\Microsoft\\VC\\{self.vs_toolset}\\Platforms\\$(Platform)\\ + + true + true + true + + + + { additional_include_directories };%(AdditionalIncludeDirectories) + + + { additional_library_directories };%(AdditionalLibraryDirectories) + + + + """)) + + @property + def gdk_include_paths(self) -> list[Path]: + return [ + self.gaming_grdk_build_path / "gamekit/include", + ] + + @property + def gdk_library_paths(self) -> list[Path]: + return [ + self.gaming_grdk_build_path / f"gamekit/lib/{self.arch}", + ] + + @property + def gdk_binary_path(self) -> list[Path]: + return [ + self.gaming_grdk_build_path / "bin", + self.game_dk_path / "bin", + ] + + @property + def build_env(self) -> dict[str, str]: + gdk_include = ";".join(str(p) for p in self.gdk_include_paths) + gdk_lib = ";".join(str(p) for p in self.gdk_library_paths) + gdk_path = ";".join(str(p) for p in self.gdk_binary_path) + return { + "GDK_INCLUDE": gdk_include, + "GDK_LIB": gdk_lib, + "GDK_PATH": gdk_path, + } + + def print_env(self) -> None: + for k, v in self.env.items(): + print(f"set \"{k}={v}\"") + print() + for k, v in self.build_env.items(): + print(f"set \"{k}={v}\"") + print() + print(f"set \"PATH=%GDK_PATH%;%PATH%\"") + print(f"set \"LIB=%GDK_LIB%;%LIB%\"") + print(f"set \"INCLUDE=%GDK_INCLUDE%;%INCLUDE%\"") + + +def main(): + logging.basicConfig(level=logging.INFO) + parser = argparse.ArgumentParser(allow_abbrev=False) + parser.add_argument("--arch", choices=["amd64"], default="amd64", help="Architecture") + parser.add_argument("--download", action="store_true", help="Download GDK") + parser.add_argument("--extract", action="store_true", help="Extract downloaded GDK") + parser.add_argument("--copy-msbuild", action="store_true", help="Copy MSBuild files") + parser.add_argument("--temp-folder", help="Temporary folder where to download and extract GDK") + parser.add_argument("--gdk-path", required=True, type=Path, help="Folder where to store the GDK") + parser.add_argument("--ref-edition", type=str, help="Git ref and GDK edition separated by comma") + parser.add_argument("--vs-folder", required=True, type=Path, help="Installation folder of Visual Studio") + parser.add_argument("--vs-version", required=False, type=int, help="Visual Studio version") + parser.add_argument("--vs-toolset", required=False, help="Visual Studio toolset (e.g. v150)") + parser.add_argument("--props-folder", required=False, type=Path, default=Path(), help="Visual Studio toolset (e.g. v150)") + parser.add_argument("--no-user-props", required=False, dest="user_props", action="store_false", help="Don't ") + args = parser.parse_args() + + logging.basicConfig(level=logging.INFO) + + git_ref = None + gdk_edition = None + if args.ref_edition is not None: + git_ref, gdk_edition = args.ref_edition.split(",", 1) + try: + int(gdk_edition) + except ValueError: + parser.error("Edition should be an integer (YYMMUU) (Y=year M=month U=update)") + + configurator = GdDesktopConfigurator( + arch=args.arch, + git_ref=git_ref, + gdk_edition=gdk_edition, + vs_folder=args.vs_folder, + vs_version=args.vs_version, + vs_toolset=args.vs_toolset, + gdk_path=args.gdk_path, + temp_folder=args.temp_folder, + ) + + if args.download: + configurator.download_archive() + + if args.extract: + configurator.extract_zip_archive() + + configurator.extract_development_kit() + + if args.copy_msbuild: + configurator.copy_msbuild() + + if args.user_props: + configurator.print_env() + configurator.create_user_props(args.props_folder / "Directory.Build.props") + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/build-scripts/wikiheaders.pl b/build-scripts/wikiheaders.pl index c1ed45e97563d..f3b83df3583d6 100755 --- a/build-scripts/wikiheaders.pl +++ b/build-scripts/wikiheaders.pl @@ -936,7 +936,7 @@ sub sanitize_c_typename { my $paramsstr = undef; - if (!$is_forced_inline && $decl =~ /\A\s*extern\s+(SDL_DEPRECATED\s+|)(SDLMAIN_|SDL_)?DECLSPEC\s+(const\s+|)(unsigned\s+|)(.*?)([\*\s]+)(\*?)\s*SDLCALL\s+(.*?)\s*\((.*?)\);/) { + 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; @@ -1000,15 +1000,21 @@ sub sanitize_c_typename { } if (!$is_forced_inline) { # don't do with forced-inline because we don't want the implementation inserted in the wiki. + my $shrink_length = 0; + $decl = ''; # rebuild this with the line breaks, since it looks better for syntax highlighting. foreach (@decllines) { if ($decl eq '') { + my $temp; + $decl = $_; - $decl =~ s/\Aextern\s+(SDL_DEPRECATED\s+|)(SDLMAIN_|SDL_)?DECLSPEC\s+(.*?)\s+(\*?)SDLCALL\s+/$3$4 /; + $temp = $decl; + $temp =~ s/\Aextern\s+(SDL_DEPRECATED\s+|)(SDLMAIN_|SDL_)?DECLSPEC\w*\s+(.*?)\s+(\*?)SDLCALL\s+/$3$4 /; + $shrink_length = length($decl) - length($temp); + $decl = $temp; } else { my $trimmed = $_; - # !!! FIXME: trim space for SDL_DEPRECATED if it was used, too. - $trimmed =~ s/\A\s{28}//; # 28 for shrinking to match the removed "extern SDL_DECLSPEC SDLCALL " + $trimmed =~ s/\A\s{$shrink_length}//; # shrink to match the removed "extern SDL_DECLSPEC SDLCALL " $decl .= $trimmed; } $decl .= "\n"; diff --git a/cmake/PreseedMSVCCache.cmake b/cmake/PreseedMSVCCache.cmake index 370dcadd33395..d0b5ebfe4b21d 100644 --- a/cmake/PreseedMSVCCache.cmake +++ b/cmake/PreseedMSVCCache.cmake @@ -1,10 +1,11 @@ if(MSVC) function(SDL_Preseed_CMakeCache) + set(COMPILER_SUPPORTS_W3 "1" CACHE INTERNAL "Test /W3") set(COMPILER_SUPPORTS_FDIAGNOSTICS_COLOR_ALWAYS "" CACHE INTERNAL "Test COMPILER_SUPPORTS_FDIAGNOSTICS_COLOR_ALWAYS") set(HAVE_ALLOCA_H "" CACHE INTERNAL "Have include alloca.h") set(HAVE_AUDIOCLIENT_H "1" CACHE INTERNAL "Have include audioclient.h") set(HAVE_D3D11_H "1" CACHE INTERNAL "Have include d3d11_1.h") - set(HAVE_D3D_H "1" CACHE INTERNAL "Have include d3d9.h") + set(HAVE_D3D9_H "1" CACHE INTERNAL "Have include d3d9.h") set(HAVE_DDRAW_H "1" CACHE INTERNAL "Have include ddraw.h") set(HAVE_DINPUT_H "1" CACHE INTERNAL "Have include dinput.h") set(HAVE_DSOUND_H "1" CACHE INTERNAL "Have include dsound.h") @@ -170,11 +171,9 @@ if(MSVC) endif() if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL "19.1") - set(HAVE_D3D12_H "1" CACHE INTERNAL "Test HAVE_D3D12_H") set(HAVE_ROAPI_H "1" CACHE INTERNAL "Have include roapi.h") set(HAVE_WINDOWS_GAMING_INPUT_H "1" CACHE INTERNAL "Test HAVE_WINDOWS_GAMING_INPUT_H") else() - set(HAVE_D3D12_H "" CACHE INTERNAL "Test HAVE_D3D12_H") set(HAVE_ROAPI_H "" CACHE INTERNAL "Have include roapi.h") set(HAVE_WINDOWS_GAMING_INPUT_H "" CACHE INTERNAL "Test HAVE_WINDOWS_GAMING_INPUT_H") endif() diff --git a/cmake/macros.cmake b/cmake/macros.cmake index aab621d0d9f49..0392a433804fa 100644 --- a/cmake/macros.cmake +++ b/cmake/macros.cmake @@ -394,7 +394,7 @@ function(SDL_PrintSummary) message(STATUS "") endif() - if(UNIX AND NOT (ANDROID OR APPLE)) + if(UNIX AND NOT (ANDROID OR APPLE OR EMSCRIPTEN)) if(NOT (HAVE_X11 OR HAVE_WAYLAND)) message(STATUS "SDL is being built without a X11 or wayland video driver.") message(STATUS "The library will not be able to create windows on most unix environments.") diff --git a/cmake/sdlcompilers.cmake b/cmake/sdlcompilers.cmake index 9db746021b253..fc352e19c86e1 100644 --- a/cmake/sdlcompilers.cmake +++ b/cmake/sdlcompilers.cmake @@ -30,6 +30,14 @@ function(SDL_AddCommonCompilerFlags TARGET) option(SDL_WERROR "Enable -Werror" OFF) get_property(TARGET_TYPE TARGET "${TARGET}" PROPERTY TYPE) + if(MSVC) + cmake_push_check_state() + check_c_compiler_flag("/W3" COMPILER_SUPPORTS_W3) + if(COMPILER_SUPPORTS_W3) + target_compile_options(${TARGET} PRIVATE "/W3") + endif() + cmake_pop_check_state() + endif() if(USE_GCC OR USE_CLANG OR USE_INTELCC OR USE_QCC) if(MINGW) diff --git a/cmake/sdlcpu.cmake b/cmake/sdlcpu.cmake index 69142bc8322a8..c077fc075f2b6 100644 --- a/cmake/sdlcpu.cmake +++ b/cmake/sdlcpu.cmake @@ -79,6 +79,8 @@ ${src_main} include(CMakePushCheckState) + set(CMAKE_TRY_COMPILE_TARGET_TYPE "STATIC_LIBRARY") + cmake_push_check_state(RESET) try_compile(SDL_CPU_CHECK_ALL "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/CMakeTmp/SDL_detect_arch" diff --git a/cmake/sdltargets.cmake b/cmake/sdltargets.cmake index e4a870db5e8ae..d11061c9cb69b 100644 --- a/cmake/sdltargets.cmake +++ b/cmake/sdltargets.cmake @@ -28,7 +28,7 @@ function(sdl_sources) set_property(TARGET SDL3-collector APPEND PROPERTY INTERFACE_SOURCES ${ARGS_SHARED} ${ARGS_STATIC} ${ARGS_UNPARSED_ARGUMENTS}) endfunction() -# Use sdl_generic_link_dependency to describe a private dependency of SDL3. All options are optional. +# Use sdl_generic_link_dependency to describe a private dependency. All options are optional. # Users should use sdl_link_dependency and sdl_test_link_dependency instead # - SHARED_TARGETS: shared targets to add this dependency to # - STATIC_TARGETS: static targets to add this dependency to @@ -36,12 +36,13 @@ endfunction() # - INCLUDES: the include directories of the dependency # - PKG_CONFIG_PREFIX: name of the prefix, when using the functions of FindPkgConfig # - PKG_CONFIG_SPECS: pkg-config spec, used as argument for the functions of FindPkgConfig -# - PKG_CONFIG_LIBS: libs that will only end up in the Libs.private of sdl3.pc +# - PKG_CONFIG_LIBS: libs that will only end up in the Libs.private of the .pc file +# - PKG_CONFIG_LINK_OPTIONS: ldflags that will only end up in the Libs.private of sdl3.pc # - CMAKE_MODULE: CMake module name of the dependency, used as argument of find_package -# - LIBS: list of libraries to link to -# - LINK_OPTIONS: list of link options +# - LIBS: list of libraries to link to (cmake and pkg-config) +# - LINK_OPTIONS: list of link options (also used in pc file, unless PKG_CONFIG_LINK_OPTION is used) function(sdl_generic_link_dependency ID) - cmake_parse_arguments(ARGS "" "COLLECTOR" "SHARED_TARGETS;STATIC_TARGETS;INCLUDES;PKG_CONFIG_LIBS;PKG_CONFIG_PREFIX;PKG_CONFIG_SPECS;CMAKE_MODULE;LIBS;LINK_OPTIONS" ${ARGN}) + cmake_parse_arguments(ARGS "" "COLLECTOR" "SHARED_TARGETS;STATIC_TARGETS;INCLUDES;PKG_CONFIG_LINK_OPTIONS;PKG_CONFIG_LIBS;PKG_CONFIG_PREFIX;PKG_CONFIG_SPECS;CMAKE_MODULE;LIBS;LINK_OPTIONS" ${ARGN}) foreach(target IN LISTS ARGS_SHARED_TARGETS) if(TARGET ${target}) target_include_directories(${target} SYSTEM PRIVATE ${ARGS_INCLUDES}) @@ -63,6 +64,7 @@ function(sdl_generic_link_dependency ID) set_property(TARGET ${ARGS_COLLECTOR} APPEND PROPERTY INTERFACE_SDL_DEP_${ID}_PKG_CONFIG_PREFIX ${ARGS_PKG_CONFIG_PREFIX}) set_property(TARGET ${ARGS_COLLECTOR} APPEND PROPERTY INTERFACE_SDL_DEP_${ID}_PKG_CONFIG_SPECS ${ARGS_PKG_CONFIG_SPECS}) set_property(TARGET ${ARGS_COLLECTOR} APPEND PROPERTY INTERFACE_SDL_DEP_${ID}_PKG_CONFIG_LIBS ${ARGS_PKG_CONFIG_LIBS}) + set_property(TARGET ${ARGS_COLLECTOR} APPEND PROPERTY INTERFACE_SDL_DEP_${ID}_PKG_CONFIG_LINK_OPTIONS ${ARGS_PKG_CONFIG_LINK_OPTIONS}) set_property(TARGET ${ARGS_COLLECTOR} APPEND PROPERTY INTERFACE_SDL_DEP_${ID}_LIBS ${ARGS_LIBS}) set_property(TARGET ${ARGS_COLLECTOR} APPEND PROPERTY INTERFACE_SDL_DEP_${ID}_LINK_OPTIONS ${ARGS_LINK_OPTIONS}) set_property(TARGET ${ARGS_COLLECTOR} APPEND PROPERTY INTERFACE_SDL_DEP_${ID}_CMAKE_MODULE ${ARGS_CMAKE_MODULE}) @@ -294,14 +296,19 @@ function(configure_sdl3_pc) get_property(CMAKE_MODULE TARGET SDL3-collector PROPERTY INTERFACE_SDL_DEP_${ID}_CMAKE_MODULE) get_property(PKG_CONFIG_SPECS TARGET SDL3-collector PROPERTY INTERFACE_SDL_DEP_${ID}_PKG_CONFIG_SPECS) get_property(PKG_CONFIG_LIBS TARGET SDL3-collector PROPERTY INTERFACE_SDL_DEP_${ID}_PKG_CONFIG_LIBS) + get_property(PKG_CONFIG_LDFLAGS TARGET SDL3-collector PROPERTY INTERFACE_SDL_DEP_${ID}_PKG_CONFIG_LINK_OPTIONS) get_property(LIBS TARGET SDL3-collector PROPERTY INTERFACE_SDL_DEP_${ID}_LIBS) get_property(LINK_OPTIONS TARGET SDL3-collector PROPERTY INTERFACE_SDL_DEP_${ID}_LINK_OPTIONS) list(APPEND private_requires ${PKG_CONFIG_SPECS}) list(APPEND private_libs ${PKG_CONFIG_LIBS}) - if(NOT PKG_CONFIG_SPECS AND NOT CMAKE_MODULE) - list(APPEND private_libs ${LIBS}) + if(PKG_CONFIG_SPECS OR PKG_CONFIG_LIBS OR PKG_CONFIG_LDFLAGS) + list(APPEND private_ldflags ${PKG_CONFIG_LDFLAGS}) + else() list(APPEND private_ldflags ${LINK_OPTIONS}) + if(NOT CMAKE_MODULE) + list(APPEND private_libs ${LIBS}) + endif() endif() endforeach() diff --git a/cmake/test/CMakeLists.txt b/cmake/test/CMakeLists.txt index 066a1ee4a3e4e..11754a828ef4e 100644 --- a/cmake/test/CMakeLists.txt +++ b/cmake/test/CMakeLists.txt @@ -3,6 +3,19 @@ cmake_minimum_required(VERSION 3.12) project(sdl_test LANGUAGES C) +include(CheckLanguage) + +if(APPLE) + # multiple values for CMAKE_OSX_ARCHITECTURES not supported with Swift + list(LENGTH CMAKE_OSX_ARCHITECTURES count_osx_archs) + if(count_osx_archs LESS_EQUAL 1) + check_language(Swift) + if(CMAKE_Swift_COMPILER) + enable_language(Swift) + endif() + endif() +endif() + message(STATUS "CMAKE_SYSTEM_NAME= ${CMAKE_SYSTEM_NAME}") message(STATUS "CMAKE_SYSTEM_PROCESSOR= ${CMAKE_SYSTEM_PROCESSOR}") @@ -74,6 +87,12 @@ if(TEST_SHARED) add_executable(sdltest-shared sdltest.c) target_link_libraries(sdltest-shared PRIVATE SDL3::SDL3_test SDL3::SDL3-shared) endif() + + if(CMAKE_Swift_COMPILER) + add_executable(swift-shared main.swift) + target_include_directories(swift-shared PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/swift") + target_link_libraries(swift-shared PRIVATE SDL3::SDL3-shared) + endif() endif() if(TEST_STATIC) @@ -92,6 +111,12 @@ if(TEST_STATIC) add_executable(sdltest-static sdltest.c) target_link_libraries(sdltest-static PRIVATE SDL3::SDL3_test SDL3::SDL3-static) endif() + + if(CMAKE_Swift_COMPILER) + add_executable(swift-static main.swift) + target_include_directories(swift-static PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/swift") + target_link_libraries(swift-static PRIVATE SDL3::SDL3-static) + endif() endif() find_package(SDL3 REQUIRED CONFIG COMPONENTS SDL3) diff --git a/cmake/test/main.swift b/cmake/test/main.swift new file mode 100644 index 0000000000000..6c5d8b8318cbd --- /dev/null +++ b/cmake/test/main.swift @@ -0,0 +1,13 @@ +/* Contributed by Piotr Usewicz (https://github.com/pusewicz) */ + +import SDL3 + +guard SDL_Init(Uint32(SDL_INIT_VIDEO)) == 0 else { + fatalError("SDL_Init error: \(String(cString: SDL_GetError()))") +} + +var sdlVersion = SDL_GetVersion() + +print("SDL version: \(sdlVersion)") + +SDL_Quit() diff --git a/cmake/test/swift/module.modulemap b/cmake/test/swift/module.modulemap new file mode 100644 index 0000000000000..bbc26a995dcfd --- /dev/null +++ b/cmake/test/swift/module.modulemap @@ -0,0 +1,4 @@ +module SDL3 [extern_c] { + header "shim.h" + export * +} diff --git a/cmake/test/swift/shim.h b/cmake/test/swift/shim.h new file mode 100644 index 0000000000000..dba8c6fd4c251 --- /dev/null +++ b/cmake/test/swift/shim.h @@ -0,0 +1,3 @@ +/* Contributed by Piotr Usewicz (https://github.com/pusewicz) */ + +#include diff --git a/docs/README-android.md b/docs/README-android.md index 754d2edfae2ad..e6065715b9e94 100644 --- a/docs/README-android.md +++ b/docs/README-android.md @@ -57,15 +57,16 @@ run: One limitation of this script is that all sources provided will be aggregated into a single directory, thus all your source files should have a unique name. -Once the project is complete the script will tell you where the debug APK is located. +Once the project is complete the script will tell you how to build the project. If you want to create a signed release APK, you can use the project created by this utility to generate it. +Running the script with `--help` will list all available options, and their purposes. + Finally, a word of caution: re running create-android-project.py wipes any changes you may have done in the build directory for the app! - For more complex projects, follow these instructions: 1. Get the source code for SDL and copy the 'android-project' directory located at SDL/android-project to a suitable location. Also make sure to rename it to your project name (In these examples: YOURPROJECT). @@ -122,6 +123,48 @@ Here's an explanation of the files in the Android project, so you can customize src/main/java/org/libsdl/app/SDLActivity.java - the Java class handling the initialization and binding to SDL. Be very careful changing this, as the SDL library relies on this implementation. You should instead subclass this for your application. +Using the SDL3 Android Archive (.aar) +================================================================================ + +The `create-android-project.py` script can +./create-android-project.py com.yourcompany.yourapp < sources.list + +The Android archive allows use of SDL3 in your Android project, without needing to copy any SDL c or java source. +For integration with CMake/ndk-build, it uses [prefab](https://google.github.io/prefab/). + +Copy the archive to a `app/libs` directory of your project and add the following to `app/gradle.build`: +``` +android { + /* ... */ + buildFeatures { + prefab true + } +} +dependencies { + implementation files('libs/@PROJECT_NAME@-@PROJECT_VERSION@.aar') + /* ... */ +} +``` + +If you're using CMake, add the following to your CMakeLists.txt: +``` +find_package(@PROJECT_NAME@ REQUIRED CONFIG) +target_link_libraries(yourgame PRIVATE @PROJECT_NAME@::@PROJECT_NAME@) +``` + +If you're using ndk-build, add the following somewhere after `LOCAL_MODULE := yourgame` to your `Android.mk` or `Application.mk`: +``` +# https://google.github.io/prefab/build-systems.html + +# Add the prefab modules to the import path. +$(call import-add-path,/out) + +# Import @PROJECT_NAME@ so we can depend on it. +$(call import-module,prefab/@PROJECT_NAME@) +``` + +If you want to avoid adding the complete SDL source base as a subproject, or adding the Java sources of the bindings to your Android project + Customizing your application name ================================================================================ diff --git a/docs/README-macos.md b/docs/README-macos.md index e8d21d61e52d1..3e4708de3c0e9 100644 --- a/docs/README-macos.md +++ b/docs/README-macos.md @@ -30,7 +30,7 @@ cmake --build . sudo cmake --install . ``` -Please note that building SDL requires at least Xcode 6 and the 10.9 SDK. +Please note that building SDL requires at least Xcode 12.2 and the 11.0 SDK. PowerPC support for macOS has been officially dropped as of SDL 2.0.2. 32-bit Intel and macOS 10.8 runtime support has been officially dropped as of SDL 2.24.0. diff --git a/docs/README-migration.md b/docs/README-migration.md index 879c2df00e484..aebcd7ecb7d04 100644 --- a/docs/README-migration.md +++ b/docs/README-migration.md @@ -158,9 +158,9 @@ Rather than iterating over audio devices using a device index, there are new fun if (devices) { for (i = 0; i < num_devices; ++i) { SDL_AudioDeviceID instance_id = devices[i]; - const char *name = SDL_GetAudioDeviceName(instance_id); - SDL_Log("AudioDevice %" SDL_PRIu32 ": %s\n", instance_id, name); + SDL_Log("AudioDevice %" SDL_PRIu32 ": %s\n", instance_id, SDL_GetAudioDeviceName(instance_id)); } + SDL_free(devices); } SDL_QuitSubSystem(SDL_INIT_AUDIO); } @@ -298,10 +298,6 @@ The following symbols have been renamed: The following symbols have been removed: * SDL_MIX_MAXVOLUME - mixer volume is now a float between 0.0 and 1.0 -## SDL_clipboard.h - -SDL_GetClipboardText() and SDL_GetPrimarySelectionText() return a const pointer to temporary memory, which does not need to be freed. You can use SDL_ClaimTemporaryMemory() to convert it to a non-const pointer that should be freed when you're done with it. - ## SDL_cpuinfo.h The intrinsics headers (mmintrin.h, etc.) have been moved to `` and are no longer automatically included in SDL.h. @@ -383,6 +379,8 @@ SDL_AddEventWatch() now returns -1 if it fails because it ran out of memory and SDL_RegisterEvents() now returns 0 if it couldn't allocate any user events. +SDL_EventFilter functions now return SDL_bool. + The following symbols have been renamed: * SDL_APP_DIDENTERBACKGROUND => SDL_EVENT_DID_ENTER_BACKGROUND * SDL_APP_DIDENTERFOREGROUND => SDL_EVENT_DID_ENTER_FOREGROUND @@ -458,10 +456,6 @@ The following functions have been removed: The following enums have been renamed: * SDL_eventaction => SDL_EventAction -## SDL_filesystem.h - -SDL_GetBasePath() and SDL_GetPrefPath() return a const pointer to temporary memory, which does not need to be freed. You can use SDL_ClaimTemporaryMemory() to convert it to a non-const pointer that should be freed when you're done with it. - ## SDL_gamecontroller.h SDL_gamecontroller.h has been renamed SDL_gamepad.h, and all APIs have been renamed to match. @@ -710,15 +704,13 @@ Rather than iterating over haptic devices using device index, there is a new fun { if (SDL_InitSubSystem(SDL_INIT_HAPTIC) == 0) { int i, num_haptics; - const SDL_HapticID *haptics = SDL_GetHaptics(&num_haptics); + SDL_HapticID *haptics = SDL_GetHaptics(&num_haptics); if (haptics) { for (i = 0; i < num_haptics; ++i) { SDL_HapticID instance_id = haptics[i]; - const char *name = SDL_GetHapticNameForID(instance_id); - - SDL_Log("Haptic %" SDL_PRIu32 ": %s\n", - instance_id, name ? name : "Unknown"); + SDL_Log("Haptic %" SDL_PRIu32 ": %s\n", instance_id, SDL_GetHapticNameForID(instance_id)); } + SDL_free(haptics); } SDL_QuitSubSystem(SDL_INIT_HAPTIC); } @@ -765,6 +757,27 @@ SDL_AddHintCallback() now returns a standard int result instead of void, returni Calling SDL_GetHint() with the name of the hint being changed from within a hint callback will now return the new value rather than the old value. The old value is still passed as a parameter to the hint callback. +SDL_SetHint, SDL_SetHintWithPriority, and SDL_ResetHint now return int (-1 on error, 0 on success) instead of SDL_bool (SDL_FALSE on error, SDL_TRUE on success). + +The environment variables SDL_VIDEODRIVER and SDL_AUDIODRIVER have been renamed to SDL_VIDEO_DRIVER and SDL_AUDIO_DRIVER. + +The environment variables SDL_VIDEO_X11_WMCLASS and SDL_VIDEO_WAYLAND_WMCLASS have been removed and replaced by either using the appindentifier param to SDL_SetAppMetadata() or setting SDL_PROP_APP_METADATA_IDENTIFIER_STRING with SDL_SetAppMetadataProperty() + +The environment variable AUDIODEV is used exclusively to specify the audio device for the OSS and NetBSD audio drivers. Its use in the ALSA driver has been replaced with the hint SDL_HINT_AUDIO_ALSA_DEFAULT_DEVICE and in the sndio driver with the environment variable AUDIODEVICE. + +The following hints have been renamed: +* SDL_HINT_VIDEODRIVER => SDL_HINT_VIDEO_DRIVER +* SDL_HINT_AUDIODRIVER => SDL_HINT_AUDIO_DRIVER +* SDL_HINT_ALLOW_TOPMOST => SDL_HINT_WINDOW_ALLOW_TOPMOST +* SDL_HINT_DIRECTINPUT_ENABLED => SDL_HINT_JOYSTICK_DIRECTINPUT +* SDL_HINT_GDK_TEXTINPUT_DEFAULT => SDL_HINT_GDK_TEXTINPUT_DEFAULT_TEXT +* SDL_HINT_JOYSTICK_GAMECUBE_RUMBLE_BRAKE => SDL_HINT_JOYSTICK_HIDAPI_GAMECUBE_RUMBLE_BRAKE +* SDL_HINT_LINUX_DIGITAL_HATS => SDL_HINT_JOYSTICK_LINUX_DIGITAL_HATS +* SDL_HINT_LINUX_HAT_DEADZONES => SDL_HINT_JOYSTICK_LINUX_HAT_DEADZONES +* SDL_HINT_LINUX_JOYSTICK_CLASSIC => SDL_HINT_JOYSTICK_LINUX_CLASSIC +* SDL_HINT_LINUX_JOYSTICK_DEADZONES => SDL_HINT_JOYSTICK_LINUX_DEADZONES +* SDL_HINT_VIDEO_WAYLAND_EMULATE_MOUSE_WARP => SDL_HINT_MOUSE_EMULATE_WARP_WITH_RELATIVE + The following hints have been removed: * SDL_HINT_ACCELEROMETER_AS_JOYSTICK * SDL_HINT_ANDROID_BLOCK_ON_PAUSE_PAUSEAUDIO - the audio will be paused when the application is paused, and SDL_HINT_ANDROID_BLOCK_ON_PAUSE can be used to control that @@ -790,23 +803,33 @@ The following hints have been removed: * SDL_HINT_VIDEO_X11_XINERAMA - Xinerama no longer supported by the X11 backend * SDL_HINT_VIDEO_X11_XVIDMODE - Xvidmode no longer supported by the X11 backend * SDL_HINT_WINDOWS_DISABLE_THREAD_NAMING - SDL now properly handles the 0x406D1388 Exception if no debugger intercepts it, preventing its propagation. +* SDL_HINT_WINDOWS_FORCE_MUTEX_CRITICAL_SECTIONS - Slim Reader/Writer Locks are always used if available * SDL_HINT_WINDOWS_NO_CLOSE_ON_ALT_F4 - replaced with SDL_HINT_WINDOWS_CLOSE_ON_ALT_F4, defaulting to SDL_TRUE * SDL_HINT_XINPUT_USE_OLD_JOYSTICK_MAPPING - -* Renamed hints SDL_HINT_VIDEODRIVER and SDL_HINT_AUDIODRIVER to SDL_HINT_VIDEO_DRIVER and SDL_HINT_AUDIO_DRIVER -* Renamed environment variables SDL_VIDEODRIVER and SDL_AUDIODRIVER to SDL_VIDEO_DRIVER and SDL_AUDIO_DRIVER -* The environment variables SDL_VIDEO_X11_WMCLASS and SDL_VIDEO_WAYLAND_WMCLASS have been removed and replaced with the unified hint SDL_HINT_APP_ID - -The following hints have been renamed: -* SDL_HINT_ALLOW_TOPMOST => SDL_HINT_WINDOW_ALLOW_TOPMOST -* SDL_HINT_DIRECTINPUT_ENABLED => SDL_HINT_JOYSTICK_DIRECTINPUT -* SDL_HINT_GDK_TEXTINPUT_DEFAULT => SDL_HINT_GDK_TEXTINPUT_DEFAULT_TEXT -* SDL_HINT_JOYSTICK_GAMECUBE_RUMBLE_BRAKE => SDL_HINT_JOYSTICK_HIDAPI_GAMECUBE_RUMBLE_BRAKE -* SDL_HINT_LINUX_DIGITAL_HATS => SDL_HINT_JOYSTICK_LINUX_DIGITAL_HATS -* SDL_HINT_LINUX_HAT_DEADZONES => SDL_HINT_JOYSTICK_LINUX_HAT_DEADZONES -* SDL_HINT_LINUX_JOYSTICK_CLASSIC => SDL_HINT_JOYSTICK_LINUX_CLASSIC -* SDL_HINT_LINUX_JOYSTICK_DEADZONES => SDL_HINT_JOYSTICK_LINUX_DEADZONES -* SDL_HINT_VIDEO_WAYLAND_EMULATE_MOUSE_WARP => SDL_HINT_MOUSE_EMULATE_WARP_WITH_RELATIVE +* SDL_HINT_AUDIO_DEVICE_APP_NAME - replaced by either using the appname param to SDL_SetAppMetadata() or setting SDL_PROP_APP_METADATA_NAME_STRING with SDL_SetAppMetadataProperty() + +The following environment variables have been renamed: +* SDL_AUDIODRIVER => SDL_AUDIO_DRIVER +* SDL_PATH_DSP => AUDIODEV +* SDL_VIDEODRIVER => SDL_VIDEO_DRIVER + +The following environment variables have been removed: +* SDL_AUDIO_ALSA_DEBUG - replaced by setting the hint SDL_HINT_LOGGING to "audio=debug" +* SDL_DISKAUDIODELAY - replaced with the hint SDL_HINT_AUDIO_DISK_TIMESCALE which allows scaling the audio time rather than specifying an absolute delay. +* SDL_DISKAUDIOFILE - replaced with the hint SDL_HINT_AUDIO_DISK_OUTPUT_FILE +* SDL_DISKAUDIOFILEIN - replaced with the hint SDL_HINT_AUDIO_DISK_INPUT_FILE +* SDL_DUMMYAUDIODELAY - replaced with the hint SDL_HINT_AUDIO_DUMMY_TIMESCALE which allows scaling the audio time rather than specifying an absolute delay. +* SDL_HAPTIC_GAIN_MAX +* SDL_HIDAPI_DISABLE_LIBUSB - replaced with the hint SDL_HINT_HIDAPI_LIBUSB +* SDL_HIDAPI_JOYSTICK_DISABLE_UDEV - replaced with the hint SDL_HINT_HIDAPI_UDEV +* SDL_INPUT_FREEBSD_KEEP_KBD - replaced with the hint SDL_HINT_MUTE_CONSOLE_KEYBOARD +* SDL_INPUT_LINUX_KEEP_KBD - replaced with the hint SDL_HINT_MUTE_CONSOLE_KEYBOARD +* VITA_DISABLE_TOUCH_BACK - replaced with the hint SDL_HINT_VITA_ENABLE_BACK_TOUCH +* VITA_DISABLE_TOUCH_FRONT - replaced with the hint SDL_HINT_VITA_ENABLE_FRONT_TOUCH +* VITA_MODULE_PATH - replaced with the hint SDL_HINT_VITA_MODULE_PATH +* VITA_PVR_OGL - replaced with the hint SDL_HINT_VITA_PVR_OPENGL +* VITA_PVR_SKIP_INIT - replaced with the hint SDL_HINT_VITA_PVR_INIT +* VITA_RESOLUTION - replaced with the hint SDL_HINT_VITA_RESOLUTION The following functions have been removed: * SDL_ClearHints() - replaced with SDL_ResetHints() @@ -840,7 +863,7 @@ Rather than iterating over joysticks using device index, there is a new function { if (SDL_InitSubSystem(SDL_INIT_JOYSTICK) == 0) { int i, num_joysticks; - const SDL_JoystickID *joysticks = SDL_GetJoysticks(&num_joysticks); + SDL_JoystickID *joysticks = SDL_GetJoysticks(&num_joysticks); if (joysticks) { for (i = 0; i < num_joysticks; ++i) { SDL_JoystickID instance_id = joysticks[i]; @@ -850,6 +873,7 @@ Rather than iterating over joysticks using device index, there is a new function SDL_Log("Joystick %" SDL_PRIu32 ": %s%s%s VID 0x%.4x, PID 0x%.4x\n", instance_id, name ? name : "Unknown", path ? ", " : "", path ? path : "", SDL_GetJoystickVendorForID(instance_id), SDL_GetJoystickProductForID(instance_id)); } + SDL_free(joysticks); } SDL_QuitSubSystem(SDL_INIT_JOYSTICK); } @@ -1034,12 +1058,10 @@ The following symbols have been renamed: SDL_LoadFunction() now returns `SDL_FunctionPointer` instead of `void *`, and should be cast to the appropriate function type. You can define SDL_FUNCTION_POINTER_IS_VOID_POINTER in your project to restore the previous behavior. -## SDL_locale.h - -SDL_GetPreferredLocales() returns a const array of locale pointers, which does not need to be freed. You can use SDL_ClaimTemporaryMemory() to convert it to a non-const pointer that should be freed when you're done with it. - ## SDL_log.h +SDL_Log() no longer prints a log prefix by default for SDL_LOG_PRIORITY_INFO and below. The log prefixes can be customized with SDL_SetLogPriorityPrefix(). + The following macros have been removed: * SDL_MAX_LOG_MESSAGE - there's no message length limit anymore. If you need an artificial limit, this used to be 4096 in SDL versions before 2.0.24. @@ -1086,6 +1108,10 @@ SDL_SystemCursor's items from SDL2 have been renamed to match CSS cursor names. The following functions have been renamed: * SDL_FreeCursor() => SDL_DestroyCursor() +The following functions have been removed: +* SDL_SetRelativeMouseMode() - replaced with SDL_SetWindowRelativeMouseMode() +* SDL_GetRelativeMouseMode() - replaced with SDL_GetWindowRelativeMouseMode() + The following symbols have been renamed: * SDL_SYSTEM_CURSOR_ARROW => SDL_SYSTEM_CURSOR_DEFAULT * SDL_SYSTEM_CURSOR_HAND => SDL_SYSTEM_CURSOR_POINTER @@ -1522,6 +1548,8 @@ SDL_IOStream *SDL_RWFromFP(FILE *fp, SDL_bool autoclose) The internal `FILE *` is available through a standard SDL_IOStream property, for streams made through SDL_IOFromFile() that use stdio behind the scenes; apps use this pointer at their own risk and should make sure that SDL and the app are using the same C runtime. +On Apple platforms, SDL_RWFromFile (now called SDL_IOFromFile) no longer tries to read from inside the app bundle's resource directory, instead now using the specified path unchanged. One can use SDL_GetBasePath() to find the resource directory on these platforms. + The functions SDL_ReadU8(), SDL_ReadU16LE(), SDL_ReadU16BE(), SDL_ReadU32LE(), SDL_ReadU32BE(), SDL_ReadU64LE(), and SDL_ReadU64BE() now return SDL_TRUE if the read succeeded and SDL_FALSE if it didn't, and store the data in a pointer passed in as a parameter. @@ -1588,7 +1616,7 @@ Rather than iterating over sensors using device index, there is a new function S { if (SDL_InitSubSystem(SDL_INIT_SENSOR) == 0) { int i, num_sensors; - const SDL_SensorID *sensors = SDL_GetSensors(&num_sensors); + SDL_SensorID *sensors = SDL_GetSensors(&num_sensors); if (sensors) { for (i = 0; i < num_sensors; ++i) { SDL_Log("Sensor %" SDL_PRIu32 ": %s, type %d, platform type %d\n", @@ -1597,6 +1625,7 @@ Rather than iterating over sensors using device index, there is a new function S SDL_GetSensorTypeForID(sensors[i]), SDL_GetSensorNonPortableTypeForID(sensors[i])); } + SDL_free(sensors); } SDL_QuitSubSystem(SDL_INIT_SENSOR); } @@ -1989,14 +2018,14 @@ The following symbols have been renamed: Several video backends have had their names lower-cased ("kmsdrm", "rpi", "android", "psp", "ps2", "vita"). SDL already does a case-insensitive compare for SDL_HINT_VIDEO_DRIVER tests, but if your app is calling SDL_GetVideoDriver() or SDL_GetCurrentVideoDriver() and doing case-sensitive compares on those strings, please update your code. -SDL_VideoInit() and SDL_VideoQuit() have been removed. Instead you can call SDL_InitSubSystem() and SDL_QuitSubSystem() with SDL_INIT_VIDEO, which will properly refcount the subsystems. You can choose a specific video driver using SDL_VIDEO_DRIVER hint. +SDL_VideoInit() and SDL_VideoQuit() have been removed. Instead you can call SDL_InitSubSystem() and SDL_QuitSubSystem() with SDL_INIT_VIDEO, which will properly refcount the subsystems. You can choose a specific video driver using SDL_HINT_VIDEO_DRIVER. Rather than iterating over displays using display index, there is a new function SDL_GetDisplays() to get the current list of displays, and functions which used to take a display index now take SDL_DisplayID, with an invalid ID being 0. ```c { if (SDL_InitSubSystem(SDL_INIT_VIDEO) == 0) { int i, num_displays = 0; - const SDL_DisplayID *displays = SDL_GetDisplays(&num_displays); + SDL_DisplayID *displays = SDL_GetDisplays(&num_displays); if (displays) { for (i = 0; i < num_displays; ++i) { SDL_DisplayID instance_id = displays[i]; @@ -2004,6 +2033,7 @@ Rather than iterating over displays using display index, there is a new function SDL_Log("Display %" SDL_PRIu32 ": %s\n", instance_id, name ? name : "Unknown"); } + SDL_free(displays); } SDL_QuitSubSystem(SDL_INIT_VIDEO); } @@ -2041,13 +2071,14 @@ Rather than iterating over display modes using an index, there is a new function { SDL_DisplayID display = SDL_GetPrimaryDisplay(); int num_modes = 0; - const SDL_DisplayMode * const *modes = SDL_GetFullscreenDisplayModes(display, &num_modes); + SDL_DisplayMode **modes = SDL_GetFullscreenDisplayModes(display, &num_modes); if (modes) { for (i = 0; i < num_modes; ++i) { SDL_DisplayMode *mode = modes[i]; SDL_Log("Display %" SDL_PRIu32 " mode %d: %dx%d@%gx %gHz\n", display, i, mode->w, mode->h, mode->pixel_density, mode->refresh_rate); } + SDL_free(modes); } } ``` @@ -2080,8 +2111,6 @@ SDL_WindowFlags is used instead of Uint32 for API functions that refer to window SDL_GetWindowOpacity() directly returns the opacity instead of using an out parameter. -SDL_GetWindowICCProfile() returns a const pointer to temporary memory, which does not need to be freed. You can use SDL_ClaimTemporaryMemory() to convert it to a non-const pointer that should be freed when you're done with it. - The following functions have been renamed: * SDL_GL_DeleteContext() => SDL_GL_DestroyContext() * SDL_GetClosestDisplayMode() => SDL_GetClosestFullscreenDisplayMode() diff --git a/docs/README-strings.md b/docs/README-strings.md index 58d94f9270af4..ea86b8d5939f2 100644 --- a/docs/README-strings.md +++ b/docs/README-strings.md @@ -5,55 +5,3 @@ Unless otherwise specified, all strings in SDL, across all platforms, are UTF-8 encoded and can represent the full range of [Unicode](https://unicode.org). - -## The SDL Get String Rule. - -Did you see 'SDL_GetStringRule' in the wiki or headers? Here are the details -that aren't worth copying across dozens of functions' documentation. - -tl;dr: If an SDL function returns a `const char *` string, do not modify or -free it, and if you need to save it, make a copy right away. - -In several cases, SDL wants to return a string to the app, and the question -in any library that does this is: _who owns this thing?_ - -The answer in almost all cases, is that SDL does, but not for long. - -The pointer is only guaranteed to be valid until the next time the event -queue is updated, or SDL_Quit is called. - -The reason for this is memory safety. - -There are several strings that SDL provides that could be freed at -any moment. For example, an app calls SDL_GetAudioDeviceName(), which returns -a string that is part of the internal audio device structure. But, while this -function is returning, the user yanks the USB audio device out of the -computer, and SDL decides to deallocate the structure...and the string! -Now the app is holding a pointer that didn't live long enough to be useful, -and could crash if accessed. - -To avoid this, instead of calling SDL_free on a string as soon as it's done -with it, SDL adds the pointer to a list. This list is freed at specific -points: when the event queue is run (for ongoing cleanup) and when SDL_Quit -is called (to catch things that are just hanging around). This allows the -app to use a string without worrying about it becoming bogus in the middle -of a printf() call. If the app needs it for longer, it should copy it. - -When does "the event queue run"? There are several points: - -- If the app calls SDL_PumpEvents() _from any thread_. -- SDL_PumpEvents is also called by several other APIs internally: - SDL_PollEvent(), SDL_PeepEvents(), SDL_WaitEvent(), - SDL_WaitEventTimeout(), and maybe others. -- If you are using [the main callbacks](main-functions#main-callbacks-in-sdl3), - the event queue can run immediately after any of the callback functions - return. - -Note that these are just guaranteed minimum lifespans; any given string -might live much longer--some might even be static memory that is _never_ -deallocated--but this rule promises that the app has a safe window. - -Note that none of this applies if the return value is `char *` instead of -`const char *`. Please see the specific function's documentation for how -to handle those pointers. - diff --git a/docs/README-winrt.md b/docs/README-winrt.md index eb20ddad7e66f..58a3074205c9c 100644 --- a/docs/README-winrt.md +++ b/docs/README-winrt.md @@ -95,45 +95,6 @@ Here is a rough list of what works, and what doesn't: -Upgrade Notes -------------- - -#### SDL_GetPrefPath() usage when upgrading WinRT apps from SDL 2.0.3 - -SDL 2.0.4 fixes two bugs found in the WinRT version of SDL_GetPrefPath(). -The fixes may affect older, SDL 2.0.3-based apps' save data. Please note -that these changes only apply to SDL-based WinRT apps, and not to apps for -any other platform. - -1. SDL_GetPrefPath() would return an invalid path, one in which the path's - directory had not been created. Attempts to create files there - (via fopen(), for example), would fail, unless that directory was - explicitly created beforehand. - -2. SDL_GetPrefPath(), for non-WinPhone-based apps, would return a path inside - a WinRT 'Roaming' folder, the contents of which get automatically - synchronized across multiple devices. This process can occur while an - application runs, and can cause existing save-data to be overwritten - at unexpected times, with data from other devices. (Windows Phone apps - written with SDL 2.0.3 did not utilize a Roaming folder, due to API - restrictions in Windows Phone 8.0). - - -SDL_GetPrefPath(), starting with SDL 2.0.4, addresses these by: - -1. making sure that SDL_GetPrefPath() returns a directory in which data - can be written to immediately, without first needing to create directories. - -2. basing SDL_GetPrefPath() off of a different, non-Roaming folder, the - contents of which do not automatically get synchronized across devices - (and which require less work to use safely, in terms of data integrity). - -Apps that wish to get their Roaming folder's path can do so either by using -SDL_GetWinRTFSPath(), or directly through the WinRT class, -Windows.Storage.ApplicationData. - - - Setup, High-Level Steps ----------------------- diff --git a/docs/doxyfile b/docs/doxyfile index 19de1623b756c..c8891d82b5be3 100644 --- a/docs/doxyfile +++ b/docs/doxyfile @@ -1298,6 +1298,7 @@ INCLUDE_FILE_PATTERNS = PREDEFINED = DOXYGEN_SHOULD_IGNORE_THIS=1 \ SDL_DECLSPEC= \ + SDL_DECLSPEC_TEMP= \ SDLCALL= \ _WIN32=1 diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index aa52e808c336e..298596feda716 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -44,7 +44,7 @@ if(NOT CMAKE_VERSION VERSION_LESS 3.20) set(example_bin_dir "${example_bin_dir}$<$:/$>") endif() -file(GLOB RESOURCE_FILES ../test/*.bmp ../test/*.wav ../test/*.hex) +file(GLOB RESOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/../test/*.bmp ${CMAKE_CURRENT_SOURCE_DIR}/../test/*.wav ${CMAKE_CURRENT_SOURCE_DIR}/../test/*.hex) set(RESOURCE_FILE_NAMES) set(RESOURCE_FILES_BINDIR) @@ -63,7 +63,7 @@ add_custom_target(copy-sdl-example-resources ) if(WINDOWS_STORE) - add_library(sdl_example_main_callbacks_uwp OBJECT ../test/main.cpp) + add_library(sdl_example_main_callbacks_uwp OBJECT ${CMAKE_CURRENT_SOURCE_DIR}/../test/main.cpp) target_link_libraries(sdl_example_main_callbacks_uwp PRIVATE SDL3::Headers) target_compile_options(sdl_example_main_callbacks_uwp PRIVATE "/ZW") target_compile_definitions(sdl_example_main_callbacks_uwp PRIVATE "SDL_MAIN_USE_CALLBACKS") @@ -96,10 +96,10 @@ macro(add_sdl_example_executable TARGET) list(APPEND EXTRA_SOURCES "${uwp_bindir}/${TARGET}.appxmanifest" - "../test/uwp/logo-50x50.png" - "../test/uwp/square-44x44.png" - "../test/uwp/square-150x150.png" - "../test/uwp/splash-620x300.png" + "${CMAKE_CURRENT_SOURCE_DIR}/../test/uwp/logo-50x50.png" + "${CMAKE_CURRENT_SOURCE_DIR}/../test/uwp/square-44x44.png" + "${CMAKE_CURRENT_SOURCE_DIR}/../test/uwp/square-150x150.png" + "${CMAKE_CURRENT_SOURCE_DIR}/../test/uwp/splash-620x300.png" ) endif() if(AST_DATAFILES) @@ -185,7 +185,8 @@ add_sdl_example_executable(renderer-clear SOURCES renderer/01-clear/renderer-cle add_sdl_example_executable(renderer-primitives SOURCES renderer/02-primitives/renderer-primitives.c) add_sdl_example_executable(audio-simple-playback SOURCES audio/01-simple-playback/simple-playback.c) add_sdl_example_executable(audio-simple-playback-callback SOURCES audio/02-simple-playback-callback/simple-playback-callback.c) -add_sdl_example_executable(audio-load-wav SOURCES audio/03-load-wav/load-wav.c DATAFILES ../test/sample.wav) +add_sdl_example_executable(audio-load-wav SOURCES audio/03-load-wav/load-wav.c DATAFILES ${CMAKE_CURRENT_SOURCE_DIR}/../test/sample.wav) +add_sdl_example_executable(game-snake SOURCES game/01-snake/main.c game/01-snake/snake.c) if(PSP) @@ -311,7 +312,7 @@ if(ANDROID AND TARGET SDL3::Jar) include(SdlAndroidFunctions) sdl_create_android_debug_keystore(SDL_example-debug-keystore) - sdl_android_compile_resources(SDL_example-resources RESFOLDER ../test/android/res) + sdl_android_compile_resources(SDL_example-resources RESFOLDER ${CMAKE_CURRENT_SOURCE_DIR}/../test/android/res) add_custom_target(sdl-example-apks) foreach(EXAMPLE ${SDL_EXAMPLE_EXECUTABLES}) set(ANDROID_MANIFEST_APP_NAME "${EXAMPLE}") @@ -323,11 +324,11 @@ if(ANDROID AND TARGET SDL3::Jar) set(GENERATED_SRC_FOLDER "${CMAKE_CURRENT_BINARY_DIR}/android/${EXAMPLE}-src") set(GENERATED_RES_FOLDER "${GENERATED_SRC_FOLDER}/res") set(JAVA_PACKAGE_DIR "${GENERATED_SRC_FOLDER}/${JAVA_PACKAGE_DIR}") - configure_file(../test/android/cmake/SDLEntryTestActivity.java.cmake "${JAVA_PACKAGE_DIR}/SDLEntryTestActivity.java" @ONLY) - configure_file(../test/android/cmake/SDLTestActivity.java.cmake "${JAVA_PACKAGE_DIR}/SDLTestActivity.java" @ONLY) - configure_file(../test/android/cmake/res/values/strings.xml.cmake android/res/values/strings-${EXAMPLE}.xml @ONLY) - configure_file(../test/android/cmake/res/xml/shortcuts.xml.cmake "${GENERATED_RES_FOLDER}/xml/shortcuts.xml" @ONLY) - configure_file(../test/android/cmake/AndroidManifest.xml.cmake "${generated_manifest_path}" @ONLY) + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/../test/android/cmake/SDLEntryTestActivity.java.cmake "${JAVA_PACKAGE_DIR}/SDLEntryTestActivity.java" @ONLY) + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/../test/android/cmake/SDLTestActivity.java.cmake "${JAVA_PACKAGE_DIR}/SDLTestActivity.java" @ONLY) + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/../test/android/cmake/res/values/strings.xml.cmake android/res/values/strings-${EXAMPLE}.xml @ONLY) + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/../test/android/cmake/res/xml/shortcuts.xml.cmake "${GENERATED_RES_FOLDER}/xml/shortcuts.xml" @ONLY) + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/../test/android/cmake/AndroidManifest.xml.cmake "${generated_manifest_path}" @ONLY) file(GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/android/${EXAMPLE}-$/res/values/strings.xml" INPUT "${CMAKE_CURRENT_BINARY_DIR}/android/res/values/strings-${EXAMPLE}.xml" diff --git a/examples/audio/01-simple-playback/README.txt b/examples/audio/01-simple-playback/README.txt new file mode 100644 index 0000000000000..9b3c07dd0e3e3 --- /dev/null +++ b/examples/audio/01-simple-playback/README.txt @@ -0,0 +1,5 @@ +If you're running this in a web browser, you need to click the window before you'll hear anything! + +This example code creates an simple audio stream for playing sound, and +generates a sine wave sound effect for it to play as time goes on. This is the +simplest way to get up and running with procedural sound. diff --git a/examples/audio/01-simple-playback/simple-playback.c b/examples/audio/01-simple-playback/simple-playback.c index 464d760d4ca54..ecf0c2dba0913 100644 --- a/examples/audio/01-simple-playback/simple-playback.c +++ b/examples/audio/01-simple-playback/simple-playback.c @@ -47,9 +47,6 @@ int SDL_AppInit(void **appstate, int argc, char *argv[]) /* SDL_OpenAudioDeviceStream starts the device paused. You have to tell it to start! */ SDL_ResumeAudioStreamDevice(stream); - /* (this is a web browser requirement, not an SDL thing.) */ - SDL_Log("If you're running this in a web browser, you need to click the window before you'll hear anything."); - return SDL_APP_CONTINUE; /* carry on with the program! */ } diff --git a/examples/audio/02-simple-playback-callback/README.txt b/examples/audio/02-simple-playback-callback/README.txt new file mode 100644 index 0000000000000..e719256d8c106 --- /dev/null +++ b/examples/audio/02-simple-playback-callback/README.txt @@ -0,0 +1,5 @@ +If you're running this in a web browser, you need to click the window before you'll hear anything! + +This example code creates an simple audio stream for playing sound, and +generates a sine wave sound effect for it to play as time goes on. Unlike +the previous example, this uses a callback to generate sound. diff --git a/examples/audio/02-simple-playback-callback/simple-playback-callback.c b/examples/audio/02-simple-playback-callback/simple-playback-callback.c index 4d21296ecf550..1fa1e0b02e6eb 100644 --- a/examples/audio/02-simple-playback-callback/simple-playback-callback.c +++ b/examples/audio/02-simple-playback-callback/simple-playback-callback.c @@ -79,9 +79,6 @@ int SDL_AppInit(void **appstate, int argc, char *argv[]) /* SDL_OpenAudioDeviceStream starts the device paused. You have to tell it to start! */ SDL_ResumeAudioStreamDevice(stream); - /* (this is a web browser requirement, not an SDL thing.) */ - SDL_Log("If you're running this in a web browser, you need to click the window before you'll hear anything."); - return SDL_APP_CONTINUE; /* carry on with the program! */ } diff --git a/examples/audio/03-load-wav/README.txt b/examples/audio/03-load-wav/README.txt new file mode 100644 index 0000000000000..5a6894c765a7b --- /dev/null +++ b/examples/audio/03-load-wav/README.txt @@ -0,0 +1,5 @@ +If you're running this in a web browser, you need to click the window before you'll hear anything! + +This example code creates an simple audio stream for playing sound, and +loads a .wav file that is pushed through the stream in a loop. + diff --git a/examples/audio/03-load-wav/load-wav.c b/examples/audio/03-load-wav/load-wav.c index 0a24a15ebba66..6467ce7e4aa30 100644 --- a/examples/audio/03-load-wav/load-wav.c +++ b/examples/audio/03-load-wav/load-wav.c @@ -61,9 +61,6 @@ int SDL_AppInit(void **appstate, int argc, char *argv[]) /* SDL_OpenAudioDeviceStream starts the device paused. You have to tell it to start! */ SDL_ResumeAudioStreamDevice(stream); - /* (this is a web browser requirement, not an SDL thing.) */ - SDL_Log("If you're running this in a web browser, you need to click the window before you'll hear anything."); - return SDL_APP_CONTINUE; /* carry on with the program! */ } @@ -83,7 +80,7 @@ int SDL_AppIterate(void *appstate) We're being lazy here, but if there's less than the entire wav file left to play, just shove a whole copy of it into the queue, so we always have _tons_ of data queued for playback. */ - if (SDL_GetAudioStreamAvailable(stream) < wav_data_len) { + if (SDL_GetAudioStreamAvailable(stream) < (int)wav_data_len) { /* feed more data to the stream. It will queue at the end, and trickle out as the hardware needs more data. */ SDL_PutAudioStreamData(stream, wav_data, wav_data_len); } diff --git a/examples/game/01-snake/README.txt b/examples/game/01-snake/README.txt new file mode 100644 index 0000000000000..1cf97846ac7b9 --- /dev/null +++ b/examples/game/01-snake/README.txt @@ -0,0 +1 @@ +A complete game of Snake, written in SDL. diff --git a/examples/game/01-snake/main.c b/examples/game/01-snake/main.c new file mode 100644 index 0000000000000..d09f008cb2ec9 --- /dev/null +++ b/examples/game/01-snake/main.c @@ -0,0 +1,161 @@ +/* + * This example code implements a Snake game that showcases some of the + * functionalities of SDL, such as timer callbacks and event handling. + * + * This code is public domain. Feel free to use it for any purpose! + */ +#define SDL_MAIN_USE_CALLBACKS 1 /* use the callbacks instead of main() */ +#include +#include +#include /* malloc(), free() */ + +#include "snake.h" + +#define STEP_RATE_IN_MILLISECONDS 125 +#define SNAKE_BLOCK_SIZE_IN_PIXELS 24 +#define SDL_WINDOW_WIDTH (SNAKE_BLOCK_SIZE_IN_PIXELS * SNAKE_GAME_WIDTH) +#define SDL_WINDOW_HEIGHT (SNAKE_BLOCK_SIZE_IN_PIXELS * SNAKE_GAME_HEIGHT) + +typedef struct +{ + SDL_Window *window; + SDL_Renderer *renderer; + SDL_TimerID step_timer; + SnakeContext snake_ctx; +} AppState; + +static Uint32 sdl_timer_callback_(void *payload, SDL_TimerID timer_id, Uint32 interval) +{ + SDL_Event e; + SDL_UserEvent ue; + /* NOTE: snake_step is not called here directly for multithreaded concerns. */ + (void)payload; + ue.type = SDL_EVENT_USER; + ue.code = 0; + ue.data1 = NULL; + ue.data2 = NULL; + e.type = SDL_EVENT_USER; + e.user = ue; + SDL_PushEvent(&e); + return interval; +} + +static int handle_key_event_(SnakeContext *ctx, SDL_Scancode key_code) +{ + switch (key_code) { + /* Quit. */ + case SDL_SCANCODE_ESCAPE: + case SDL_SCANCODE_Q: + return SDL_APP_SUCCESS; + /* Restart the game as if the program was launched. */ + case SDL_SCANCODE_R: + snake_initialize(ctx, SDL_rand); + break; + /* Decide new direction of the snake. */ + case SDL_SCANCODE_RIGHT: + snake_redir(ctx, SNAKE_DIR_RIGHT); + break; + case SDL_SCANCODE_UP: + snake_redir(ctx, SNAKE_DIR_UP); + break; + case SDL_SCANCODE_LEFT: + snake_redir(ctx, SNAKE_DIR_LEFT); + break; + case SDL_SCANCODE_DOWN: + snake_redir(ctx, SNAKE_DIR_DOWN); + break; + default: + break; + } + return SDL_APP_CONTINUE; +} + +static void set_rect_xy_(SDL_FRect *r, short x, short y) +{ + r->x = (float)(x * SNAKE_BLOCK_SIZE_IN_PIXELS); + r->y = (float)(y * SNAKE_BLOCK_SIZE_IN_PIXELS); +} + +int SDL_AppIterate(void *appstate) +{ + AppState *as; + SnakeContext *ctx; + SDL_FRect r; + unsigned i; + unsigned j; + int ct; + as = (AppState *)appstate; + ctx = &as->snake_ctx; + r.w = r.h = SNAKE_BLOCK_SIZE_IN_PIXELS; + SDL_SetRenderDrawColor(as->renderer, 0, 0, 0, 255); + SDL_RenderClear(as->renderer); + for (i = 0; i < SNAKE_GAME_WIDTH; i++) { + for (j = 0; j < SNAKE_GAME_HEIGHT; j++) { + ct = snake_cell_at(ctx, i, j); + if (ct == SNAKE_CELL_NOTHING) + continue; + set_rect_xy_(&r, i, j); + if (ct == SNAKE_CELL_FOOD) + SDL_SetRenderDrawColor(as->renderer, 0, 0, 128, 255); + else /* body */ + SDL_SetRenderDrawColor(as->renderer, 0, 128, 0, 255); + SDL_RenderFillRect(as->renderer, &r); + } + } + SDL_SetRenderDrawColor(as->renderer, 255, 255, 0, 255); /*head*/ + set_rect_xy_(&r, ctx->head_xpos, ctx->head_ypos); + SDL_RenderFillRect(as->renderer, &r); + SDL_RenderPresent(as->renderer); + return SDL_APP_CONTINUE; +} + +int SDL_AppInit(void **appstate, int argc, char *argv[]) +{ + (void)argc; + (void)argv; + if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER) < 0) { + return SDL_APP_FAILURE; + } + SDL_SetHint("SDL_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR", "0"); + AppState *as = malloc(sizeof(AppState)); + *appstate = as; + as->step_timer = 0; + if (SDL_CreateWindowAndRenderer("examples/game/snake", SDL_WINDOW_WIDTH, SDL_WINDOW_HEIGHT, 0, &as->window, &as->renderer) == -1) { + return SDL_APP_FAILURE; + } + snake_initialize(&as->snake_ctx, SDL_rand); + as->step_timer = SDL_AddTimer( + STEP_RATE_IN_MILLISECONDS, + sdl_timer_callback_, + NULL); + if (as->step_timer == 0) { + return SDL_APP_FAILURE; + } + return SDL_APP_CONTINUE; +} + +int SDL_AppEvent(void *appstate, const SDL_Event *event) +{ + SnakeContext *ctx = &((AppState *)appstate)->snake_ctx; + switch (event->type) { + case SDL_EVENT_QUIT: + return SDL_APP_SUCCESS; + case SDL_EVENT_USER: + snake_step(ctx, SDL_rand); + break; + case SDL_EVENT_KEY_DOWN: + return handle_key_event_(ctx, event->key.scancode); + } + return SDL_APP_CONTINUE; +} + +void SDL_AppQuit(void *appstate) +{ + if (appstate != NULL) { + AppState *as = (AppState *)appstate; + SDL_RemoveTimer(as->step_timer); + SDL_DestroyRenderer(as->renderer); + SDL_DestroyWindow(as->window); + free(as); + } +} diff --git a/examples/game/01-snake/snake.c b/examples/game/01-snake/snake.c new file mode 100644 index 0000000000000..294edff1699c7 --- /dev/null +++ b/examples/game/01-snake/snake.c @@ -0,0 +1,154 @@ +/* + * Logic implementation of the Snake game. It is designed to efficiently + * represent in memory the state of the game. + * + * This code is public domain. Feel free to use it for any purpose! + */ +#include "snake.h" + +#include /* CHAR_BIT, CHAR_MAX */ +#include /* memcpy() */ + +#define THREE_BITS 0x7U /* ~CHAR_MAX >> (CHAR_BIT - SNAKE_CELL_MAX_BITS) */ +#define SHIFT(x, y) (((x) + ((y) * SNAKE_GAME_WIDTH)) * SNAKE_CELL_MAX_BITS) + +static void put_cell_at_(SnakeContext *ctx, char x, char y, SnakeCell ct) +{ + const int shift = SHIFT(x, y); + const int adjust = shift % CHAR_BIT; + unsigned char *const pos = ctx->cells + (shift / CHAR_BIT); + unsigned short range; + memcpy(&range, pos, sizeof(range)); + range &= ~(THREE_BITS << adjust); /* clear bits */ + range |= (ct & THREE_BITS) << adjust; + memcpy(pos, &range, sizeof(range)); +} + +static int are_cells_full_(SnakeContext *ctx) +{ + return ctx->occupied_cells == SNAKE_GAME_WIDTH * SNAKE_GAME_HEIGHT; +} + +static void new_food_pos_(SnakeContext *ctx, RandFunc rand) +{ + char x; + char y; + for (;;) { + x = (char) rand(SNAKE_GAME_WIDTH); + y = (char) rand(SNAKE_GAME_HEIGHT); + if (snake_cell_at(ctx, x, y) == SNAKE_CELL_NOTHING) { + put_cell_at_(ctx, x, y, SNAKE_CELL_FOOD); + break; + } + } +} + +void snake_initialize(SnakeContext *ctx, RandFunc rand) +{ + int i; + memset(ctx, 0, sizeof ctx->cells); + ctx->head_xpos = ctx->tail_xpos = SNAKE_GAME_WIDTH / 2; + ctx->head_ypos = ctx->tail_ypos = SNAKE_GAME_HEIGHT / 2; + ctx->next_dir = SNAKE_DIR_RIGHT; + ctx->inhibit_tail_step = ctx->occupied_cells = 4; + --ctx->occupied_cells; + put_cell_at_(ctx, ctx->tail_xpos, ctx->tail_ypos, SNAKE_CELL_SRIGHT); + for (i = 0; i < 4; i++) { + new_food_pos_(ctx, rand); + ++ctx->occupied_cells; + } +} + +void snake_redir(SnakeContext *ctx, SnakeDirection dir) +{ + SnakeCell ct = snake_cell_at(ctx, ctx->head_xpos, ctx->head_ypos); + if ((dir == SNAKE_DIR_RIGHT && ct != SNAKE_CELL_SLEFT) || + (dir == SNAKE_DIR_UP && ct != SNAKE_CELL_SDOWN) || + (dir == SNAKE_DIR_LEFT && ct != SNAKE_CELL_SRIGHT) || + (dir == SNAKE_DIR_DOWN && ct != SNAKE_CELL_SUP)) + ctx->next_dir = dir; +} + +static void wrap_around_(char *val, char max) +{ + if (*val < 0) + *val = max - 1; + if (*val > max - 1) + *val = 0; +} + +void snake_step(SnakeContext *ctx, RandFunc rand) +{ + const SnakeCell dir_as_cell = (SnakeCell)(ctx->next_dir + 1); + SnakeCell ct; + char prev_xpos; + char prev_ypos; + /* Move tail forward */ + if (--ctx->inhibit_tail_step == 0) { + ++ctx->inhibit_tail_step; + ct = snake_cell_at(ctx, ctx->tail_xpos, ctx->tail_ypos); + put_cell_at_(ctx, ctx->tail_xpos, ctx->tail_ypos, SNAKE_CELL_NOTHING); + switch (ct) { + case SNAKE_CELL_SRIGHT: + ctx->tail_xpos++; + break; + case SNAKE_CELL_SUP: + ctx->tail_ypos--; + break; + case SNAKE_CELL_SLEFT: + ctx->tail_xpos--; + break; + case SNAKE_CELL_SDOWN: + ctx->tail_ypos++; + break; + default: + break; + } + wrap_around_(&ctx->tail_xpos, SNAKE_GAME_WIDTH); + wrap_around_(&ctx->tail_ypos, SNAKE_GAME_HEIGHT); + } + /* Move head forward */ + prev_xpos = ctx->head_xpos; + prev_ypos = ctx->head_ypos; + switch (ctx->next_dir) { + case SNAKE_DIR_RIGHT: + ++ctx->head_xpos; + break; + case SNAKE_DIR_UP: + --ctx->head_ypos; + break; + case SNAKE_DIR_LEFT: + --ctx->head_xpos; + break; + case SNAKE_DIR_DOWN: + ++ctx->head_ypos; + break; + } + wrap_around_(&ctx->head_xpos, SNAKE_GAME_WIDTH); + wrap_around_(&ctx->head_ypos, SNAKE_GAME_HEIGHT); + /* Collisions */ + ct = snake_cell_at(ctx, ctx->head_xpos, ctx->head_ypos); + if (ct != SNAKE_CELL_NOTHING && ct != SNAKE_CELL_FOOD) { + snake_initialize(ctx, rand); + return; + } + put_cell_at_(ctx, prev_xpos, prev_ypos, dir_as_cell); + put_cell_at_(ctx, ctx->head_xpos, ctx->head_ypos, dir_as_cell); + if (ct == SNAKE_CELL_FOOD) { + if (are_cells_full_(ctx)) { + snake_initialize(ctx, rand); + return; + } + new_food_pos_(ctx, rand); + ++ctx->inhibit_tail_step; + ++ctx->occupied_cells; + } +} + +SnakeCell snake_cell_at(const SnakeContext *ctx, char x, char y) +{ + const int shift = SHIFT(x, y); + unsigned short range; + memcpy(&range, ctx->cells + (shift / CHAR_BIT), sizeof(range)); + return (SnakeCell)((range >> (shift % CHAR_BIT)) & THREE_BITS); +} diff --git a/examples/game/01-snake/snake.h b/examples/game/01-snake/snake.h new file mode 100644 index 0000000000000..dd2d507c850d5 --- /dev/null +++ b/examples/game/01-snake/snake.h @@ -0,0 +1,54 @@ +/* + * Interface definition of the Snake game. + * + * This code is public domain. Feel free to use it for any purpose! + */ +#ifndef SNAKE_H +#define SNAKE_H + +#include + +#define SNAKE_GAME_WIDTH 24U +#define SNAKE_GAME_HEIGHT 18U +#define SNAKE_MATRIX_SIZE (SNAKE_GAME_WIDTH * SNAKE_GAME_HEIGHT) + +typedef enum +{ + SNAKE_CELL_NOTHING = 0U, + SNAKE_CELL_SRIGHT = 1U, + SNAKE_CELL_SUP = 2U, + SNAKE_CELL_SLEFT = 3U, + SNAKE_CELL_SDOWN = 4U, + SNAKE_CELL_FOOD = 5U +} SnakeCell; + +#define SNAKE_CELL_MAX_BITS 3U /* floor(log2(SNAKE_CELL_FOOD)) + 1 */ + +typedef enum +{ + SNAKE_DIR_RIGHT, + SNAKE_DIR_UP, + SNAKE_DIR_LEFT, + SNAKE_DIR_DOWN +} SnakeDirection; + +typedef struct +{ + unsigned char cells[(SNAKE_MATRIX_SIZE * SNAKE_CELL_MAX_BITS) / 8U]; + char head_xpos; + char head_ypos; + char tail_xpos; + char tail_ypos; + char next_dir; + char inhibit_tail_step; + unsigned occupied_cells; +} SnakeContext; + +typedef Sint32 (SDLCALL *RandFunc)(Sint32 n); + +void snake_initialize(SnakeContext *ctx, RandFunc rand); +void snake_redir(SnakeContext *ctx, SnakeDirection dir); +void snake_step(SnakeContext *ctx, RandFunc rand); +SnakeCell snake_cell_at(const SnakeContext *ctx, char x, char y); + +#endif /* SNAKE_H */ diff --git a/examples/renderer/01-clear/README.txt b/examples/renderer/01-clear/README.txt new file mode 100644 index 0000000000000..ce9ef81319863 --- /dev/null +++ b/examples/renderer/01-clear/README.txt @@ -0,0 +1,3 @@ +This example code creates an SDL window and renderer, and then clears the +window to a different color every frame, so you'll effectively get a window +that's smoothly fading between colors. diff --git a/examples/renderer/02-primitives/README.txt b/examples/renderer/02-primitives/README.txt new file mode 100644 index 0000000000000..1e2bfce8b794e --- /dev/null +++ b/examples/renderer/02-primitives/README.txt @@ -0,0 +1,3 @@ +This example creates an SDL window and renderer, and then draws some lines, +rectangles and points to it every frame. + diff --git a/examples/renderer/02-primitives/renderer-primitives.c b/examples/renderer/02-primitives/renderer-primitives.c index 9bb8f079f420c..e2021e885492a 100644 --- a/examples/renderer/02-primitives/renderer-primitives.c +++ b/examples/renderer/02-primitives/renderer-primitives.c @@ -1,6 +1,6 @@ /* - * This example code creates an SDL window and renderer, and then draws a few - * lines and rectangles to it every frame. + * This example creates an SDL window and renderer, and then draws some lines, + * rectangles and points to it every frame. * * This code is public domain. Feel free to use it for any purpose! */ diff --git a/examples/template.html b/examples/template.html index c047d615b3199..e8be678b31fe5 100644 --- a/examples/template.html +++ b/examples/template.html @@ -5,63 +5,157 @@ @project_name@ Example: @category_name@/@example_name@ -
- +
+ +
+
+ @description@ +
+
+
- -
@htmlified_source_code@
+
+
@htmlified_source_code@
+