diff --git a/.gitattributes b/.gitattributes index 740e182d..2dc9f280 100644 --- a/.gitattributes +++ b/.gitattributes @@ -19,6 +19,3 @@ # Custom for this project *.bat eol=crlf *.txt eol=crlf - -# Fix an issue with Python 2 on Linux -tcod/libtcod_cdef.h eol=lf diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000..2b2eb8c4 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,15 @@ +# Keep GitHub Actions up to date with GitHub's Dependabot... +# https://docs.github.com/en/code-security/dependabot/working-with-dependabot/keeping-your-actions-up-to-date-with-dependabot +# https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file#package-ecosystem +version: 2 +updates: + - package-ecosystem: github-actions + directory: / + groups: + github-actions: + patterns: + - "*" # Group all Actions updates into a single larger pull request + schedule: + interval: weekly + cooldown: + default-days: 7 diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index 1b1ecec6..6084d0dc 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -4,9 +4,13 @@ name: Package on: + workflow_dispatch: push: pull_request: - types: [opened, reopened] + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true defaults: run: @@ -14,52 +18,49 @@ defaults: env: git-depth: 0 # Depth to search for tags. + sdl-version: "3.2.16" # SDL version to bundle jobs: - black: + ruff: runs-on: ubuntu-latest + timeout-minutes: 5 steps: - - uses: actions/checkout@v3 - - name: Install Black - run: pip install black - - name: Run Black - run: black --check --diff examples/ scripts/ tcod/ tests/ *.py - - isort: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - name: Install isort - run: pip install isort - - name: isort - uses: liskin/gh-problem-matcher-wrap@v2 + - uses: actions/checkout@v7 with: - linters: isort - run: isort scripts/ tcod/ tests/ examples/ --check --diff + persist-credentials: false + - name: Install Ruff + run: pip install ruff + - name: Ruff Check + run: ruff check . --fix-only --exit-non-zero-on-fix --output-format=github + - name: Ruff Format + run: ruff format . --check mypy: runs-on: ubuntu-latest + timeout-minutes: 5 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v7 + with: + persist-credentials: false - name: Checkout submodules run: git submodule update --init --recursive --depth 1 - name: Install typing dependencies run: pip install mypy pytest -r requirements.txt - name: Mypy - uses: liskin/gh-problem-matcher-wrap@v2 - with: - linters: mypy - run: mypy --show-column-numbers + run: mypy sdist: runs-on: ubuntu-latest + timeout-minutes: 5 steps: - - name: APT update - run: sudo apt-get update - - name: Install APT dependencies - run: sudo apt-get install libsdl2-dev - - uses: actions/checkout@v3 + - uses: libsdl-org/setup-sdl@1894460666e4fe0c6603ea0cce5733f1cca56ba1 with: + install-linux-dependencies: true + build-type: "Debug" + version: ${{ env.sdl-version }} + - uses: actions/checkout@v7 + with: + persist-credentials: false fetch-depth: ${{ env.git-depth }} - name: Checkout submodules run: git submodule update --init --recursive --depth 1 @@ -67,29 +68,31 @@ jobs: run: pip install build - name: Build source distribution run: python -m build --sdist - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v7 with: name: sdist path: dist/tcod-*.tar.gz retention-days: 7 - + compression-level: 0 # This makes sure that the latest versions of the SDL headers parse correctly. parse_sdl: - needs: [black, isort, mypy] + needs: [ruff, mypy] runs-on: ${{ matrix.os }} + timeout-minutes: 5 strategy: matrix: os: ["windows-latest", "macos-latest"] - sdl-version: ["2.0.14", "2.0.16"] + sdl-version: ["3.2.16"] fail-fast: true steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v7 with: + persist-credentials: false fetch-depth: ${{ env.git-depth }} - name: Checkout submodules run: git submodule update --init --recursive --depth 1 - - uses: actions/setup-python@v4 + - uses: actions/setup-python@v6 with: python-version: "3.x" - name: Install build dependencies @@ -100,31 +103,38 @@ jobs: SDL_VERSION: ${{ matrix.sdl-version }} build: - needs: [black, isort, mypy] + needs: [ruff, mypy, sdist] runs-on: ${{ matrix.os }} + timeout-minutes: 15 strategy: matrix: os: ["ubuntu-latest", "windows-latest"] - python-version: ["3.8", "3.9", "pypy-3.8"] + python-version: ["3.10", "3.14t", "pypy-3.10"] architecture: ["x64"] include: - os: "windows-latest" - python-version: "3.8" + python-version: "3.10" architecture: "x86" - os: "windows-latest" - python-version: "pypy-3.8" + python-version: "3.14t" architecture: "x86" + - os: "windows-11-arm" + python-version: "3.11" + architecture: "arm64" + - os: "windows-11-arm" + python-version: "3.14t" + architecture: "arm64" fail-fast: false steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v7 with: + persist-credentials: false fetch-depth: ${{ env.git-depth }} - name: Checkout submodules - run: | - git submodule update --init --recursive --depth 1 + run: git submodule update --init --recursive --depth 1 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v4 + uses: actions/setup-python@v6 with: python-version: ${{ matrix.python-version }} architecture: ${{ matrix.architecture }} @@ -132,47 +142,54 @@ jobs: if: runner.os == 'Linux' run: | sudo apt-get update - sudo apt-get install libsdl2-dev xvfb + sudo apt-get install xvfb + - uses: libsdl-org/setup-sdl@1894460666e4fe0c6603ea0cce5733f1cca56ba1 + if: runner.os == 'Linux' + with: + install-linux-dependencies: true + build-type: "Release" + version: ${{ env.sdl-version }} - name: Install Python dependencies run: | python -m pip install --upgrade pip pip install pytest pytest-cov pytest-benchmark pytest-timeout build if [ -f requirements.txt ]; then pip install -r requirements.txt; fi - name: Initialize package - run: | - pip install -e . # Install the package in-place. + run: pip install -e . # Install the package in-place. - name: Build package - run: | - python -m build + run: python -m build - name: Test with pytest if: runner.os == 'Windows' - run: | - pytest --cov-report=xml --timeout=300 + run: pytest --cov-report=xml --timeout=300 - name: Test with pytest (Xvfb) if: always() && runner.os != 'Windows' - run: | - xvfb-run -e /tmp/xvfb.log --server-num=$RANDOM --auto-servernum pytest --cov-report=xml --timeout=300 + run: xvfb-run -e /tmp/xvfb.log --server-num=$RANDOM --auto-servernum pytest --cov-report=xml --timeout=300 - name: Xvfb logs if: runner.os != 'Windows' run: cat /tmp/xvfb.log - - uses: codecov/codecov-action@v3 - - uses: actions/upload-artifact@v3 + - uses: codecov/codecov-action@v7 + - uses: actions/upload-artifact@v7 if: runner.os == 'Windows' with: - name: wheels-windows + name: wheels-windows-${{ matrix.architecture }}-${{ matrix.python-version }} path: dist/*.whl retention-days: 7 + compression-level: 0 test-docs: - needs: [black, isort, mypy] + needs: [ruff, mypy, sdist] runs-on: ubuntu-latest + timeout-minutes: 15 steps: - - name: Install APT dependencies - run: | - sudo apt-get update - sudo apt-get install libsdl2-dev - - uses: actions/checkout@v3 + - uses: libsdl-org/setup-sdl@1894460666e4fe0c6603ea0cce5733f1cca56ba1 + if: runner.os == 'Linux' + with: + install-linux-dependencies: true + build-type: "Debug" + version: ${{ env.sdl-version }} + - uses: actions/checkout@v7 with: + persist-credentials: false fetch-depth: ${{ env.git-depth }} - name: Checkout submodules run: git submodule update --init --recursive --depth 1 @@ -188,64 +205,56 @@ jobs: working-directory: docs run: python -m sphinx -T -E -W --keep-going . _build/html - isolated: # Test installing the package from source. - needs: [black, isort, mypy, sdist] + tox: + needs: [ruff, sdist] runs-on: ${{ matrix.os }} + timeout-minutes: 15 strategy: matrix: - os: ["ubuntu-latest", "windows-latest"] + os: ["ubuntu-latest"] # "windows-latest" disabled due to free-threaded build issues steps: + - uses: actions/checkout@v7 + with: + persist-credentials: false + fetch-depth: ${{ env.git-depth }} + - name: Checkout submodules + run: git submodule update --init --depth 1 - name: Set up Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v6 with: - python-version: 3.x + python-version: "3.x" - name: Install Python dependencies run: | - python -m pip install --upgrade pip - pip install wheel - - name: Install APT dependencies + python -m pip install --upgrade pip tox + - uses: libsdl-org/setup-sdl@1894460666e4fe0c6603ea0cce5733f1cca56ba1 if: runner.os == 'Linux' - run: | - sudo apt-get update - sudo apt-get install libsdl2-dev - - uses: actions/download-artifact@v3 with: - name: sdist - - name: Build package in isolation + install-linux-dependencies: true + build-type: "Debug" + version: ${{ env.sdl-version }} + - name: Run tox run: | - pip install tcod-*.tar.gz - - name: Confirm package import - run: | - python -c "import tcod.context" + tox -vv linux-wheels: - needs: [black, isort, mypy] - runs-on: "ubuntu-latest" + needs: [ruff, mypy, sdist] + runs-on: ${{ matrix.arch == 'aarch64' && 'ubuntu-24.04-arm' || 'ubuntu-latest'}} + timeout-minutes: 15 strategy: matrix: arch: ["x86_64", "aarch64"] - build: ["cp38-manylinux*", "pp38-manylinux*"] + build: ["cp310-manylinux*", "pp311-manylinux*", "cp314t-manylinux*"] + env: + BUILD_DESC: "" steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v7 with: + persist-credentials: false fetch-depth: ${{ env.git-depth }} - - name: Set up QEMU - if: ${{ matrix.arch == 'aarch64' }} - uses: docker/setup-qemu-action@v2 - name: Checkout submodules - run: | - git submodule update --init --recursive --depth 1 - - name: Set up Python - uses: actions/setup-python@v4 - with: - python-version: "3.x" - - name: Install Python dependencies - run: | - python -m pip install --upgrade pip - pip install cibuildwheel==2.3.1 + run: git submodule update --init --recursive --depth 1 - name: Build wheels - run: | - python -m cibuildwheel --output-dir wheelhouse + uses: pypa/cibuildwheel@v4.0.0rc1 env: CIBW_BUILD: ${{ matrix.build }} CIBW_ARCHS_LINUX: ${{ matrix.arch }} @@ -255,38 +264,63 @@ jobs: CIBW_BEFORE_ALL_LINUX: > yum install -y epel-release && yum-config-manager --enable epel && - yum install -y SDL2-devel + yum install -y gcc git-core make cmake \ + alsa-lib-devel pulseaudio-libs-devel \ + libX11-devel libXext-devel libXrandr-devel libXcursor-devel libXfixes-devel \ + libXi-devel libXScrnSaver-devel dbus-devel ibus-devel \ + systemd-devel mesa-libGL-devel libxkbcommon-devel mesa-libGLES-devel \ + mesa-libEGL-devel vulkan-devel wayland-devel wayland-protocols-devel \ + libdrm-devel mesa-libgbm-devel libusb-devel + git clone --depth 1 --branch release-${{env.sdl-version}} https://github.com/libsdl-org/SDL.git sdl_repo && + cmake -S sdl_repo -B sdl_build && + cmake --build sdl_build --config Release && + cmake --install sdl_build --config Release --prefix /usr/local && + cp --verbose /usr/local/lib64/pkgconfig/sdl3.pc /lib64/pkgconfig/sdl3.pc CIBW_BEFORE_TEST: pip install numpy CIBW_TEST_COMMAND: python -c "import tcod.context" # Skip test on emulated architectures CIBW_TEST_SKIP: "*_aarch64" + - name: Remove asterisk from label + env: + BUILD_DESC: ${{ matrix.build }} + run: | + BUILD_DESC=${BUILD_DESC//\*} + echo BUILD_DESC=${BUILD_DESC} >> $GITHUB_ENV - name: Archive wheel - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v7 with: - name: wheels-linux + name: wheels-linux-${{ matrix.arch }}-${{ env.BUILD_DESC }} path: wheelhouse/*.whl retention-days: 7 + compression-level: 0 build-macos: - needs: [black, isort, mypy] - runs-on: "macos-11" + needs: [ruff, mypy, sdist] + runs-on: "macos-14" + timeout-minutes: 15 strategy: fail-fast: true matrix: - python: ["cp38-*_universal2", "cp38-*_x86_64", "cp38-*_arm64", "pp38-*"] + python: ["cp310-*_universal2", "cp314t-*_universal2", "pp311-*"] + env: + PYTHON_DESC: "" steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v7 with: + persist-credentials: false fetch-depth: ${{ env.git-depth }} - name: Checkout submodules run: git submodule update --init --recursive --depth 1 + - uses: actions/setup-python@v6 + with: + python-version: "3.x" - name: Install Python dependencies - run: pip3 install -r requirements.txt + run: pip install -r requirements.txt - name: Prepare package - # Downloads SDL2 for the later step. - run: python3 build_sdl.py + # Downloads SDL for the later step. + run: python build_sdl.py - name: Build wheels - uses: pypa/cibuildwheel@v2.12.3 + uses: pypa/cibuildwheel@v4.0.0rc1 env: CIBW_BUILD: ${{ matrix.python }} CIBW_ARCHS_MACOS: x86_64 arm64 universal2 @@ -294,39 +328,69 @@ jobs: CIBW_BEFORE_TEST: pip install numpy CIBW_TEST_COMMAND: python -c "import tcod.context" CIBW_TEST_SKIP: "pp* *-macosx_arm64 *-macosx_universal2:arm64" + MACOSX_DEPLOYMENT_TARGET: "10.13" + - name: Remove asterisk from label + env: + PYTHON_DESC: ${{ matrix.python }} + run: | + PYTHON_DESC=${PYTHON_DESC//\*/X} + echo PYTHON_DESC=${PYTHON_DESC} >> $GITHUB_ENV - name: Archive wheel - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v7 with: - name: wheels-macos + name: wheels-macos-${{ env.PYTHON_DESC }} path: wheelhouse/*.whl retention-days: 7 + compression-level: 0 - publish: - needs: [sdist, build, build-macos, linux-wheels] - runs-on: ubuntu-latest - if: startsWith(github.ref, 'refs/tags/') - environment: - name: release - url: https://pypi.org/p/tcod - permissions: - id-token: write - steps: - - uses: actions/download-artifact@v3 + pyodide: + needs: [ruff, mypy, sdist] + runs-on: ubuntu-24.04 + timeout-minutes: 15 + steps: + - uses: actions/checkout@v7 with: - name: sdist - path: dist/ - - uses: actions/download-artifact@v3 + persist-credentials: false + fetch-depth: ${{ env.git-depth }} + - name: Checkout submodules + run: git submodule update --init --recursive --depth 1 + - uses: libsdl-org/setup-sdl@1894460666e4fe0c6603ea0cce5733f1cca56ba1 with: - name: wheels-windows - path: dist/ - - uses: actions/download-artifact@v3 + install-linux-dependencies: true + build-type: "Debug" + version: "3.2.4" # Should be equal or less than the version used by Emscripten + - uses: pypa/cibuildwheel@v4.0.0rc1 + env: + CIBW_BUILD: cp313-pyodide_wasm32 + CIBW_PLATFORM: pyodide + - name: Archive wheel + uses: actions/upload-artifact@v7 + with: + name: wheels-pyodide + path: wheelhouse/*.whl + retention-days: 30 + compression-level: 0 + + publish: + needs: [sdist, build, build-macos, linux-wheels, pyodide] + runs-on: ubuntu-latest + timeout-minutes: 5 + if: github.ref_type == 'tag' + environment: + name: pypi + url: https://pypi.org/project/tcod/${{ github.ref_name }} + permissions: + id-token: write # Attestation + steps: + - uses: actions/download-artifact@v8 with: - name: wheels-macos + name: sdist path: dist/ - - uses: actions/download-artifact@v3 + - uses: actions/download-artifact@v8 with: - name: wheels-linux + pattern: wheels-* path: dist/ + merge-multiple: true - uses: pypa/gh-action-pypi-publish@release/v1 with: skip-existing: true diff --git a/.github/workflows/release-on-tag.yml b/.github/workflows/release-on-tag.yml index 95cf02d3..87e4f62a 100644 --- a/.github/workflows/release-on-tag.yml +++ b/.github/workflows/release-on-tag.yml @@ -5,21 +5,26 @@ on: name: Create Release +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: false + jobs: build: name: Create Release runs-on: ubuntu-latest + timeout-minutes: 5 permissions: - contents: write + contents: write # Publish GitHub Releases steps: - - name: Checkout code - uses: actions/checkout@v3 + - uses: actions/checkout@v7 + with: + persist-credentials: false - name: Generate body - run: | - scripts/get_release_description.py | tee release_body.md + run: scripts/get_release_description.py | tee release_body.md - name: Create Release id: create_release - uses: ncipollo/release-action@v1 - with: - name: "" - bodyFile: release_body.md + # https://cli.github.com/manual/gh_release_create + run: gh release create "${GITHUB_REF_NAME}" --verify-tag --notes-file release_body.md + env: + GH_TOKEN: ${{ github.token }} diff --git a/.github/zizmor.yaml b/.github/zizmor.yaml new file mode 100644 index 00000000..b247c2d3 --- /dev/null +++ b/.github/zizmor.yaml @@ -0,0 +1,7 @@ +rules: + anonymous-definition: + disable: true + excessive-permissions: + disable: true + unpinned-uses: + disable: true diff --git a/.gitignore b/.gitignore index 6ec1f7fb..28aabdcd 100644 --- a/.gitignore +++ b/.gitignore @@ -69,10 +69,10 @@ Release/ tcod/_*.c tcod/_*.pyd .benchmarks -dependencies/SDL2* +dependencies/SDL* tcod/x86 tcod/x64 -tcod/SDL2.framework +tcod/SDL?.framework deb_dist/ *.tar.gz tcod/version.py diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 54a9a88e..e435c5cc 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,8 +1,10 @@ # See https://pre-commit.com for more information # See https://pre-commit.com/hooks.html for more hooks +ci: + autoupdate_schedule: quarterly repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.4.0 + rev: v6.0.0 hooks: - id: trailing-whitespace - id: end-of-file-fixer @@ -14,13 +16,13 @@ repos: - id: debug-statements - id: fix-byte-order-marker - id: detect-private-key - - repo: https://github.com/psf/black - rev: 23.3.0 + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.15.20 hooks: - - id: black - - repo: https://github.com/pycqa/isort - rev: 5.12.0 + - id: ruff-check + args: [--fix-only, --exit-non-zero-on-fix] + - id: ruff-format + - repo: https://github.com/zizmorcore/zizmor-pre-commit + rev: v1.26.1 hooks: - - id: isort -default_language_version: - python: python3.11 + - id: zizmor diff --git a/.readthedocs.yaml b/.readthedocs.yaml index 81bb11ae..098d76db 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -5,11 +5,21 @@ version: 2 build: - os: ubuntu-22.04 + os: ubuntu-24.04 tools: python: "3.11" apt_packages: - - libsdl2-dev + - build-essential + - make + - pkg-config + - cmake + - ninja-build + jobs: + pre_install: + - git clone --depth 1 --branch release-3.2.16 https://github.com/libsdl-org/SDL.git sdl_repo + - cmake -S sdl_repo -B sdl_build -D CMAKE_INSTALL_PREFIX=~/.local + - cmake --build sdl_build --config Debug + - cmake --install sdl_build submodules: include: all diff --git a/.vscode/extensions.json b/.vscode/extensions.json index d589600c..a8a06ae2 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -3,16 +3,15 @@ // Extension identifier format: ${publisher}.${name}. Example: vscode.csharp // List of extensions which should be recommended for users of this workspace. "recommendations": [ - "austin.code-gnu-global", "editorconfig.editorconfig", "ms-python.python", - "ms-python.black-formatter", "ms-python.vscode-pylance", "ms-vscode.cpptools", "redhat.vscode-yaml", "streetsidesoftware.code-spell-checker", "tamasfe.even-better-toml", "xaver.clang-format", + "charliermarsh.ruff" ], // List of extensions recommended by VS Code that should not be recommended for users of this workspace. "unwantedRecommendations": [] diff --git a/.vscode/launch.json b/.vscode/launch.json index 0f237387..a4d5d9e7 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -6,24 +6,24 @@ "configurations": [ { "name": "Python: Current File", - "type": "python", + "type": "debugpy", "request": "launch", "program": "${file}", - "console": "internalConsole", + "console": "integratedTerminal", }, { // Run the Python samples. // tcod will be built and installed in editalbe mode. "name": "Python: Python samples", - "type": "python", + "type": "debugpy", "request": "launch", "program": "${workspaceFolder}/examples/samples_tcod.py", "cwd": "${workspaceFolder}/examples", - "console": "internalConsole", + "console": "integratedTerminal", }, { "name": "Python: Run tests", - "type": "python", + "type": "debugpy", "request": "launch", "module": "pytest", "preLaunchTask": "develop python-tcod", @@ -31,7 +31,7 @@ { "name": "Documentation: Launch Chrome", "request": "launch", - "type": "pwa-chrome", + "type": "chrome", "url": "file://${workspaceFolder}/docs/_build/html/index.html", "webRoot": "${workspaceFolder}", "preLaunchTask": "build documentation", @@ -39,7 +39,7 @@ { "name": "Documentation: Launch Edge", "request": "launch", - "type": "pwa-msedge", + "type": "msedge", "url": "file://${workspaceFolder}/docs/_build/html/index.html", "webRoot": "${workspaceFolder}", "preLaunchTask": "build documentation", diff --git a/.vscode/settings.json b/.vscode/settings.json index 7a02db40..10b92720 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -5,35 +5,35 @@ ], "editor.formatOnSave": true, "editor.codeActionsOnSave": { - "source.fixAll": true, - "source.organizeImports": false + "source.fixAll": "always", + "source.organizeImports": "never" }, + "cmake.configureOnOpen": false, "files.trimFinalNewlines": true, "files.insertFinalNewline": true, "files.trimTrailingWhitespace": true, - "python.linting.enabled": true, - "python.linting.flake8Enabled": false, - "python.linting.mypyEnabled": true, - "python.linting.mypyArgs": [ - "--follow-imports=silent", - "--show-column-numbers" - ], - "python.formatting.provider": "none", "files.associations": { - "*.spec": "python", + "*.spec": "python" }, + "mypy-type-checker.importStrategy": "fromEnvironment", "cSpell.words": [ + "aarch", "ADDA", "ADDALPHA", + "ADDEVENT", + "addoption", + "addopts", "addressof", "addsub", "addx", "addy", "algo", "ALPH", + "alsa", "ALTERASE", "arange", "ARCHS", + "arctan", "asarray", "ascontiguousarray", "astar", @@ -48,9 +48,11 @@ "AUDIOREWIND", "AUDIOSTOP", "autoclass", + "AUTOCORRECT", "autofunction", "autogenerated", "automodule", + "autoupdate", "backlinks", "bdist", "Benesch", @@ -72,16 +74,14 @@ "bysource", "caeldera", "CAPSLOCK", - "caxis", - "cbutton", "ccoef", "cdef", - "cdevice", "cffi", "cflags", "CFLAGS", "CHARMAP", "Chebyshev", + "choicelist", "cibuildwheel", "CIBW", "CLEARAGAIN", @@ -95,16 +95,20 @@ "Coef", "COLCTRL", "COMPILEDVERSION", + "condlist", "consolas", "contextdata", + "contextlib", "CONTROLLERAXISMOTION", "CONTROLLERBUTTONDOWN", "CONTROLLERBUTTONUP", "CONTROLLERDEVICEADDED", "CONTROLLERDEVICEREMAPPED", "CONTROLLERDEVICEREMOVED", + "cooldown", "cplusplus", "CPLUSPLUS", + "cpython", "CRSEL", "ctypes", "CURRENCYSUBUNIT", @@ -113,7 +117,9 @@ "dataclasses", "datas", "DBLAMPERSAND", + "DBLAPOSTROPHE", "DBLVERTICALBAR", + "dbus", "dcost", "DCROSS", "DECIMALSEPARATOR", @@ -126,7 +132,10 @@ "devel", "DHLINE", "DISPLAYSWITCH", + "distutils", "dlopen", + "docstrings", + "doctest", "documentclass", "Doryen", "DPAD", @@ -136,30 +145,44 @@ "DTEEW", "dtype", "dtypes", + "dunder", "DVLINE", "elif", + "Emscripten", + "EMSDK", + "ENDCALL", "endianness", + "epel", "epub", "EQUALSAS", "errorvf", "EXCLAM", "EXSEL", + "faulthandler", "favicon", "ffade", "fgcolor", "fheight", + "filterwarnings", + "Flecs", "flto", "fmean", "fontx", "fonty", + "freethreading", "freetype", "frombuffer", "fullscreen", "furo", "fwidth", "GAMECONTROLLER", + "gamedev", "gamepad", + "gaxis", + "gbutton", + "gdevice", "genindex", + "getbbox", "GFORCE", "GLES", "globaltoc", @@ -167,6 +190,7 @@ "greyscale", "groupwise", "guass", + "hasattr", "heapify", "heightmap", "heightmaps", @@ -181,16 +205,23 @@ "htmlhelp", "htmlzip", "IBEAM", + "ibus", + "ICCPROF", "ifdef", "ifndef", "iinfo", "IJKL", "imageio", + "imread", + "includepath", "INCOL", + "INPUTTYPE", "INROW", + "interactable", "intersphinx", "isinstance", "isort", + "issubdtype", "itemsize", "itleref", "ivar", @@ -201,6 +232,7 @@ "jhat", "jice", "jieba", + "jmccardle", "JOYAXISMOTION", "JOYBALLMOTION", "JOYBUTTONDOWN", @@ -242,9 +274,14 @@ "lerp", "letterpaper", "LGUI", + "LHYPER", + "libdrm", + "libgbm", "libsdl", "libtcod", "libtcodpy", + "libusb", + "libxkbcommon", "linspace", "liskin", "LMASK", @@ -272,11 +309,13 @@ "mgrid", "milli", "minmax", + "minversion", "mipmap", "mipmaps", "MMASK", "modindex", "moduleauthor", + "modversion", "MOUSEBUTTONDOWN", "MOUSEBUTTONUP", "MOUSEMOTION", @@ -297,6 +336,8 @@ "neww", "noarchive", "NODISCARD", + "NOLONGLONG", + "NOMESSAGE", "Nonrepresentable", "NONUSBACKSLASH", "NONUSHASH", @@ -307,15 +348,18 @@ "numpy", "ogrid", "ogrids", + "oldnames", "onefile", "OPENGL", "opensearch", "OPER", + "OVERSCAN", "packbits", "PAGEDOWN", "pagerefs", "PAGEUP", "papersize", + "passthru", "PATCHLEVEL", "pathfinding", "pathlib", @@ -323,27 +367,36 @@ "PERLIN", "PILCROW", "pilmode", + "PIXELART", "PIXELFORMAT", "PLUSMINUS", "pointsize", + "popleft", "PRESENTVSYNC", "PRINTF", "printn", "PRINTSCREEN", "propname", + "pulseaudio", + "pushdown", "pycall", "pycparser", + "pydocstyle", "pyinstaller", + "pyodide", "pypa", "PYPI", "pypiwin", "pypy", "pytest", + "pytestmark", + "PYTHONHASHSEED", "PYTHONOPTIMIZE", "Pyup", "quickstart", "QUOTEDBL", "RALT", + "randint", "randomizer", "rbutton", "RCTRL", @@ -352,8 +405,10 @@ "Redistributable", "redistributables", "repr", + "rexpaint", "rgba", "RGUI", + "RHYPER", "RIGHTBRACE", "RIGHTBRACKET", "RIGHTDOWN", @@ -365,17 +420,20 @@ "RMASK", "rmeta", "roguelike", + "roguelikedev", "rpath", "RRGGBB", "rtype", "RWIN", "RWOPS", + "SCALEMODE", "scalex", "scaley", "Scancode", "scancodes", "scipy", "scoef", + "Scrn", "SCROLLLOCK", "sdist", "SDL's", @@ -395,16 +453,23 @@ "SIZEWE", "SMILIE", "snprintf", + "SOFTLEFT", + "SOFTRIGHT", "soundfile", "sourcelink", "sphinxstrong", "sphinxtitleref", + "staticmethod", + "stdarg", + "stddef", "stdeb", "struct", "structs", + "subclassing", "SUBP", "SYSREQ", "tablefmt", + "Tamzen", "TARGETTEXTURE", "tcod", "tcoddoc", @@ -412,6 +477,8 @@ "TCODLIB", "TEEE", "TEEW", + "termbox", + "testpaths", "TEXTUREACCESS", "thirdparty", "THOUSANDSSEPARATOR", @@ -429,9 +496,11 @@ "TRIGGERRIGHT", "tris", "truetype", + "tryddle", "typestr", "undoc", "Unifont", + "UNISTD", "unraisable", "unraisablehook", "unraiseable", @@ -445,6 +514,7 @@ "VERTICALBAR", "vflip", "viewcode", + "VITAFILE", "vline", "VOLUMEDOWN", "VOLUMEUP", @@ -455,6 +525,7 @@ "WAITARROW", "WASD", "waterlevel", + "WINAPI", "windowclose", "windowenter", "WINDOWEVENT", @@ -473,19 +544,25 @@ "windowshown", "windowsizechanged", "windowtakefocus", + "xcframework", + "Xcursor", "xdst", + "Xext", + "Xfixes", + "Xrandr", "xrel", "xvfb", "ydst", - "yrel" + "yrel", + "zizmor" ], "python.testing.pytestArgs": [], "python.testing.unittestEnabled": false, "python.testing.pytestEnabled": true, "[python]": { - "editor.defaultFormatter": "ms-python.black-formatter" + "editor.defaultFormatter": "charliermarsh.ruff" }, - "cSpell.enableFiletypes": [ - "github-actions-workflow" - ] + "[html]": { + "editor.formatOnSave": false + } } diff --git a/CHANGELOG.md b/CHANGELOG.md index ad769aaf..9b128a56 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,1283 +1,2107 @@ # Changelog + Changes relevant to the users of python-tcod are documented here. This project adheres to [Semantic Versioning](https://semver.org/) since version `2.0.0`. ## [Unreleased] +## [21.2.1] - 2026-06-04 + +### Deployment + +- PyPy wheels switched from PyPy 3.10 to PyPy 3.11. +- Experimental Pyodide wheels are now uploaded to PyPI. + +### Fixed + +- `tcod.sdl.joystick.get_joysticks`, `get_controllers`, and related enumeration passed device indices to SDL3 functions expecting instance IDs, so enumeration could fail or open the wrong device after a joystick was reconnected. + +## [21.2.0] - 2026-04-04 + +### Added + +- `tcod.event.MouseWheel` now has `which`, `window_id`, `position` and `integer_position` attributes. + +## Fixed + +- `tcod.event.convert_coordinates_from_window` was not converting all types of mouse events. + +## [21.1.0] - 2026-04-04 + +### Added + +- `MouseButtonEvent.integer_position` property. + +## Fixed + +- `integer_position` was missing from mouse button events. + +## [21.0.0] - 2026-03-13 + +### Added + +- `tcod.sdl.video.Window` now accepts an SDL WindowID. +- `tcod.event`: + - `MouseState.integer_position` and `MouseMotion.integer_motion` to handle cases where integer values are preferred. + - `ClipboardUpdate` event. + - `Drop` event. + - `KeyboardEvent.pressed`, `KeyboardEvent.which`, `KeyboardEvent.window_id` attributes. + - `WindowEvent.data` and `WindowEvent.window_id` attributes and added missing SDL3 window events. + - `which` and `window_id` attributes for mouse events. + - Events now have `Event.timestamp` and `Event.timestamp_ns` which use SDL's timer at `tcod.event.time` and `tcod.event.time_ns`. + +### Changed + +- Event classes are now more strict with attribute types +- Event class initializers are keyword-only and no longer take a type parameter, with exceptions. + Generally event class initialization is an internal process. +- `MouseButtonEvent` no longer a subclass of `MouseState`. +- `tcod.event.Point` is now a generic type containing `int` or `float` values depending on the context. +- When converting mouse events to tiles: + `MouseState.position` and `MouseMotion.motion` refers to sub-tile coordinates. + `MouseState.integer_position` and `MouseMotion.integer_motion` refers to integer tile coordinates. + +### Deprecated + +- `Event.type` is deprecated except for special cases such as `ControllerDevice`, `WindowEvent`, etc. +- `MouseButtonEvent.state` is deprecated, replaced by the existing `.button` attribute. + +### Fixed + +- Fixed incorrect C FFI types inside `tcod.event.get_mouse_state`. +- Fixed regression in mouse event tile coordinates being `float` instead of `int`. + `convert_coordinates_from_window` can be used if sub-tile coordinates were desired. +- Fixed regression in `libtcodpy.bsp_split_recursive` not accepting `0`. + +## [20.1.0] - 2026-02-25 + +### Added + +- `Tileset` now supports `MutableMapping` semantics. + Can get, set, or iterate over tiles as if it were a dictionary containing tile glyph arrays. + Also supports `+`, `|`, `+=`, and `|=` with other tilesets or mappings to merge them into a single Tileset. +- `tcod.tileset.procedural_block_elements` can take a tile shape and return a tileset. + +### Deprecated + +- `Tileset.set_tile(codepoint, tile)` was replaced with `tileset[codepoint] = tile` syntax. +- `Tileset.get_tile(codepoint)` was soft replaced with `tileset[codepoint]` syntax. +- `tcod.tileset.procedural_block_elements` should be used with dictionary semantics instead of passing in a tileset. + +## [20.0.0] - 2026-02-06 + +### Added + +- Now supports free-threaded Python, deploys with `cp314t` wheels. +- Added methods: `Renderer.coordinates_from_window` and `Renderer.coordinates_to_window` +- Added `tcod.event.convert_coordinates_from_window`. + +### Changed + +- `Renderer.logical_size` now returns `None` instead of `(0, 0)` when logical size is unset. + +## [19.6.3] - 2026-01-12 + +Fix missing deployment + +## [19.6.2] - 2026-01-12 + +### Changed + +- Update to libtcod 2.2.2 + +### Fixed + +- Mouse coordinate to tile conversions now support SDL renderer logical size and scaling. + +## [19.6.1] - 2025-12-15 + +### Fixed + +- `tcod.event.add_watch` was crashing due to a cdef type mismatch. + +## [19.6.0] - 2025-10-20 + +### Added + +- Alternative syntax for number symbols with `KeySym`, can now specify `KeySym["3"]`, etc. + Only available on Python 3.13 or later. + +### Fixed + +- Fixed regression with lowercase key symbols with `tcod.event.K_*` and `KeySym.*` constants, these are still deprecated. + Event constants are only fixed for `tcod.event.K_*`, not the undocumented `tcod.event_constants` module. + Lowercase `KeySym.*` constants are only available on Python 3.13 or later. +- `BSP.split_recursive` did not accept a `Random` class as the seed. #168 + +## [19.5.0] - 2025-09-13 + +### Changed + +- Update to libtcod 2.2.1. +- Scaling defaults to nearest, set `os.environ["SDL_RENDER_SCALE_QUALITY"] = "linear"` if linear scaling was preferred. + +### Fixed + +- `SDL_RENDER_SCALE_QUALITY` is now respected again since the change to SDL3. +- Fixed crash on controller events. + +## [19.4.1] - 2025-08-27 + +### Fixed + +- Fixed dangling pointer in `Pathfinder.clear` method. +- Fixed hang in `Pathfinder.rebuild_frontier` method. + +## [19.4.0] - 2025-08-06 + +### Changed + +- Checking "WindowSizeChanged" was not valid since SDL 3 and was also not valid in previous examples. + You must no longer check the type of the `WindowResized` event. + +### Fixed + +- Corrected some inconsistent angle brackets in the `__str__` of Event subclasses. #165 +- Fix regression with window events causing them to be `Unknown` and uncheckable. + +## [19.3.1] - 2025-08-02 + +Solved a deprecation warning which was internal to tcod and no doubt annoyed many devs. +Thanks to jmccardle for forcing me to resolve this. + +### Fixed + +- Silenced internal deprecation warnings within `Context.convert_event`. + +## [19.3.0] - 2025-07-26 + +Thanks to cr0ne for pointing out missing texture scaling options. +These options did not exist in SDL2. + +### Added + +- `tcod.sdl.render`: Added `ScaleMode` enum and `Texture.scale_mode` attribute. + +## [19.2.0] - 2025-07-20 + +Thanks to tryddle for demonstrating how bad the current API was with chunked world generation. + +### Added + +- `tcod.noise.grid` now has the `offset` parameter for easier sampling of noise chunks. + +## [19.1.0] - 2025-07-12 + +### Added + +- Added text input support to `tcod.sdl.video.Window` which was missing since the SDL3 update. + After creating a context use `assert context.sdl_window` or `if context.sdl_window:` to verify that an SDL window exists then use `context.sdl_window.start_text_input` to enable text input events. + Keep in mind that this can open an on-screen keyboard. + +## [19.0.2] - 2025-07-11 + +Resolve wheel deployment issue. + +## [19.0.1] - 2025-07-11 + +### Fixed + +- `Console.print` methods using `string` keyword were marked as invalid instead of deprecated. + +## [19.0.0] - 2025-06-13 + +Finished port to SDL3, this has caused several breaking changes from SDL such as lowercase key constants now being uppercase and mouse events returning `float` instead of `int`. +Be sure to run [Mypy](https://mypy.readthedocs.io/en/stable/getting_started.html) on your projects to catch any issues from this update. + +### Changed + +- Updated libtcod to 2.1.1 +- Updated SDL to 3.2.16 + This will cause several breaking changes such as the names of keyboard constants and other SDL enums. +- `tcod.sdl.video.Window.grab` has been split into `.mouse_grab` and `.keyboard_grab` attributes. +- `tcod.event.KeySym` single letter symbols are now all uppercase. +- Relative mouse mode is set via `tcod.sdl.video.Window.relative_mouse_mode` instead of `tcod.sdl.mouse.set_relative_mode`. +- `tcod.sdl.render.new_renderer`: Removed `software` and `target_textures` parameters, `vsync` takes `int`, `driver` takes `str` instead of `int`. +- `tcod.sdl.render.Renderer`: `integer_scaling` and `logical_size` are now set with `set_logical_presentation` method. +- `tcod.sdl.render.Renderer.geometry` now takes float values for `color` instead of 8-bit integers. +- `tcod.event.Point` and other mouse/tile coordinate types now use `float` instead of `int`. + SDL3 has decided that mouse events have subpixel precision. + If you see any usual `float` types in your code then this is why. +- `tcod.sdl.audio` has been affected by major changes to SDL3. + - `tcod.sdl.audio.open` has new behavior due to SDL3 and should be avoided. + - Callbacks which were assigned to `AudioDevice`'s must now be applied to `AudioStream`'s instead. + - `AudioDevice`'s are now opened using references to existing devices. + - Sound queueing methods were moved from `AudioDevice` to a new `AudioStream` class. + - `BasicMixer` may require manually specifying `frequency` and `channels` to replicate old behavior. + - `get_devices` and `get_capture_devices` now return `dict[str, AudioDevice]`. +- `TextInput` events are no longer enabled by default. + +### Deprecated + +- `tcod.sdl.audio.open` was replaced with a newer API, get a default device with `tcod.sdl.audio.get_default_playback().open()`. +- `tcod.sdl.audio.BasicMixer` should be replaced with `AudioStream`'s. +- Should no longer use `tcod.sdl.audio.AudioDevice` in a context, use `contextlib.closing` for the old behavior. + +### Removed + +- Support dropped for Python 3.8 and 3.9. +- Removed `Joystick.get_current_power` due to SDL3 changes. +- `WindowFlags.FULLSCREEN_DESKTOP` is now just `WindowFlags.FULLSCREEN` +- `tcod.sdl.render.Renderer.integer_scaling` removed. +- Removed `callback`, `spec`, `queued_samples`, `queue_audio`, and `dequeue_audio` attributes from `tcod.sdl.audio.AudioDevice`. +- `tcod.event.WindowResized`: `type="WindowSizeChanged"` removed and must no longer be checked for. + `EventDispatch.ev_windowsizechanged` is no longer called. + +### Fixed + +- `Joystick.get_ball` was broken. + +## [18.1.0] - 2025-05-05 + +### Added + +- `tcod.path.path2d` to compute paths for the most basic cases. + +### Fixed + +- `tcod.noise.grid` would raise `TypeError` when given a plain integer for scale. + +## [18.0.0] - 2025-04-08 + +### Changed + +- `Console.print` now accepts `height` and `width` keywords and has renamed `string` to `text`. +- Text printed with `Console.print` using right-alignment has been shifted to the left by 1-tile. + +### Deprecated + +- In general the `fg`, `bg`, and `bg_blend` keywords are too hard to keep track of as positional arguments so they must be replaced with keyword arguments instead. +- `Console.print`: deprecated `string`, `fg`, `bg`, and `bg_blend` being given as positional arguments. + The `string` parameter has been renamed to `text`. +- `Console.print_box` has been replaced by `Console.print`. +- `Console.draw_frame`: deprecated `clear`, `fg`, `bg`, and `bg_blend` being given as positional arguments. +- `Console.draw_rect`: deprecated `fg`, `bg`, and `bg_blend` being given as positional arguments. +- The `EventDispatch` class is now deprecated. + This class was made before Python supported protocols and structural pattern matching, + now the class serves little purpose and its usage can create a minor technical burden. + +## [17.1.0] - 2025-03-29 + +### Added + +- SDL renderer primitive drawing methods now support sequences of tuples. + +### Fixed + +- `tcod.sdl.Renderer.draw_lines` type hint was too narrow. +- Fixed crash in `tcod.sdl.Renderer.geometry`. + +## [17.0.0] - 2025-03-28 + +### Changed + +- `EventDispatch`'s on event methods are now defined as positional parameters, so renaming the `event` parameter is now valid in subclasses. + +### Deprecated + +- Keyboard bitmask modifiers `tcod.event.KMOD_*` have been replaced by `tcod.event.Modifier`. + +### Fixed + +- Suppressed internal `mouse.tile_motion` deprecation warning. +- Fixed SDL renderer primitive drawing methods. #159 + +## [16.2.3] - 2024-07-16 + +### Fixed + +- Fixed access violation when events are polled before SDL is initialized. +- Fixed access violation when libtcod images fail to load. +- Verify input files exist when calling `libtcodpy.parser_run`, `libtcodpy.namegen_parse`, `tcod.image.load`. + +## [16.2.2] - 2024-01-16 + +### Fixed + +- Ignore the locale when encoding file paths outside of Windows. +- Fix performance when calling joystick functions. + +## [16.2.1] - 2023-09-24 + +### Fixed + +- Fixed errors loading files on Windows where their paths are non-ASCII and the locale is not UTF-8. + +## [16.2.0] - 2023-09-20 + +### Changed + +- Renamed `gauss` methods to fix typos. + ## [16.1.1] - 2023-07-10 + ### Changed + - Added an empty `__slots__` to `EventDispatch`. - Bundle `SDL 2.28.1` on Windows and MacOS. ### Fixed + - Fixed "SDL failed to get a vertex buffer for this Direct3D 9 rendering batch!" https://github.com/libtcod/python-tcod/issues/131 ### Removed + - Dropped support for Python 3.7. ## [16.1.0] - 2023-06-23 + ### Added + - Added the enums `tcod.event.MouseButton` and `tcod.event.MouseButtonMask`. ### Changed + - Using `libtcod 1.24.0`. ### Deprecated + - Mouse button and mask constants have been replaced by enums. ### Fixed + - `WindowResized` literal annotations were in the wrong case. ## [16.0.3] - 2023-06-04 + ### Changed + - Enabled logging for libtcod and SDL. ### Deprecated + - Deprecated using `tcod` as an implicit alias for `libtcodpy`. You should use `from tcod import libtcodpy` if you want to access this module. - Deprecated constants being held directly in `tcod`, get these from `tcod.libtcodpy` instead. - Deprecated `tcod.Console` which should be accessed from `tcod.console.Console` instead. ## [16.0.2] - 2023-06-02 + ### Fixed + - Joystick/controller device events would raise `RuntimeError` when accessed after removal. ## [16.0.1] - 2023-05-28 + ### Fixed + - `AudioDevice.stopped` was inverted. - Fixed the audio mixer stop and fadeout methods. - Exceptions raised in the audio mixer callback no longer cause a messy crash, they now go to `sys.unraisablehook`. ## [16.0.0] - 2023-05-27 + ### Added + - Added PathLike support to more libtcodpy functions. - New `tcod.sdl.mouse.show` function for querying or setting mouse visibility. -- New class method `tcod.image.Image.from_file` to load images with. This replaces `tcod.image_load`. +- New class method `tcod.image.Image.from_file` to load images with. This replaces `tcod.image_load`. - `tcod.sdl.audio.AudioDevice` is now a context manager. ### Changed + - SDL audio conversion will now pass unconvertible floating types as float32 instead of raising. ### Deprecated + - Deprecated the libtcodpy functions for images and noise generators. ### Removed + - `tcod.console_set_custom_font` can no longer take bytes as the file path. ### Fixed + - Fix `tcod.sdl.mouse.warp_in_window` function. - Fix `TypeError: '_AudioCallbackUserdata' object is not callable` when using an SDL audio device callback. [#128](https://github.com/libtcod/python-tcod/issues/128) ## [15.0.3] - 2023-05-25 + ### Deprecated -- Deprecated all libtcod color constants. Replace these with your own manually defined colors. + +- Deprecated all libtcod color constants. Replace these with your own manually defined colors. Using a color will tell you the color values of the deprecated color in the warning. -- Deprecated older scancode and keysym constants. These were replaced with the Scancode and KeySym enums. +- Deprecated older scancode and keysym constants. These were replaced with the Scancode and KeySym enums. ### Fixed + - DLL loader could fail to load `SDL2.dll` when other tcod namespace packages were installed. ## [15.0.1] - 2023-03-30 + ### Added + - Added support for `tcod.sdl` namespace packages. ### Fixed -- ``Renderer.read_pixels`` method was completely broken. + +- `Renderer.read_pixels` method was completely broken. ## [15.0.0] - 2023-01-04 + ### Changed + - Modified the letter case of window event types to match their type annotations. - This may cause regressions. Run Mypy to check for ``[comparison-overlap]`` errors. -- Mouse event attributes have been changed ``.pixel -> .position`` and ``.pixel_motion -> .motion``. + This may cause regressions. Run Mypy to check for `[comparison-overlap]` errors. +- Mouse event attributes have been changed `.pixel -> .position` and `.pixel_motion -> .motion`. - `Context.convert_event` now returns copies of events with mouse coordinates converted into tile positions. ### Deprecated + - Mouse event pixel and tile attributes have been deprecated. ## [14.0.0] - 2022-12-09 + ### Added + - Added explicit support for namespace packages. ### Changed + - Using `libtcod 1.23.1`. - Bundle `SDL 2.26.0` on Windows and MacOS. - Code Page 437: Character 0x7F is now assigned to 0x2302 (HOUSE). -- Forced all renderers to ``RENDERER_SDL2`` to fix rare graphical artifacts with OpenGL. +- Forced all renderers to `RENDERER_SDL2` to fix rare graphical artifacts with OpenGL. ### Deprecated + - The `renderer` parameter of new contexts is now deprecated. ## [13.8.1] - 2022-09-23 + ### Fixed + - `EventDispatch` was missing new event names. ## [13.8.0] - 2022-09-22 + ### Added + - Ported SDL2 joystick handing as `tcod.sdl.joystick`. - New joystick related events. ### Changed + - Using `libtcod 1.22.3`. - Bundle `SDL 2.24.0` on Windows and MacOS. ### Deprecated + - Renderers other than `tcod.RENDERER_SDL2` are now discouraged. ### Fixed + - Fixed double present bug in non-context flush functions. This was affecting performance and also caused a screen flicker whenever the global fade color was active. - Fixed the parsing of SDL 2.24.0 headers on Windows. ## [13.7.0] - 2022-08-07 + ### Added + - You can new use `SDLConsoleRender.atlas` to access the `SDLTilesetAtlas` used to create it. [#121](https://github.com/libtcod/python-tcod/issues/121) ### Fixed -- Fixed the parsing of SDL 2.0.22 headers. Specifically `SDL_FLT_EPSILON`. + +- Fixed the parsing of SDL 2.0.22 headers. Specifically `SDL_FLT_EPSILON`. ## [13.6.2] - 2022-05-02 + ### Fixed + - SDL renderers were ignoring tiles where only the background red channel was changed. ## [13.6.1] - 2022-03-29 + ### Changed + - The SDL2 renderer has had a major performance update when compiled with SDL 2.0.18. - SDL2 is now the default renderer to avoid rare issues with the OpenGL 2 renderer. ## [13.6.0] - 2022-02-19 + ### Added -- `BasicMixer` and `Channel` classes added to `tcod.sdl.audio`. These handle simple audio mixing. + +- `BasicMixer` and `Channel` classes added to `tcod.sdl.audio`. These handle simple audio mixing. - `AudioDevice.convert` added to handle simple conversions to the active devices format. - `tcod.sdl.audio.convert_audio` added to handle any other conversions needed. ## [13.5.0] - 2022-02-11 + ### Added -- `tcod.sdl.audio`, a new module exposing SDL audio devices. This does not include an audio mixer yet. + +- `tcod.sdl.audio`, a new module exposing SDL audio devices. This does not include an audio mixer yet. - `tcod.sdl.mouse`, for SDL mouse and cursor handing. - `Context.sdl_atlas`, which provides the relevant `SDLTilesetAtlas` when one is being used by the context. - Several missing features were added to `tcod.sdl.render`. - `Window.mouse_rect` added to SDL windows to set the mouse confinement area. + ### Changed + - `Texture.access` and `Texture.blend_mode` properties now return enum instances. You can still set `blend_mode` with `int` but Mypy will complain. ## [13.4.0] - 2022-02-04 + ### Added + - Adds `sdl_window` and `sdl_renderer` properties to tcod contexts. - Adds `tcod.event.add_watch` and `tcod.event.remove_watch` to handle SDL events via callback. - Adds the `tcod.sdl.video` module to handle SDL windows. - Adds the `tcod.sdl.render` module to handle SDL renderers. - Adds the `tcod.render` module which gives more control over the rendering of consoles and tilesets. + ### Fixed + - Fixed handling of non-Path PathLike parameters and filepath encodings. ## [13.3.0] - 2022-01-07 + ### Added + - New experimental renderer `tcod.context.RENDERER_XTERM`. + ### Changed + - Using `libtcod 1.20.1`. + ### Fixed + - Functions accepting `Path`-like parameters now accept the more correct `os.PathLike` type. - BDF files with blank lines no longer fail to load with an "Unknown keyword" error. ## [13.2.0] - 2021-12-24 + ### Added + - New `console` parameter in `tcod.context.new` which sets parameters from an existing Console. ### Changed + - Using `libtcod 1.20.0`. ### Fixed + - Fixed segfault when an OpenGL2 context fails to load. - Gaussian number generation no longer affects the results of unrelated RNG's. - Gaussian number generation is now reentrant and thread-safe. - Fixed potential crash in PNG image loading. ## [13.1.0] - 2021-10-22 + ### Added + - Added the `tcod.tileset.procedural_block_elements` function. ### Removed + - Python 3.6 is no longer supported. ## [13.0.0] - 2021-09-20 + ### Changed + - Console print and drawing functions now always use absolute coordinates for negative numbers. ## [12.7.3] - 2021-08-13 + ### Deprecated + - `tcod.console_is_key_pressed` was replaced with `tcod.event.get_keyboard_state`. - `tcod.console_from_file` is deprecated. - The `.asc` and `.apf` formats are no longer actively supported. ### Fixed + - Fixed the parsing of SDL 2.0.16 headers. ## [12.7.2] - 2021-07-01 + ### Fixed -- *Scancode* and *KeySym* enums no longer crash when SDL returns an unexpected value. + +- _Scancode_ and _KeySym_ enums no longer crash when SDL returns an unexpected value. ## [12.7.1] - 2021-06-30 + ### Added + - Started uploading wheels for ARM64 macOS. ## [12.7.0] - 2021-06-29 + ### Added -- *tcod.image* and *tcod.tileset* now support *pathlib*. + +- _tcod.image_ and _tcod.tileset_ now support _pathlib_. ### Fixed + - Wheels for 32-bit Windows now deploy again. ## [12.6.2] - 2021-06-15 + ### Fixed + - Git is no longer required to install from source. ## [12.6.1] - 2021-06-09 + ### Fixed + - Fixed version mismatch when building from sources. ## [12.6.0] - 2021-06-09 + ### Added -- Added the *decoration* parameter to *Console.draw_frame*. - You may use this parameter to designate custom glyphs as the frame border. + +- Added the _decoration_ parameter to _Console.draw_frame_. + You may use this parameter to designate custom glyphs as the frame border. ### Deprecated + - The handling of negative indexes given to console drawing and printing - functions will be changed to be used as absolute coordinates in the future. + functions will be changed to be used as absolute coordinates in the future. ## [12.5.1] - 2021-05-30 + ### Fixed + - The setup script should no longer fail silently when cffi is unavailable. ## [12.5.0] - 2021-05-21 + ### Changed + - `KeyboardEvent`'s '`scancode`, `sym`, and `mod` attributes now use their respective enums. ## [12.4.0] - 2021-05-21 + ### Added + - Added modernized REXPaint saving/loading functions. - - `tcod.console.load_xp` - - `tcod.console.save_xp` + - `tcod.console.load_xp` + - `tcod.console.save_xp` ### Changed + - Using `libtcod 1.18.1`. - `tcod.event.KeySym` and `tcod.event.Scancode` can now be hashed. ## [12.3.2] - 2021-05-15 + ### Changed + - Using `libtcod 1.17.1`. ### Fixed + - Fixed regression with loading PNG images. ## [12.3.1] - 2021-05-13 + ### Fixed + - Fix Windows deployment. ## [12.3.0] - 2021-05-13 + ### Added + - New keyboard enums: - - `tcod.event.KeySym` - - `tcod.event.Scancode` - - `tcod.event.Modifier` + - `tcod.event.KeySym` + - `tcod.event.Scancode` + - `tcod.event.Modifier` - New functions: - - `tcod.event.get_keyboard_state` - - `tcod.event.get_modifier_state` + - `tcod.event.get_keyboard_state` + - `tcod.event.get_modifier_state` - Added `tcod.console.rgb_graphic` and `tcod.console.rgba_graphic` dtypes. - Another name for the Console array attributes: `Console.rgb` and `Console.rgba`. ### Changed + - Using `libtcod 1.17.0`. ### Deprecated + - `Console_tiles_rgb` is being renamed to `Console.rgb`. - `Console_tiles` being renamed to `Console.rgba`. ### Fixed + - Contexts now give a more useful error when pickled. - Fixed regressions with `tcod.console_print_frame` and `Console.print_frame` - when given empty strings as the banner. + when given empty strings as the banner. ## [12.2.0] - 2021-04-09 + ### Added + - Added `tcod.noise.Algorithm` and `tcod.noise.Implementation` enums. - Added `tcod.noise.grid` helper function. ### Deprecated + - The non-enum noise implementation names have been deprecated. ### Fixed + - Indexing Noise classes now works with the FBM implementation. ## [12.1.0] - 2021-04-01 + ### Added + - Added package-level PyInstaller hook. ### Changed + - Using `libtcod 1.16.7`. - `tcod.path.dijkstra2d` now returns the output and accepts an `out` parameter. ### Deprecated -- In the future `tcod.path.dijkstra2d` will no longer modify the input by default. Until then an `out` parameter must be given. + +- In the future `tcod.path.dijkstra2d` will no longer modify the input by default. Until then an `out` parameter must be given. ### Fixed + - Fixed crashes from loading tilesets with non-square tile sizes. - Tilesets with a size of 0 should no longer crash when used. - Prevent division by zero from recommended-console-size functions. ## [12.0.0] - 2021-03-05 + ### Added + - Now includes PyInstaller hooks within the package itself. ### Deprecated + - The Random class will now warn if the seed it's given will not used - deterministically. It will no longer accept non-integer seeds in the future. + deterministically. It will no longer accept non-integer seeds in the future. ### Changed + - Now bundles SDL 2.0.14 for MacOS. - `tcod.event` can now detect and will warn about uninitialized tile - attributes on mouse events. + attributes on mouse events. ### Removed + - Python 3.5 is no longer supported. - The `tdl` module has been dropped. ## [11.19.3] - 2021-01-07 + ### Fixed + - Some wheels had broken version metadata. ## [11.19.2] - 2020-12-30 + ### Changed + - Now bundles SDL 2.0.10 for MacOS and SDL 2.0.14 for Windows. ### Fixed + - MacOS wheels were failing to bundle dependencies for SDL2. ## [11.19.1] - 2020-12-29 + ### Fixed + - MacOS wheels failed to deploy for the previous version. ## [11.19.0] - 2020-12-29 + ### Added + - Added the important `order` parameter to `Context.new_console`. ## [11.18.3] - 2020-12-28 + ### Changed + - Now bundles SDL 2.0.14 for Windows/MacOS. ### Deprecated + - Support for Python 3.5 will be dropped. - `tcod.console_load_xp` has been deprecated, `tcod.console_from_xp` can load - these files without modifying an existing console. + these files without modifying an existing console. ### Fixed + - `tcod.console_from_xp` now has better error handling (instead of crashing.) - Can now compile with SDL 2.0.14 headers. ## [11.18.2] - 2020-12-03 + ### Fixed + - Fixed missing `tcod.FOV_SYMMETRIC_SHADOWCAST` constant. -- Fixed regression in `tcod.sys_get_current_resolution` behavior. This - function now returns the monitor resolution as was previously expected. +- Fixed regression in `tcod.sys_get_current_resolution` behavior. This + function now returns the monitor resolution as was previously expected. ## [11.18.1] - 2020-11-30 + ### Fixed + - Code points from the Private Use Area will now print correctly. ## [11.18.0] - 2020-11-13 + ### Added + - New context method `Context.new_console`. ### Changed + - Using `libtcod 1.16.0-alpha.15`. ## [11.17.0] - 2020-10-30 + ### Added + - New FOV implementation: `tcod.FOV_SYMMETRIC_SHADOWCAST`. ### Changed + - Using `libtcod 1.16.0-alpha.14`. ## [11.16.1] - 2020-10-28 + ### Deprecated + - Changed context deprecations to PendingDeprecationWarning to reduce mass - panic from tutorial followers. + panic from tutorial followers. ### Fixed + - Fixed garbled titles and crashing on some platforms. ## [11.16.0] - 2020-10-23 + ### Added + - Added `tcod.context.new` function. - Contexts now support a CLI. - You can now provide the window x,y position when making contexts. - `tcod.noise.Noise` instances can now be indexed to generate noise maps. ### Changed + - Using `libtcod 1.16.0-alpha.13`. - The OpenGL 2 renderer can now use `SDL_HINT_RENDER_SCALE_QUALITY` to - determine the tileset upscaling filter. + determine the tileset upscaling filter. - Improved performance of the FOV_BASIC algorithm. ### Deprecated + - `tcod.context.new_window` and `tcod.context.new_terminal` have been replaced - by `tcod.context.new`. + by `tcod.context.new`. ### Fixed + - Pathfinders will now work with boolean arrays. - Console blits now ignore alpha compositing which would result in division by - zero. + zero. - `tcod.console_is_key_pressed` should work even if libtcod events are ignored. - The `TCOD_RENDERER` and `TCOD_VSYNC` environment variables should work now. - `FOV_PERMISSIVE` algorithm is now reentrant. ## [11.15.3] - 2020-07-30 + ### Fixed + - `tcod.tileset.Tileset.remap`, codepoint and index were swapped. ## [11.15.2] - 2020-07-27 + ### Fixed + - `tcod.path.dijkstra2d`, fixed corrupted output with int8 arrays. ## [11.15.1] - 2020-07-26 + ### Changed + - `tcod.event.EventDispatch` now uses the absolute names for event type hints - so that IDE's can better auto-complete method overrides. + so that IDE's can better auto-complete method overrides. ### Fixed + - Fixed libtcodpy heightmap data alignment issues on non-square maps. ## [11.15.0] - 2020-06-29 + ### Added + - `tcod.path.SimpleGraph` for pathfinding on simple 2D arrays. ### Changed + - `tcod.path.CustomGraph` now accepts an `order` parameter. ## [11.14.0] - 2020-06-23 + ### Added + - New `tcod.los` module for NumPy-based line-of-sight algorithms. - Includes `tcod.los.bresenham`. + Includes `tcod.los.bresenham`. ### Deprecated + - `tcod.line_where` and `tcod.line_iter` have been deprecated. ## [11.13.6] - 2020-06-19 + ### Deprecated + - `console_init_root` and `console_set_custom_font` have been replaced by the - modern API. + modern API. - All functions which handle SDL windows without a context are deprecated. - All functions which modify a globally active tileset are deprecated. - `tcod.map.Map` is deprecated, NumPy arrays should be passed to functions - directly instead of through this class. + directly instead of through this class. ## [11.13.5] - 2020-06-15 + ### Fixed + - Install requirements will no longer try to downgrade `cffi`. ## [11.13.4] - 2020-06-15 ## [11.13.3] - 2020-06-13 + ### Fixed + - `cffi` requirement has been updated to version `1.13.0`. - The older versions raise TypeError's. + The older versions raise TypeError's. ## [11.13.2] - 2020-06-12 + ### Fixed + - SDL related errors during package installation are now more readable. ## [11.13.1] - 2020-05-30 + ### Fixed + - `tcod.event.EventDispatch`: `ev_*` methods now allow `Optional[T]` return - types. + types. ## [11.13.0] - 2020-05-22 + ### Added + - `tcod.path`: New `Pathfinder` and `CustomGraph` classes. ### Changed + - Added `edge_map` parameter to `tcod.path.dijkstra2d` and - `tcod.path.hillclimb2d`. + `tcod.path.hillclimb2d`. ### Fixed + - tcod.console_init_root` and context initializing functions were not - raising exceptions on failure. + raising exceptions on failure. ## [11.12.1] - 2020-05-02 + ### Fixed + - Prevent adding non-existent 2nd halves to potential double-wide charterers. ## [11.12.0] - 2020-04-30 + ### Added -- Added `tcod.context` module. You now have more options for making libtcod - controlled contexts. + +- Added `tcod.context` module. You now have more options for making libtcod + controlled contexts. - `tcod.tileset.load_tilesheet`: Load a simple tilesheet as a Tileset. - `Tileset.remap`: Reassign codepoints to tiles on a Tileset. - `tcod.tileset.CHARMAP_CP437`: Character mapping for `load_tilesheet`. - `tcod.tileset.CHARMAP_TCOD`: Older libtcod layout. ### Changed + - `EventDispatch.dispatch` can now return the values returned by the `ev_*` - methods. The class is now generic to support type checking these values. + methods. The class is now generic to support type checking these values. - Event mouse coordinates are now strictly int types. - Submodules are now implicitly imported. ## [11.11.4] - 2020-04-26 + ### Changed + - Using `libtcod 1.16.0-alpha.10`. ### Fixed + - Fixed characters being dropped when color codes were used. ## [11.11.3] - 2020-04-24 + ### Changed + - Using `libtcod 1.16.0-alpha.9`. ### Fixed + - `FOV_DIAMOND` and `FOV_RESTRICTIVE` algorithms are now reentrant. - [libtcod#48](https://github.com/libtcod/libtcod/pull/48) + [libtcod#48](https://github.com/libtcod/libtcod/pull/48) - The `TCOD_VSYNC` environment variable was being ignored. ## [11.11.2] - 2020-04-22 ## [11.11.1] - 2020-04-03 + ### Changed + - Using `libtcod 1.16.0-alpha.8`. ### Fixed + - Changing the active tileset now redraws tiles correctly on the next frame. ## [11.11.0] - 2020-04-02 + ### Added + - Added `Console.close` as a more obvious way to close the active window of a - root console. + root console. ### Changed + - GCC is no longer needed to compile the library on Windows. - Using `libtcod 1.16.0-alpha.7`. - `tcod.console_flush` will now accept an RGB tuple as a `clear_color`. ### Fixed + - Changing the active tileset will now properly show it on the next render. ## [11.10.0] - 2020-03-26 + ### Added + - Added `tcod.tileset.load_bdf`, you can now load BDF fonts. - `tcod.tileset.set_default` and `tcod.tileset.get_default` are now stable. ### Changed + - Using `libtcod 1.16.0-alpha.6`. ### Deprecated + - The `snap_to_integer` parameter in `tcod.console_flush` has been deprecated - since it can cause minor scaling issues which don't exist when using - `integer_scaling` instead. + since it can cause minor scaling issues which don't exist when using + `integer_scaling` instead. ## [11.9.2] - 2020-03-17 + ### Fixed + - Fixed segfault after the Tileset returned by `tcod.tileset.get_default` goes - out of scope. + out of scope. ## [11.9.1] - 2020-02-28 + ### Changed + - Using `libtcod 1.16.0-alpha.5`. - Mouse tile coordinates are now always zero before the first call to - `tcod.console_flush`. + `tcod.console_flush`. ## [11.9.0] - 2020-02-22 + ### Added + - New method `Tileset.render` renders an RGBA NumPy array from a tileset and - a console. + a console. ## [11.8.2] - 2020-02-22 + ### Fixed + - Prevent KeyError when representing unusual keyboard symbol constants. ## [11.8.1] - 2020-02-22 + ### Changed + - Using `libtcod 1.16.0-alpha.4`. ### Fixed + - Mouse tile coordinates are now correct on any resized window. ## [11.8.0] - 2020-02-21 + ### Added + - Added `tcod.console.recommended_size` for when you want to change your main - console size at runtime. + console size at runtime. - Added `Console.tiles_rgb` as a replacement for `Console.tiles2`. ### Changed + - Using `libtcod 1.16.0-alpha.3`. - Added parameters to `tcod.console_flush`, you can now manually provide a - console and adjust how it is presented. + console and adjust how it is presented. ### Deprecated + - `Console.tiles2` is deprecated in favour of `Console.tiles_rgb`. - `Console.buffer` is now deprecated in favour of `Console.tiles`, instead of - the other way around. + the other way around. ### Fixed + - Fixed keyboard state and mouse state functions losing state when events were - flushed. + flushed. ## [11.7.2] - 2020-02-16 + ### Fixed + - Fixed regression in `tcod.console_clear`. ## [11.7.1] - 2020-02-16 + ### Fixed + - Fixed regression in `Console.draw_frame`. - The wavelet noise generator now excludes -1.0f and 1.0f as return values. - Fixed console fading color regression. ## [11.7.0] - 2020-02-14 + ### Changed + - Using `libtcod 1.16.0-alpha.2`. - When a renderer fails to load it will now fallback to a different one. - The order is: OPENGL2 -> OPENGL -> SDL2. + The order is: OPENGL2 -> OPENGL -> SDL2. - The default renderer is now SDL2. - The SDL and OPENGL renderers are no longer deprecated, but they now point to - slightly different backward compatible implementations. + slightly different backward compatible implementations. ### Deprecated + - The use of `libtcod.cfg` and `terminal.png` is deprecated. ### Fixed + - `tcod.sys_update_char` now works with the newer renderers. - Fixed buffer overflow in name generator. - `tcod.image_from_console` now works with the newer renderers. - New renderers now auto-load fonts from `libtcod.cfg` or `terminal.png`. ## [11.6.0] - 2019-12-05 + ### Changed + - Console blit operations now perform per-cell alpha transparency. ## [11.5.1] - 2019-11-23 + ### Fixed + - Python 3.8 wheels failed to deploy. ## [11.5.0] - 2019-11-22 + ### Changed + - Quarter block elements are now rendered using Unicode instead of a custom - encoding. + encoding. ### Fixed + - `OPENGL` and `GLSL` renderers were not properly clearing space characters. ## [11.4.1] - 2019-10-15 + ### Added + - Uploaded Python 3.8 wheels to PyPI. ## [11.4.0] - 2019-09-20 + ### Added + - Added `__array_interface__` to the Image class. - Added `Console.draw_semigraphics` as a replacement for blit_2x functions. - `draw_semigraphics` can handle array-like objects. + `draw_semigraphics` can handle array-like objects. - `Image.from_array` class method creates an Image from an array-like object. - `tcod.image.load` loads a PNG file as an RGBA array. ### Changed + - `Console.tiles` is now named `Console.buffer`. ## [11.3.0] - 2019-09-06 + ### Added + - New attribute `Console.tiles2` is similar to `Console.tiles` but without an - alpha channel. + alpha channel. ## [11.2.2] - 2019-08-25 + ### Fixed + - Fixed a regression preventing PyInstaller distributions from loading SDL2. ## [11.2.1] - 2019-08-25 ## [11.2.0] - 2019-08-24 + ### Added + - `tcod.path.dijkstra2d`: Computes Dijkstra from an arbitrary initial state. - `tcod.path.hillclimb2d`: Returns a path from a distance array. - `tcod.path.maxarray`: Creates arrays filled with maximum finite values. ### Fixed + - Changing the tiles of an active tileset on OPENGL2 will no longer leave - temporary artifact tiles. + temporary artifact tiles. - It's now harder to accidentally import tcod's internal modules. ## [11.1.2] - 2019-08-02 + ### Changed + - Now bundles SDL 2.0.10 for Windows/MacOS. ### Fixed + - Can now parse SDL 2.0.10 headers during installation without crashing. ## [11.1.1] - 2019-08-01 + ### Deprecated + - Using an out-of-bounds index for field-of-view operations now raises a - warning, which will later become an error. + warning, which will later become an error. ### Fixed + - Changing the tiles of an active tileset will now work correctly. ## [11.1.0] - 2019-07-05 + ### Added + - You can now set the `TCOD_RENDERER` and `TCOD_VSYNC` environment variables to - force specific options to be used. - Example: ``TCOD_RENDERER=sdl2 TCOD_VSYNC=1`` + force specific options to be used. + Example: `TCOD_RENDERER=sdl2 TCOD_VSYNC=1` ### Changed + - `tcod.sys_set_renderer` now raises an exception if it fails. ### Fixed + - `tcod.console_map_ascii_code_to_font` functions will now work when called - before `tcod.console_init_root`. + before `tcod.console_init_root`. ## [11.0.2] - 2019-06-21 + ### Changed + - You no longer need OpenGL to build python-tcod. ## [11.0.1] - 2019-06-21 + ### Changed + - Better runtime checks for Windows dependencies should now give distinct - errors depending on if the issue is SDL2 or missing redistributables. + errors depending on if the issue is SDL2 or missing redistributables. ### Fixed + - Changed NumPy type hints from `np.array` to `np.ndarray` which should - resolve issues. + resolve issues. ## [11.0.0] - 2019-06-14 + ### Changed + - `tcod.map.compute_fov` now takes a 2-item tuple instead of separate `x` and - `y` parameters. This causes less confusion over how axes are aligned. + `y` parameters. This causes less confusion over how axes are aligned. ## [10.1.1] - 2019-06-02 + ### Changed + - Better string representations for `tcod.event.Event` subclasses. ### Fixed + - Fixed regressions in text alignment for non-rectangle print functions. ## [10.1.0] - 2019-05-24 + ### Added + - `tcod.console_init_root` now has an optional `vsync` parameter. ## [10.0.5] - 2019-05-17 + ### Fixed + - Fixed shader compilation issues in the OPENGL2 renderer. - Fallback fonts should fail less on Linux. ## [10.0.4] - 2019-05-17 + ### Changed + - Now depends on cffi 0.12 or later. ### Fixed + - `tcod.console_init_root` and `tcod.console_set_custom_font` will raise - exceptions instead of terminating. + exceptions instead of terminating. - Fixed issues preventing `tcod.event` from working on 32-bit Windows. ## [10.0.3] - 2019-05-10 + ### Fixed + - Corrected bounding box issues with the `Console.print_box` method. ## [10.0.2] - 2019-04-26 + ### Fixed + - Resolved Color warnings when importing tcod. - When compiling, fixed a name conflict with endianness macros on FreeBSD. ## [10.0.1] - 2019-04-19 + ### Fixed + - Fixed horizontal alignment for TrueType fonts. - Fixed taking screenshots with the older SDL renderer. ## [10.0.0] - 2019-03-29 + ### Added + - New `Console.tiles` array attribute. + ### Changed + - `Console.DTYPE` changed to add alpha to its color types. + ### Fixed + - Console printing was ignoring color codes at the beginning of a string. ## [9.3.0] - 2019-03-15 + ### Added + - The SDL2/OPENGL2 renderers can potentially use a fall-back font when none - are provided. + are provided. - New function `tcod.event.get_mouse_state`. - New function `tcod.map.compute_fov` lets you get a visibility array directly - from a transparency array. + from a transparency array. + ### Deprecated + - The following functions and classes have been deprecated. - - `tcod.Key` - - `tcod.Mouse` - - `tcod.mouse_get_status` - - `tcod.console_is_window_closed` - - `tcod.console_check_for_keypress` - - `tcod.console_wait_for_keypress` - - `tcod.console_delete` - - `tcod.sys_check_for_event` - - `tcod.sys_wait_for_event` + - `tcod.Key` + - `tcod.Mouse` + - `tcod.mouse_get_status` + - `tcod.console_is_window_closed` + - `tcod.console_check_for_keypress` + - `tcod.console_wait_for_keypress` + - `tcod.console_delete` + - `tcod.sys_check_for_event` + - `tcod.sys_wait_for_event` - The SDL, OPENGL, and GLSL renderers have been deprecated. - Many libtcodpy functions have been marked with PendingDeprecationWarning's. + ### Fixed + - To be more compatible with libtcodpy `tcod.console_init_root` will default - to the SDL render, but will raise warnings when an old renderer is used. + to the SDL render, but will raise warnings when an old renderer is used. ## [9.2.5] - 2019-03-04 + ### Fixed + - Fixed `tcod.namegen_generate_custom`. ## [9.2.4] - 2019-03-02 + ### Fixed + - The `tcod` package is has been marked as typed and will now work with MyPy. ## [9.2.3] - 2019-03-01 + ### Deprecated + - The behavior for negative indexes on the new print functions may change in - the future. + the future. - Methods and functionality preventing `tcod.Color` from behaving like a tuple - have been deprecated. + have been deprecated. ## [9.2.2] - 2019-02-26 + ### Fixed + - `Console.print_box` wasn't setting the background color by default. ## [9.2.1] - 2019-02-25 + ### Fixed + - `tcod.sys_get_char_size` fixed on the new renderers. ## [9.2.0] - 2019-02-24 + ### Added + - New `tcod.console.get_height_rect` function, which can be used to get the - height of a print call without an existing console. + height of a print call without an existing console. - New `tcod.tileset` module, with a `set_truetype_font` function. + ### Fixed + - The new print methods now handle alignment according to how they were - documented. + documented. - `SDL2` and `OPENGL2` now support screenshots. - Windows and MacOS builds now restrict exported SDL2 symbols to only - SDL 2.0.5; This will avoid hard to debug import errors when the wrong - version of SDL is dynamically linked. + SDL 2.0.5; This will avoid hard to debug import errors when the wrong + version of SDL is dynamically linked. - The root console now starts with a white foreground. ## [9.1.0] - 2019-02-23 + ### Added + - Added the `tcod.random.MULTIPLY_WITH_CARRY` constant. + ### Changed + - The overhead for warnings has been reduced when running Python with the - optimize `-O` flag. + optimize `-O` flag. - `tcod.random.Random` now provides a default algorithm. ## [9.0.0] - 2019-02-17 + ### Changed + - New console methods now default to an `fg` and `bg` of None instead of - white-on-black. + white-on-black. ## [8.5.0] - 2019-02-15 + ### Added + - `tcod.console.Console` now supports `str` and `repr`. - Added new Console methods which are independent from the console defaults. - You can now give an array when initializing a `tcod.console.Console` - instance. + instance. - `Console.clear` can now take `ch`, `fg`, and `bg` parameters. + ### Changed + - Updated libtcod to 1.10.6 - Printing generates more compact layouts. + ### Deprecated + - Most libtcodpy console functions have been replaced by the tcod.console - module. -- Deprecated the `set_key_color` functions. You can pass key colors to - `Console.blit` instead. + module. +- Deprecated the `set_key_color` functions. You can pass key colors to + `Console.blit` instead. - `Console.clear` should be given the colors to clear with as parameters, - rather than by using `default_fg` or `default_bg`. + rather than by using `default_fg` or `default_bg`. - Most functions which depend on console default values have been deprecated. - The new deprecation warnings will give details on how to make default values - explicit. + The new deprecation warnings will give details on how to make default values + explicit. + ### Fixed + - `tcod.console.Console.blit` was ignoring the key color set by - `Console.set_key_color`. + `Console.set_key_color`. - The `SDL2` and `OPENGL2` renders can now large numbers of tiles. ## [8.4.3] - 2019-02-06 + ### Changed + - Updated libtcod to 1.10.5 - The SDL2/OPENGL2 renderers will now auto-detect a custom fonts key-color. ## [8.4.2] - 2019-02-05 + ### Deprecated + - The tdl module has been deprecated. - The libtcodpy parser functions have been deprecated. + ### Fixed + - `tcod.image_is_pixel_transparent` and `tcod.image_get_alpha` now return - values. + values. - `Console.print_frame` was clearing tiles outside if its bounds. - The `FONT_LAYOUT_CP437` layout was incorrect. ## [8.4.1] - 2019-02-01 + ### Fixed + - Window event types were not upper-case. - Fixed regression where libtcodpy mouse wheel events unset mouse coordinates. ## [8.4.0] - 2019-01-31 + ### Added + - Added tcod.event module, based off of the sdlevent.py shim. + ### Changed + - Updated libtcod to 1.10.3 + ### Fixed + - Fixed libtcodpy `struct_add_value_list` function. - Use correct math for tile-based delta in mouse events. - New renderers now support tile-based mouse coordinates. - SDL2 renderer will now properly refresh after the window is resized. ## [8.3.2] - 2018-12-28 + ### Fixed + - Fixed rare access violations for some functions which took strings as - parameters, such as `tcod.console_init_root`. + parameters, such as `tcod.console_init_root`. ## [8.3.1] - 2018-12-28 + ### Fixed + - libtcodpy key and mouse functions will no longer accept the wrong types. - The `new_struct` method was not being called for libtcodpy's custom parsers. ## [8.3.0] - 2018-12-08 + ### Added + - Added BSP traversal methods in tcod.bsp for parity with libtcodpy. + ### Deprecated + - Already deprecated bsp functions are now even more deprecated. ## [8.2.0] - 2018-11-27 + ### Added + - New layout `tcod.FONT_LAYOUT_CP437`. + ### Changed + - Updated libtcod to 1.10.2 - `tcod.console_print_frame` and `Console.print_frame` now support Unicode - strings. + strings. + ### Deprecated + - Deprecated using bytes strings for all printing functions. + ### Fixed + - Console objects are now initialized with spaces. This fixes some blit - operations. + operations. - Unicode code-points above U+FFFF will now work on all platforms. ## [8.1.1] - 2018-11-16 + ### Fixed + - Printing a frame with an empty string no longer displays a title bar. ## [8.1.0] - 2018-11-15 + ### Changed + - Heightmap functions now support 'F_CONTIGUOUS' arrays. - `tcod.heightmap_new` now has an `order` parameter. - Updated SDL to 2.0.9 + ### Deprecated + - Deprecated heightmap functions which sample noise grids, this can be done - using the `Noise.sample_ogrid` method. + using the `Noise.sample_ogrid` method. ## [8.0.0] - 2018-11-02 + ### Changed + - The default renderer can now be anything if not set manually. - Better error message for when a font file isn't found. ## [7.0.1] - 2018-10-27 + ### Fixed + - Building from source was failing because `console_2tris.glsl*` was missing - from source distributions. + from source distributions. ## [7.0.0] - 2018-10-25 + ### Added + - New `RENDERER_SDL2` and `RENDERER_OPENGL2` renderers. + ### Changed + - Updated libtcod to 1.9.0 - Now requires SDL 2.0.5, which is not trivially installable on - Ubuntu 16.04 LTS. + Ubuntu 16.04 LTS. + ### Removed + - Dropped support for Python versions before 3.5 - Dropped support for MacOS versions before 10.9 Mavericks. ## [6.0.7] - 2018-10-24 + ### Fixed + - The root console no longer loses track of buffers and console defaults on a - renderer change. + renderer change. ## [6.0.6] - 2018-10-01 + ### Fixed + - Replaced missing wheels for older and 32-bit versions of MacOS. ## [6.0.5] - 2018-09-28 + ### Fixed + - Resolved CDefError error during source installs. ## [6.0.4] - 2018-09-11 + ### Fixed + - tcod.Key right-hand modifiers are now set independently at initialization, - instead of mirroring the left-hand modifier value. + instead of mirroring the left-hand modifier value. ## [6.0.3] - 2018-09-05 + ### Fixed + - tcod.Key and tcod.Mouse no longer ignore initiation parameters. ## [6.0.2] - 2018-08-28 + ### Fixed + - Fixed color constants missing at build-time. ## [6.0.1] - 2018-08-24 + ### Fixed + - Source distributions were missing C++ source files. ## [6.0.0] - 2018-08-23 + ### Changed + - Project renamed to tcod on PyPI. + ### Deprecated + - Passing bytes strings to libtcodpy print functions is deprecated. + ### Fixed + - Fixed libtcodpy print functions not accepting bytes strings. - libtcod constants are now generated at build-time fixing static analysis - tools. + tools. ## [5.0.1] - 2018-07-08 + ### Fixed + - tdl.event no longer crashes with StopIteration on Python 3.7 ## [5.0.0] - 2018-07-05 + ### Changed + - tcod.path: all classes now use `shape` instead of `width` and `height`. - tcod.path now respects NumPy array shape, instead of assuming that arrays - need to be transposed from C memory order. From now on `x` and `y` mean - 1st and 2nd axis. This doesn't affect non-NumPy code. + need to be transposed from C memory order. From now on `x` and `y` mean + 1st and 2nd axis. This doesn't affect non-NumPy code. - tcod.path now has full support of non-contiguous memory. ## [4.6.1] - 2018-06-30 + ### Added + - New function `tcod.line_where` for indexing NumPy arrays using a Bresenham - line. + line. + ### Deprecated + - Python 2.7 support will be dropped in the near future. ## [4.5.2] - 2018-06-29 + ### Added + - New wheels for Python3.7 on Windows. + ### Fixed + - Arrays from `tcod.heightmap_new` are now properly zeroed out. ## [4.5.1] - 2018-06-23 + ### Deprecated + - Deprecated all libtcodpy map functions. + ### Fixed + - `tcod.map_copy` could break the `tcod.map.Map` class. - `tcod.map_clear` `transparent` and `walkable` parameters were reversed. - When multiple SDL2 headers were installed, the wrong ones would be used when - the library is built. + the library is built. - Fails to build via pip unless Numpy is installed first. ## [4.5.0] - 2018-06-12 + ### Changed + - Updated libtcod to v1.7.0 - Updated SDL to v2.0.8 - Error messages when failing to create an SDL window should be a less vague. - You no longer need to initialize libtcod before you can print to an - off-screen console. + off-screen console. + ### Fixed + - Avoid crashes if the root console has a character code higher than expected. + ### Removed + - No more debug output when loading fonts. ## [4.4.0] - 2018-05-02 + ### Added -- Added the libtcodpy module as an alias for tcod. Actual use of it is - deprecated, it exists primarily for backward compatibility. + +- Added the libtcodpy module as an alias for tcod. Actual use of it is + deprecated, it exists primarily for backward compatibility. - Adding missing libtcodpy functions `console_has_mouse_focus` and - `console_is_active`. + `console_is_active`. + ### Changed + - Updated libtcod to v1.6.6 ## [4.3.2] - 2018-03-18 + ### Deprecated + - Deprecated the use of falsy console parameters with libtcodpy functions. + ### Fixed + - Fixed libtcodpy image functions not supporting falsy console parameters. - Fixed tdl `Window.get_char` method. (Kaczor2704) ## [4.3.1] - 2018-03-07 + ### Fixed + - Fixed cffi.api.FFIError "unsupported expression: expected a simple numeric - constant" error when building on platforms with an older cffi module and - newer SDL headers. + constant" error when building on platforms with an older cffi module and + newer SDL headers. - tcod/tdl Map and Console objects were not saving stride data when pickled. ## [4.3.0] - 2018-02-01 + ### Added + - You can now set the numpy memory order on tcod.console.Console, - tcod.map.Map, and tdl.map.Map objects well as from the - tcod.console_init_root function. + tcod.map.Map, and tdl.map.Map objects well as from the + tcod.console_init_root function. + ### Changed + - The `console_init_root` `title` parameter is now optional. + ### Fixed + - OpenGL renderer alpha blending is now consistent with all other render - modes. + modes. ## [4.2.3] - 2018-01-06 + ### Fixed + - Fixed setup.py regression that could prevent building outside of the git - repository. + repository. ## [4.2.2] - 2018-01-06 + ### Fixed + - The Windows dynamic linker will now prefer the bundled version of SDL. - This fixes: - "ImportError: DLL load failed: The specified procedure could not be found." + This fixes: + "ImportError: DLL load failed: The specified procedure could not be found." - `key.c` is no longer set when `key.vk == KEY_TEXT`, this fixes a regression - which was causing events to be heard twice in the libtcod/Python tutorial. + which was causing events to be heard twice in the libtcod/Python tutorial. ## [4.2.0] - 2018-01-02 + ### Changed + - Updated libtcod backend to v1.6.4 - Updated SDL to v2.0.7 for Windows/MacOS. + ### Removed + - Source distributions no longer include tests, examples, or fonts. - [Find these on GitHub.](https://github.com/libtcod/python-tcod) + [Find these on GitHub.](https://github.com/libtcod/python-tcod) + ### Fixed + - Fixed "final link failed: Nonrepresentable section on output" error - when compiling for Linux. + when compiling for Linux. - `tcod.console_init_root` defaults to the SDL renderer, other renderers - cause issues with mouse movement events. + cause issues with mouse movement events. ## [4.1.1] - 2017-11-02 + ### Fixed + - Fixed `ConsoleBuffer.blit` regression. - Console defaults corrected, the root console's blend mode and alignment is - the default value for newly made Console's. + the default value for newly made Console's. - You can give a byte string as a filename to load parsers. ## [4.1.0] - 2017-07-19 + ### Added + - tdl Map class can now be pickled. + ### Changed + - Added protection to the `transparent`, `walkable`, and `fov` - attributes in tcod and tdl Map classes, to prevent them from being - accidentally overridden. + attributes in tcod and tdl Map classes, to prevent them from being + accidentally overridden. - tcod and tdl Map classes now use numpy arrays as their attributes. ## [4.0.1] - 2017-07-12 + ### Fixed + - tdl: Fixed NameError in `set_fps`. ## [4.0.0] - 2017-07-08 + ### Changed + - tcod.bsp: `BSP.split_recursive` parameter `random` is now `seed`. - tcod.console: `Console.blit` parameters have been rearranged. - Most of the parameters are now optional. + Most of the parameters are now optional. - tcod.noise: `Noise.__init__` parameter `rand` is now named `seed`. - tdl: Changed `set_fps` parameter name to `fps`. + ### Fixed + - tcod.bsp: Corrected spelling of max_vertical_ratio. ## [3.2.0] - 2017-07-04 + ### Changed + - Merged libtcod-cffi dependency with TDL. + ### Fixed + - Fixed boolean related crashes with Key 'text' events. -- tdl.noise: Fixed crash when given a negative seed. As well as cases - where an instance could lose its seed being pickled. +- tdl.noise: Fixed crash when given a negative seed. As well as cases + where an instance could lose its seed being pickled. ## [3.1.0] - 2017-05-28 + ### Added + - You can now pass tdl Console instances as parameters to libtcod-cffi - functions expecting a tcod Console. + functions expecting a tcod Console. + ### Changed + - Dependencies updated: `libtcod-cffi>=2.5.0,<3` - The `Console.tcod_console` attribute is being renamed to - `Console.console_c`. + `Console.console_c`. + ### Deprecated + - The tdl.noise and tdl.map modules will be deprecated in the future. + ### Fixed + - Resolved crash-on-exit issues for Windows platforms. ## [3.0.2] - 2017-04-13 + ### Changed + - Dependencies updated: `libtcod-cffi>=2.4.3,<3` - You can now create Console instances before a call to `tdl.init`. + ### Removed + - Dropped support for Python 3.3 + ### Fixed + - Resolved issues with MacOS builds. - 'OpenGL' and 'GLSL' renderers work again. ## [3.0.1] - 2017-03-22 + ### Changed + - `KeyEvent`'s with `text` now have all their modifier keys set to False. + ### Fixed + - Undefined behavior in text events caused crashes on 32-bit builds. ## [3.0.0] - 2017-03-21 + ### Added + - `KeyEvent` supports libtcod text and meta keys. + ### Changed + - `KeyEvent` parameters have been moved. - This version requires `libtcod-cffi>=2.3.0`. + ### Deprecated + - `KeyEvent` camel capped attribute names are deprecated. + ### Fixed + - Crashes with key-codes undefined by libtcod. - `tdl.map` typedef issues with libtcod-cffi. - ## [2.0.1] - 2017-02-22 + ### Fixed + - `tdl.init` renderer was defaulted to OpenGL which is not supported in the - current version of libtcod. + current version of libtcod. ## [2.0.0] - 2017-02-15 + ### Changed + - Dependencies updated, tdl now requires libtcod-cffi 2.x.x - Some event behaviors have changed with SDL2, event keys might be different - than what you expect. + than what you expect. + ### Removed + - Key repeat functions were removed from SDL2. - `set_key_repeat` is now stubbed, and does nothing. + `set_key_repeat` is now stubbed, and does nothing. ## [1.6.0] - 2016-11-18 + - Console.blit methods can now take fg_alpha and bg_alpha parameters. ## [1.5.3] - 2016-06-04 + - set_font no longer crashes when loading a file without the implied font size in its name ## [1.5.2] - 2016-03-11 + - Fixed non-square Map instances ## [1.5.1] - 2015-12-20 + - Fixed errors with Unicode and non-Unicode literals on Python 2 - Fixed attribute error in compute_fov ## [1.5.0] - 2015-07-13 + - python-tdl distributions are now universal builds - New Map class - map.bresenham now returns a list - This release will require libtcod-cffi v0.2.3 or later ## [1.4.0] - 2015-06-22 + - The DLL's have been moved into another library which you can find at https://github.com/HexDecimal/libtcod-cffi You can use this library to have some raw access to libtcod if you want. Plus it can be used alongside TDL. - The libtcod console objects in Console instances have been made public. -- Added tdl.event.wait function. This function can called with a timeout and +- Added tdl.event.wait function. This function can called with a timeout and can automatically call tdl.flush. ## [1.3.1] - 2015-06-19 + - Fixed pathfinding regressions. ## [1.3.0] - 2015-06-19 -- Updated backend to use python-cffi instead of ctypes. This gives decent + +- Updated backend to use python-cffi instead of ctypes. This gives decent boost to speed in CPython and a drastic to boost in speed in PyPy. ## [1.2.0] - 2015-06-06 -- The set_colors method now changes the default colors used by the draw_* - methods. You can use Python's Ellipsis to explicitly select default colors + +- The set*colors method now changes the default colors used by the draw*\* + methods. You can use Python's Ellipsis to explicitly select default colors this way. - Functions and Methods renamed to match Python's style-guide PEP 8, the old function names still exist and are depreciated. - The fgcolor and bgcolor parameters have been shortened to fg and bg. ## [1.1.7] - 2015-03-19 + - Noise generator now seeds properly. - The OS event queue will now be handled during a call to tdl.flush. This prevents a common newbie programmer hang where events are handled @@ -1285,11 +2109,13 @@ This project adheres to [Semantic Versioning](https://semver.org/) since version - Fixed a major bug that would cause a crash in later versions of Python 3 ## [1.1.6] - 2014-06-27 + - Fixed a race condition when importing on some platforms. - Fixed a type issue with quickFOV on Linux. - Added a bresenham function to the tdl.map module. ## [1.1.5] - 2013-11-10 + - A for loop can iterate over all coordinates of a Console. - drawStr can be configured to scroll or raise an error. - You can now configure or disable key repeating with tdl.event.setKeyRepeat @@ -1297,6 +2123,7 @@ This project adheres to [Semantic Versioning](https://semver.org/) since version - setColors method fixed. ## [1.1.4] - 2013-03-06 + - Merged the Typewriter and MetaConsole classes, You now have a virtual cursor with Console and Window objects. - Fixed the clear method on the Window class. @@ -1307,11 +2134,13 @@ This project adheres to [Semantic Versioning](https://semver.org/) since version - Fixed event.keyWait, and now converts window closed events into Alt+F4. ## [1.1.3] - 2012-12-17 + - Some of the setFont parameters were incorrectly labeled and documented. - setFont can auto-detect tilesets if the font sizes are in the filenames. - Added some X11 unicode tilesets, including Unifont. ## [1.1.2] - 2012-12-13 + - Window title now defaults to the running scripts filename. - Fixed incorrect deltaTime for App.update - App will no longer call tdl.flush on its own, you'll need to call this @@ -1320,6 +2149,7 @@ This project adheres to [Semantic Versioning](https://semver.org/) since version - clear method now defaults to black on black. ## [1.1.1] - 2012-12-05 + - Map submodule added with AStar class and quickFOV function. - New Typewriter class. - Most console functions can use Python-style negative indexes now. @@ -1327,6 +2157,7 @@ This project adheres to [Semantic Versioning](https://semver.org/) since version - Rectangle geometry is less strict. ## [1.1.0] - 2012-10-04 + - KeyEvent.keyname is now KeyEvent.key - MouseButtonEvent.button now behaves like KeyEvent.keyname does. - event.App class added. @@ -1334,20 +2165,24 @@ This project adheres to [Semantic Versioning](https://semver.org/) since version - KeyEvent.ctrl is now KeyEvent.control ## [1.0.8] - 2010-04-07 + - No longer works in Python 2.5 but now works in 3.x and has been partly tested. - Many bug fixes. ## [1.0.5] - 2010-04-06 + - Got rid of setuptools dependency, this will make it much more compatible with Python 3.x - Fixed a typo with the MacOS library import. ## [1.0.4] - 2010-04-06 -- All constant colors (C_*) have been removed, they may be put back in later. + +- All constant colors (C\_\*) have been removed, they may be put back in later. - Made some type assertion failures show the value they received to help in - general debugging. Still working on it. + general debugging. Still working on it. - Added MacOS and 64-bit Linux support. ## [1.0.0] - 2009-01-31 + - First public release. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c4f7d5ea..846d492c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -11,7 +11,7 @@ pre-commit install ## Building python-tcod To work with the tcod source, your environment must be set up to build -Python C extensions. You'll also need `cpp` installed for +Python C extensions. You'll also need `cpp` installed for use with pycparser. ### Windows diff --git a/LICENSE.txt b/LICENSE.txt index d91bf759..8af4c736 100755 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,6 +1,6 @@ BSD 2-Clause License -Copyright (c) 2009-2023, Kyle Benesch and the python-tcod contributors. +Copyright (c) 2009-2026, Kyle Benesch and the python-tcod contributors. All rights reserved. Redistribution and use in source and binary forms, with or without diff --git a/README.rst b/README.rst index 82aad962..32e9164c 100755 --- a/README.rst +++ b/README.rst @@ -46,9 +46,9 @@ For the most part it's just:: ============== Requirements ============== -* Python 3.8+ +* Python 3.10+ * Windows, Linux, or MacOS X 10.9+. -* On Linux, requires libsdl2 (2.0.10+). +* On Linux, requires libsdl3 =========== Changelog diff --git a/build_libtcod.py b/build_libtcod.py index b912ecc1..14a46710 100755 --- a/build_libtcod.py +++ b/build_libtcod.py @@ -1,23 +1,35 @@ -#!/usr/bin/env python3 +#!/usr/bin/env python """Parse and compile libtcod and SDL sources for CFFI.""" + from __future__ import annotations +import ast import contextlib import glob import os import platform import re +import subprocess import sys from pathlib import Path -from typing import Any, Iterable, Iterator +from typing import TYPE_CHECKING, Any, ClassVar, Final +import attrs +import pycparser # type: ignore[import-untyped] +import pycparser.c_ast # type: ignore[import-untyped] +import pycparser.c_generator # type: ignore[import-untyped] from cffi import FFI +# ruff: noqa: T201 + sys.path.append(str(Path(__file__).parent)) # Allow importing local modules. -import build_sdl # noqa: E402 +import build_sdl + +if TYPE_CHECKING: + from collections.abc import Iterable, Iterator -Py_LIMITED_API = 0x03060000 +Py_LIMITED_API: None | int = 0x03100000 HEADER_PARSE_PATHS = ("tcod/", "libtcod/src/libtcod/") HEADER_PARSE_EXCLUDES = ("gl2_ext_.h", "renderer_gl_internal.h", "event.h") @@ -33,6 +45,7 @@ r"TCODLIB_C?API|TCOD_PUBLIC|TCOD_NODISCARD|TCOD_DEPRECATED_NOMESSAGE|TCOD_DEPRECATED_ENUM" r"|(TCOD_DEPRECATED\(\".*?\"\))" r"|(TCOD_DEPRECATED|TCODLIB_FORMAT)\([^)]*\)|__restrict" + r"|TCODLIB_(BEGIN|END)_IGNORE_DEPRECATIONS" ) RE_VAFUNC = re.compile(r"^[^;]*\([^;]*va_list.*\);", re.MULTILINE) RE_INLINE = re.compile(r"(^.*?inline.*?\(.*?\))\s*\{.*?\}$", re.DOTALL | re.MULTILINE) @@ -45,18 +58,18 @@ class ParsedHeader: """ # Class dictionary of all parsed headers. - all_headers: dict[Path, ParsedHeader] = {} + all_headers: ClassVar[dict[Path, ParsedHeader]] = {} def __init__(self, path: Path) -> None: """Initialize and organize a header file.""" - self.path = path = path.resolve(True) + self.path = path = path.resolve(strict=True) directory = path.parent depends = set() header = self.path.read_text(encoding="utf-8") header = RE_COMMENT.sub("", header) header = RE_CPLUSPLUS.sub("", header) for dependency in RE_INCLUDE.findall(header): - depends.add((directory / str(dependency)).resolve(True)) + depends.add((directory / str(dependency)).resolve(strict=True)) header = RE_PREPROCESSOR.sub("", header) header = RE_TAGS.sub("", header) header = RE_VAFUNC.sub("", header) @@ -90,7 +103,7 @@ def walk_includes(directory: str) -> Iterator[ParsedHeader]: if file in HEADER_PARSE_EXCLUDES: continue if file.endswith(".h"): - yield ParsedHeader(Path(path, file).resolve(True)) + yield ParsedHeader(Path(path, file).resolve(strict=True)) def resolve_dependencies( @@ -152,7 +165,17 @@ def walk_sources(directory: str) -> Iterator[str]: libraries: list[str] = [*build_sdl.libraries] library_dirs: list[str] = [*build_sdl.library_dirs] -define_macros: list[tuple[str, Any]] = [("Py_LIMITED_API", Py_LIMITED_API)] +define_macros: list[tuple[str, Any]] = [] + +if "free-threading build" in sys.version: + Py_LIMITED_API = None +if "PYODIDE" in os.environ: + # Unable to apply Py_LIMITED_API to Pyodide in cffi<=1.17.1 + # https://github.com/python-cffi/cffi/issues/179 + Py_LIMITED_API = None + +if Py_LIMITED_API: + define_macros.append(("Py_LIMITED_API", Py_LIMITED_API)) sources += walk_sources("tcod/") sources += walk_sources("libtcod/src/libtcod/") @@ -170,8 +193,8 @@ def walk_sources(directory: str) -> Iterator[str]: include_dirs.append("libtcod/src/zlib/") -if sys.platform == "darwin": - # Fix "implicit declaration of function 'close'" in zlib. +if sys.platform != "win32": + # Fix implicit declaration of multiple functions in zlib. define_macros.append(("HAVE_UNISTD_H", 1)) @@ -200,15 +223,16 @@ def walk_sources(directory: str) -> Iterator[str]: extra_link_args.extend(GCC_CFLAGS[tdl_build]) ffi = FFI() -ffi.cdef(build_sdl.get_cdef()) +sdl_cdef, sdl_strings = build_sdl.get_cdef() +ffi.cdef(sdl_cdef) for include in includes: try: ffi.cdef(include.header) - except Exception: + except Exception: # noqa: PERF203 # Print the source, for debugging. print(f"Error with: {include.path}") for i, line in enumerate(include.header.split("\n"), 1): - print("%03i %s" % (i, line)) + print(f"{i:03i} {line}") raise ffi.cdef( """ @@ -217,7 +241,10 @@ def walk_sources(directory: str) -> Iterator[str]: ) ffi.set_source( module_name, - "#include \n#include ", + """\ +#include +#define SDL_oldnames_h_ +#include """, include_dirs=include_dirs, library_dirs=library_dirs, sources=sources, @@ -232,6 +259,7 @@ def walk_sources(directory: str) -> Iterator[str]: This module is auto-generated by `build_libtcod.py`. """ + from tcod.color import Color ''' @@ -248,7 +276,7 @@ def find_sdl_attrs(prefix: str) -> Iterator[tuple[str, int | str | Any]]: `prefix` is used to filter out which names to copy. """ - from tcod._libtcod import lib + from tcod._libtcod import lib # noqa: PLC0415 if prefix.startswith("SDL_"): name_starts_at = 4 @@ -299,12 +327,14 @@ def parse_sdl_attrs(prefix: str, all_names: list[str] | None) -> tuple[str, str] ] +RE_CONSTANTS_ALL: Final = re.compile( + r"(.*# --- From constants.py ---).*(# --- End constants.py ---.*)", + re.DOTALL, +) + + def update_module_all(filename: Path, new_all: str) -> None: """Update the __all__ of a file with the constants from new_all.""" - RE_CONSTANTS_ALL = re.compile( - r"(.*# --- From constants.py ---).*(# --- End constants.py ---.*)", - re.DOTALL, - ) match = RE_CONSTANTS_ALL.match(filename.read_text(encoding="utf-8")) assert match, f"Can't determine __all__ subsection in {filename}!" header, footer = match.groups() @@ -325,8 +355,8 @@ def generate_enums(prefix: str) -> Iterator[str]: def write_library_constants() -> None: """Write libtcod constants into the tcod.constants module.""" - import tcod.color - from tcod._libtcod import ffi, lib + import tcod.color # noqa: PLC0415 + from tcod._libtcod import ffi, lib # noqa: PLC0415 with Path("tcod/constants.py").open("w", encoding="utf-8") as f: all_names = [] @@ -365,7 +395,7 @@ def write_library_constants() -> None: f.write(f"{name[5:]} = {color!r}\n") all_names_merged = ",\n ".join(f'"{name}"' for name in all_names) - f.write(f"\n__all__ = [\n {all_names_merged},\n]\n") + f.write(f"\n__all__ = [ # noqa: RUF022\n {all_names_merged},\n]\n") update_module_all(Path("tcod/libtcodpy.py"), all_names_merged) with Path("tcod/event_constants.py").open("w", encoding="utf-8") as f: @@ -375,15 +405,15 @@ def write_library_constants() -> None: f.write(f"""{parse_sdl_attrs("SDL_SCANCODE", None)[0]}\n""") f.write("\n# --- SDL keyboard symbols ---\n") - f.write(f"""{parse_sdl_attrs("SDLK", None)[0]}\n""") + f.write(f"""{parse_sdl_attrs("SDLK_", None)[0]}\n""") f.write("\n# --- SDL keyboard modifiers ---\n") - f.write("{}\n_REVERSE_MOD_TABLE = {}\n".format(*parse_sdl_attrs("KMOD", all_names))) + f.write("{}\n_REVERSE_MOD_TABLE = {}\n".format(*parse_sdl_attrs("SDL_KMOD", None))) f.write("\n# --- SDL wheel ---\n") f.write("{}\n_REVERSE_WHEEL_TABLE = {}\n".format(*parse_sdl_attrs("SDL_MOUSEWHEEL", all_names))) all_names_merged = ",\n ".join(f'"{name}"' for name in all_names) - f.write(f"\n__all__ = [\n {all_names_merged},\n]\n") + f.write(f"\n__all__ = [ # noqa: RUF022\n {all_names_merged},\n]\n") event_py = Path("tcod/event.py").read_text(encoding="utf-8") @@ -402,6 +432,240 @@ def write_library_constants() -> None: Path("tcod/event.py").write_text(event_py, encoding="utf-8") + with Path("tcod/sdl/constants.py").open("w", encoding="utf-8") as f: + f.write('"""SDL private constants."""\n\n') + for name, value in sdl_strings.items(): + f.write(f"{name} = {ast.literal_eval(value)!r}\n") + + subprocess.run(["ruff", "format", "--silent", Path("tcod/sdl/constants.py")], check=True) # noqa: S603, S607 + + +def _fix_reserved_name(name: str) -> str: + """Add underscores to reserved Python keywords.""" + assert isinstance(name, str) + if name in ("def", "in"): + return name + "_" + return name + + +@attrs.define(frozen=True) +class ConvertedParam: + """Converted type parameter from C types into Python type-hints.""" + + name: str = attrs.field(converter=_fix_reserved_name) + hint: str + original: str + + +def _type_from_names(names: list[str]) -> str: + if not names: + return "" + if names[-1] == "void": + return "None" + if names in (["unsigned", "char"], ["bool"]): + return "bool" + if names[-1] in ("size_t", "int", "ptrdiff_t"): + return "int" + if names[-1] in ("float", "double"): + return "float" + return "Any" + + +def _param_as_hint(node: pycparser.c_ast.Node, default_name: str) -> ConvertedParam: # noqa: PLR0911 + """Return a Python type-hint from a C AST node.""" + original = pycparser.c_generator.CGenerator().visit(node) + name: str + names: list[str] + match node: + case pycparser.c_ast.Typename(type=pycparser.c_ast.TypeDecl(type=pycparser.c_ast.IdentifierType(names=names))): + # Unnamed type + return ConvertedParam(default_name, _type_from_names(names), original) + case pycparser.c_ast.Decl( + name=name, type=pycparser.c_ast.TypeDecl(type=pycparser.c_ast.IdentifierType(names=names)) + ): + # Named type + return ConvertedParam(name, _type_from_names(names), original) + case pycparser.c_ast.Decl( + name=name, + type=pycparser.c_ast.ArrayDecl( + type=pycparser.c_ast.TypeDecl(type=pycparser.c_ast.IdentifierType(names=names)) + ), + ): + # Named array + return ConvertedParam(name, "Any", original) + case pycparser.c_ast.Decl(name=name, type=pycparser.c_ast.PtrDecl()): + # Named pointer + return ConvertedParam(name, "Any", original) + case pycparser.c_ast.Typename(name=name, type=pycparser.c_ast.PtrDecl()): + # Forwarded struct + return ConvertedParam(name or default_name, "Any", original) + case pycparser.c_ast.TypeDecl(type=pycparser.c_ast.IdentifierType(names=names)): + # Return type + return ConvertedParam(default_name, _type_from_names(names), original) + case pycparser.c_ast.PtrDecl(): + # Return pointer + return ConvertedParam(default_name, "Any", original) + case pycparser.c_ast.EllipsisParam(): + # C variable args + return ConvertedParam("*__args", "Any", original) + case _: + raise AssertionError + + +class DefinitionCollector(pycparser.c_ast.NodeVisitor): # type: ignore[misc] + """Gathers functions and names from C headers.""" + + def __init__(self) -> None: + """Initialize the object with empty values.""" + self.functions: list[str] = [] + """Indented Python function definitions.""" + self.variables: set[str] = set() + """Python variable definitions.""" + + def parse_defines(self, string: str, /) -> None: + """Parse C define directives into hinted names.""" + for match in re.finditer(r"#define\s+(\S+)\s+(\S+)\s*", string): + name, value = match.groups() + if value == "...": + self.variables.add(f"{name}: Final[int]") + else: + self.variables.add(f"{name}: Final[Literal[{value}]] = {value}") + + def visit_Decl(self, node: pycparser.c_ast.Decl) -> None: # noqa: N802 + """Parse C FFI functions into type hinted Python functions.""" + match node: + case pycparser.c_ast.Decl( + type=pycparser.c_ast.FuncDecl(), + ): + assert isinstance(node.type.args, pycparser.c_ast.ParamList), type(node.type.args) + arg_hints = [_param_as_hint(param, f"arg{i}") for i, param in enumerate(node.type.args.params)] + return_hint = _param_as_hint(node.type.type, "") + if len(arg_hints) == 1 and arg_hints[0].hint == "None": # Remove void parameter + arg_hints = [] + + python_params = [f"{p.name}: {p.hint}" for p in arg_hints] + if python_params: + if arg_hints[-1].name.startswith("*"): + python_params.insert(-1, "/") + else: + python_params.append("/") + c_def = pycparser.c_generator.CGenerator().visit(node) + python_def = f"""def {node.name}({", ".join(python_params)}) -> {return_hint.hint}:""" + self.functions.append(f''' {python_def}\n """{c_def}"""''') + + def visit_Enumerator(self, node: pycparser.c_ast.Enumerator) -> None: # noqa: N802 + """Parse C enums into hinted names.""" + name: str | None + value: str | int + match node: + case pycparser.c_ast.Enumerator(name=name, value=None): + self.variables.add(f"{name}: Final[int]") + case pycparser.c_ast.Enumerator(name=name, value=pycparser.c_ast.ID()): + self.variables.add(f"{name}: Final[int]") + case pycparser.c_ast.Enumerator(name=name, value=pycparser.c_ast.Constant(value=value)): + value = int(str(value).removesuffix("u"), base=0) + self.variables.add(f"{name}: Final[Literal[{value}]] = {value}") + case pycparser.c_ast.Enumerator( + name=name, value=pycparser.c_ast.UnaryOp(op="-", expr=pycparser.c_ast.Constant(value=value)) + ): + value = -int(str(value).removesuffix("u"), base=0) + self.variables.add(f"{name}: Final[Literal[{value}]] = {value}") + case pycparser.c_ast.Enumerator(name=name): + self.variables.add(f"{name}: Final[int]") + case _: + raise AssertionError + + +def write_hints() -> None: + """Write a custom _libtcod.pyi file from C definitions.""" + function_collector = DefinitionCollector() + c = pycparser.CParser() + + # Parse SDL headers + cdef = sdl_cdef + cdef = cdef.replace("int...", "int") + cdef = ( + """ +typedef int bool; +typedef int int8_t; +typedef int uint8_t; +typedef int int16_t; +typedef int uint16_t; +typedef int int32_t; +typedef int uint32_t; +typedef int int64_t; +typedef int uint64_t; +typedef int wchar_t; +typedef int intptr_t; +""" + + cdef + ) + for match in re.finditer(r"SDL_PIXELFORMAT_\w+", cdef): + function_collector.variables.add(f"{match.group()}: int") + cdef = re.sub(r"(typedef enum SDL_PixelFormat).*(SDL_PixelFormat;)", r"\1 \2", cdef, flags=re.DOTALL) + cdef = cdef.replace("padding[...]", "padding[]") + cdef = cdef.replace("...;} SDL_TouchFingerEvent;", "} SDL_TouchFingerEvent;") + function_collector.parse_defines(cdef) + cdef = re.sub(r"\n#define .*", "", cdef) + cdef = re.sub(r"""extern "Python" \{(.*?)\}""", r"\1", cdef, flags=re.DOTALL) + cdef = re.sub(r"//.*", "", cdef) + cdef = cdef.replace("...;", ";") + ast = c.parse(cdef) + function_collector.visit(ast) + + # Parse libtcod headers + cdef = "\n".join(include.header for include in includes) + function_collector.parse_defines(cdef) + cdef = re.sub(r"\n?#define .*", "", cdef) + cdef = re.sub(r"//.*", "", cdef) + cdef = ( + """ +typedef int int8_t; +typedef int uint8_t; +typedef int int16_t; +typedef int uint16_t; +typedef int int32_t; +typedef int uint32_t; +typedef int int64_t; +typedef int uint64_t; +typedef int wchar_t; +typedef int intptr_t; +typedef int ptrdiff_t; +typedef int size_t; +typedef unsigned char bool; +typedef void* SDL_PropertiesID; +""" + + cdef + ) + cdef = re.sub(r"""extern "Python" \{(.*?)\}""", r"\1", cdef, flags=re.DOTALL) + function_collector.visit(c.parse(cdef)) + function_collector.variables.add("TCOD_ctx: Any") + function_collector.variables.add("TCOD_COMPILEDVERSION: Final[int]") + + # Write PYI file + out_functions = """\n\n @staticmethod\n""".join(sorted(function_collector.functions)) + out_variables = "\n ".join(sorted(function_collector.variables)) + + pyi = f"""\ +# Autogenerated with build_libtcod.py +from typing import Any, Final, Literal + +# pyi files for CFFI ports are not standard +# ruff: noqa: A002, ANN401, D402, D403, D415, N801, N802, N803, N815, PLW0211, PYI021 + +class _lib: + @staticmethod +{out_functions} + + {out_variables} + +lib: _lib +ffi: Any +""" + Path("tcod/_libtcod.pyi").write_text(pyi) + subprocess.run(["ruff", "format", "--silent", Path("tcod/_libtcod.pyi")], check=True) # noqa: S603, S607 + if __name__ == "__main__": + write_hints() write_library_constants() diff --git a/build_sdl.py b/build_sdl.py index b47b426c..a1b8f6a2 100755 --- a/build_sdl.py +++ b/build_sdl.py @@ -1,38 +1,60 @@ -#!/usr/bin/env python3 +#!/usr/bin/env python """Build script to parse SDL headers and generate CFFI bindings.""" + from __future__ import annotations +import functools import io +import logging import os -import platform import re import shutil import subprocess import sys import zipfile from pathlib import Path +from tempfile import TemporaryDirectory from typing import Any -import pcpp # type: ignore +import pcpp # type: ignore[import-untyped] import requests # This script calls a lot of programs. # ruff: noqa: S603, S607 -BIT_SIZE, LINKAGE = platform.architecture() +# Ignore f-strings in logging, these will eventually be replaced with t-strings. +# ruff: noqa: G004 + +logger = logging.getLogger(__name__) + + +RE_MACHINE = re.compile(r".*\((.+)\)\]", re.DOTALL) + + +@functools.cache +def python_machine() -> str: + """Return the Python machine architecture (e.g. 'i386', 'AMD64', 'ARM64').""" + # Only needs to function correctly for Windows platforms. + match = RE_MACHINE.match(sys.version) + assert match, repr(sys.version) + (machine,) = match.groups() + machine = {"Intel": "i386"}.get(machine, machine) + logger.info(f"python_machine: {machine}") + return machine + # Reject versions of SDL older than this, update the requirements in the readme if you change this. -SDL_MIN_VERSION = (2, 0, 10) -# The SDL2 version to parse and export symbols from. -SDL2_PARSE_VERSION = os.environ.get("SDL_VERSION", "2.0.20") -# The SDL2 version to include in binary distributions. -SDL2_BUNDLE_VERSION = os.environ.get("SDL_VERSION", "2.28.1") +SDL_MIN_VERSION = (3, 2, 0) +# The SDL version to parse and export symbols from. +SDL_PARSE_VERSION = os.environ.get("SDL_VERSION", "3.2.16") +# The SDL version to include in binary distributions. +SDL_BUNDLE_VERSION = os.environ.get("SDL_VERSION", "3.2.16") # Used to remove excessive newlines in debug outputs. RE_NEWLINES = re.compile(r"\n\n+") # Functions using va_list need to be culled. -RE_VAFUNC = re.compile(r"^.*?\([^()]*va_list[^()]*\);$", re.MULTILINE) +RE_VAFUNC = re.compile(r"^.*?\([^()]*va_list[^()]*\)\s*;\s*$", re.MULTILINE) # Static inline functions need to be culled. RE_INLINE = re.compile(r"^static inline.*?^}$", re.MULTILINE | re.DOTALL) # Most SDL_PIXELFORMAT names need their values scrubbed. @@ -40,16 +62,49 @@ # Most SDLK names need their values scrubbed. RE_SDLK = re.compile(r"(?PSDLK_\w+) =.*?(?=,\n|}\n)") # Remove compile time assertions from the cdef. -RE_ASSERT = re.compile(r"^.*SDL_compile_time_assert.*$", re.MULTILINE) +RE_ASSERT = re.compile(r"^.*SDL_COMPILE_TIME_ASSERT.*$", re.MULTILINE) # Padding values need to be scrubbed. RE_PADDING = re.compile(r"padding\[[^;]*\];") # These structs have an unusual size when packed by SDL on 32-bit platforms. FLEXIBLE_STRUCTS = ( - "SDL_AudioCVT", + "SDL_CommonEvent", + "SDL_DisplayEvent", + "SDL_WindowEvent", + "SDL_KeyboardDeviceEvent", + "SDL_KeyboardEvent", + "SDL_TextEditingEvent", + "SDL_TextEditingCandidatesEvent", + "SDL_TextInputEvent", + "SDL_MouseDeviceEvent", + "SDL_MouseMotionEvent", + "SDL_MouseButtonEvent", + "SDL_MouseWheelEvent", + "SDL_JoyAxisEvent", + "SDL_JoyBallEvent", + "SDL_JoyHatEvent", + "SDL_JoyButtonEvent", + "SDL_JoyDeviceEvent", + "SDL_JoyBatteryEvent", + "SDL_GamepadAxisEvent", + "SDL_GamepadButtonEvent", + "SDL_GamepadDeviceEvent", + "SDL_GamepadTouchpadEvent", + "SDL_GamepadSensorEvent", + "SDL_AudioDeviceEvent", + "SDL_CameraDeviceEvent", + "SDL_RenderEvent", "SDL_TouchFingerEvent", - "SDL_MultiGestureEvent", - "SDL_DollarGestureEvent", + "SDL_PenProximityEvent", + "SDL_PenMotionEvent", + "SDL_PenTouchEvent", + "SDL_PenButtonEvent", + "SDL_PenAxisEvent", + "SDL_DropEvent", + "SDL_ClipboardEvent", + "SDL_SensorEvent", + "SDL_QuitEvent", + "SDL_UserEvent", ) # Other defined names which sometimes cause issues when parsed. @@ -59,6 +114,12 @@ "SDL_INLINE", "SDL_FORCE_INLINE", "SDL_FALLTHROUGH", + "SDL_HAS_FALLTHROUGH", + "SDL_NO_THREAD_SAFETY_ANALYSIS", + "SDL_SCOPED_CAPABILITY", + "SDL_NODISCARD", + "SDL_NOLONGLONG", + "SDL_WINAPI_FAMILY_PHONE", # Might show up in parsing and not in source. "SDL_ANDROID_EXTERNAL_STORAGE_READ", "SDL_ANDROID_EXTERNAL_STORAGE_WRITE", @@ -78,66 +139,75 @@ def check_sdl_version() -> None: - """Check the local SDL version on Linux distributions.""" + """Check the local SDL3 version on Linux distributions.""" if not sys.platform.startswith("linux"): return needed_version = f"{SDL_MIN_VERSION[0]}.{SDL_MIN_VERSION[1]}.{SDL_MIN_VERSION[2]}" try: - sdl_version_str = subprocess.check_output(["sdl2-config", "--version"], universal_newlines=True).strip() - except FileNotFoundError as exc: - msg = ( - "libsdl2-dev or equivalent must be installed on your system and must be at least version" - f" {needed_version}.\nsdl2-config must be on PATH." - ) - raise RuntimeError(msg) from exc - print(f"Found SDL {sdl_version_str}.") + sdl_version_str = subprocess.check_output( + ["pkg-config", "sdl3", "--modversion"], universal_newlines=True + ).strip() + except FileNotFoundError: + try: + sdl_version_str = subprocess.check_output(["sdl3-config", "--version"], universal_newlines=True).strip() + except FileNotFoundError as exc: + msg = ( + f"libsdl3-dev or equivalent must be installed on your system and must be at least version {needed_version}.\n" + "sdl3-config must be on PATH." + ) + raise RuntimeError(msg) from exc + except subprocess.CalledProcessError as exc: + if sys.version_info >= (3, 11): + exc.add_note(f"Note: {os.environ.get('PKG_CONFIG_PATH')=}") + raise + logger.info(f"Found SDL {sdl_version_str}.") sdl_version = tuple(int(s) for s in sdl_version_str.split(".")) if sdl_version < SDL_MIN_VERSION: msg = f"SDL version must be at least {needed_version}, (found {sdl_version_str})" raise RuntimeError(msg) -def get_sdl2_file(version: str) -> Path: - """Return a path to an SDL2 archive for the current platform. The archive is downloaded if missing.""" +def get_sdl_file(version: str) -> Path: + """Return a path to an SDL3 archive for the current platform. The archive is downloaded if missing.""" if sys.platform == "win32": - sdl2_file = f"SDL2-devel-{version}-VC.zip" + sdl_archive = f"SDL3-devel-{version}-VC.zip" else: assert sys.platform == "darwin" - sdl2_file = f"SDL2-{version}.dmg" - sdl2_local_file = Path("dependencies", sdl2_file) - sdl2_remote_file = f"https://www.libsdl.org/release/{sdl2_file}" - if not sdl2_local_file.exists(): - print(f"Downloading {sdl2_remote_file}") + sdl_archive = f"SDL3-{version}.dmg" + sdl_local_file = Path("dependencies", sdl_archive) + sdl_remote_url = f"https://www.libsdl.org/release/{sdl_archive}" + if not sdl_local_file.exists(): + logger.info(f"Downloading {sdl_remote_url}") Path("dependencies/").mkdir(parents=True, exist_ok=True) - with requests.get(sdl2_remote_file) as response: # noqa: S113 + with requests.get(sdl_remote_url) as response: # noqa: S113 response.raise_for_status() - sdl2_local_file.write_bytes(response.content) - return sdl2_local_file + sdl_local_file.write_bytes(response.content) + return sdl_local_file -def unpack_sdl2(version: str) -> Path: +def unpack_sdl(version: str) -> Path: """Return the path to an extracted SDL distribution. Creates it if missing.""" - sdl2_path = Path(f"dependencies/SDL2-{version}") + sdl_path = Path(f"dependencies/SDL3-{version}") if sys.platform == "darwin": - sdl2_dir = sdl2_path - sdl2_path /= "SDL2.framework" - if sdl2_path.exists(): - return sdl2_path - sdl2_arc = get_sdl2_file(version) - print(f"Extracting {sdl2_arc}") - if sdl2_arc.suffix == ".zip": - with zipfile.ZipFile(sdl2_arc) as zf: + sdl_dir = sdl_path + sdl_path /= "SDL3.framework" + if sdl_path.exists(): + return sdl_path + sdl_archive = get_sdl_file(version) + logger.info(f"Extracting {sdl_archive}") + if sdl_archive.suffix == ".zip": + with zipfile.ZipFile(sdl_archive) as zf: zf.extractall("dependencies/") elif sys.platform == "darwin": - assert sdl2_arc.suffix == ".dmg" - subprocess.check_call(["hdiutil", "mount", sdl2_arc]) - subprocess.check_call(["mkdir", "-p", sdl2_dir]) - subprocess.check_call(["cp", "-r", "/Volumes/SDL2/SDL2.framework", sdl2_dir]) - subprocess.check_call(["hdiutil", "unmount", "/Volumes/SDL2"]) - return sdl2_path + assert sdl_archive.suffix == ".dmg" + subprocess.check_call(["hdiutil", "mount", sdl_archive]) + subprocess.check_call(["mkdir", "-p", sdl_dir]) + subprocess.check_call(["cp", "-r", "/Volumes/SDL3/SDL3.xcframework/macos-arm64_x86_64/SDL3.framework", sdl_dir]) + subprocess.check_call(["hdiutil", "unmount", "/Volumes/SDL3"]) + return sdl_path -class SDLParser(pcpp.Preprocessor): # type: ignore +class SDLParser(pcpp.Preprocessor): # type: ignore[misc] """A modified preprocessor to output code in a format for CFFI.""" def __init__(self) -> None: @@ -155,12 +225,19 @@ def get_output(self) -> str: buffer.write(f"#define {name} ...\n") return buffer.getvalue() - def on_include_not_found(self, is_malformed: bool, is_system_include: bool, curdir: str, includepath: str) -> None: + def on_file_open(self, is_system_include: bool, includepath: str) -> Any: # noqa: ANN401, FBT001 + """Ignore includes other than SDL headers.""" + if not Path(includepath).parent.name == "SDL3": + raise FileNotFoundError + return super().on_file_open(is_system_include, includepath) + + def on_include_not_found(self, is_malformed: bool, is_system_include: bool, curdir: str, includepath: str) -> None: # noqa: ARG002, FBT001 """Remove bad includes such as stddef.h and stdarg.h.""" + assert "SDL3/SDL" not in includepath, (includepath, curdir) raise pcpp.OutputDirective(pcpp.Action.IgnoreAndRemove) - def _should_track_define(self, tokens: list[Any]) -> bool: - if len(tokens) < 3: + def _should_track_define(self, tokens: list[Any]) -> bool: # noqa: PLR0911 + if len(tokens) < 3: # noqa: PLR2004 return False if tokens[0].value in IGNORE_DEFINES: return False @@ -172,103 +249,137 @@ def _should_track_define(self, tokens: list[Any]) -> bool: return False # Likely calls a private function. if tokens[1].type == "CPP_LPAREN": return False # Function-like macro. - if len(tokens) >= 4 and tokens[2].type == "CPP_INTEGER" and tokens[3].type == "CPP_DOT": + if len(tokens) >= 4 and tokens[2].type == "CPP_INTEGER" and tokens[3].type == "CPP_DOT": # noqa: PLR2004 return False # Value is a floating point number. if tokens[0].value.startswith("SDL_PR") and (tokens[0].value.endswith("32") or tokens[0].value.endswith("64")): return False # Data type for printing, which is not needed. - return bool( - tokens[0].value.startswith("KMOD_") - or tokens[0].value.startswith("SDL_") - or tokens[0].value.startswith("AUDIO_") - ) + if tokens[0].value.startswith("SDL_PLATFORM_"): + return False # Ignore platform definitions + return bool(str(tokens[0].value).startswith(("SDL_", "SDLK_"))) def on_directive_handle( - self, directive: Any, tokens: list[Any], if_passthru: bool, preceding_tokens: list[Any] # noqa: ANN401 + self, + directive: Any, # noqa: ANN401 + tokens: list[Any], + if_passthru: bool, # noqa: FBT001 + preceding_tokens: list[Any], ) -> Any: # noqa: ANN401 """Catch and store definitions.""" if directive.value == "define" and self._should_track_define(tokens): if tokens[2].type == "CPP_STRING": self.known_string_defines[tokens[0].value] = tokens[2].value + elif tokens[2].value in self.known_string_defines: + self.known_string_defines[tokens[0].value] = "..." else: self.known_defines.add(tokens[0].value) return super().on_directive_handle(directive, tokens, if_passthru, preceding_tokens) -check_sdl_version() +def get_emscripten_include_dir() -> Path: + """Find and return the Emscripten include dir.""" + # None of the EMSDK environment variables exist! Search PATH for Emscripten as a workaround + for path in os.environ["PATH"].split(os.pathsep)[::-1]: + if Path(path).match("upstream/emscripten"): + return Path(path, "system/include").resolve(strict=True) + raise AssertionError(os.environ["PATH"]) -if sys.platform in ["win32", "darwin"]: - SDL2_PARSE_PATH = unpack_sdl2(SDL2_PARSE_VERSION) - SDL2_BUNDLE_PATH = unpack_sdl2(SDL2_BUNDLE_VERSION) -SDL2_INCLUDE: Path -if sys.platform == "win32": - SDL2_INCLUDE = SDL2_PARSE_PATH / "include" -elif sys.platform == "darwin": - SDL2_INCLUDE = SDL2_PARSE_PATH / "Versions/A/Headers" +check_sdl_version() + +SDL_PARSE_PATH: Path | None = None +SDL_BUNDLE_PATH: Path | None = None +if (sys.platform == "win32" or sys.platform == "darwin") and "PYODIDE" not in os.environ: + SDL_PARSE_PATH = unpack_sdl(SDL_PARSE_VERSION) + SDL_BUNDLE_PATH = unpack_sdl(SDL_BUNDLE_VERSION) + +SDL_INCLUDE: Path +if sys.platform == "win32" and SDL_PARSE_PATH is not None: + SDL_INCLUDE = SDL_PARSE_PATH / "include" +elif sys.platform == "darwin" and SDL_PARSE_PATH is not None: + SDL_INCLUDE = SDL_PARSE_PATH / "Versions/A/Headers" else: # Unix matches = re.findall( r"-I(\S+)", - subprocess.check_output(["sdl2-config", "--cflags"], universal_newlines=True), + subprocess.check_output(["pkg-config", "sdl3", "--cflags"], universal_newlines=True), ) - assert matches + if not matches: + matches = ["/usr/include"] for match in matches: - if Path(match, "SDL_stdinc.h").is_file(): - SDL2_INCLUDE = match - assert SDL2_INCLUDE + if Path(match, "SDL3/SDL.h").is_file(): + SDL_INCLUDE = Path(match) + break + else: + raise AssertionError(matches) + assert SDL_INCLUDE +logger.info(f"{SDL_INCLUDE=}") EXTRA_CDEF = """ #define SDLK_SCANCODE_MASK ... extern "Python" { // SDL_AudioCallback callback. -void _sdl_audio_callback(void* userdata, Uint8* stream, int len); +void _sdl_audio_stream_callback(void* userdata, SDL_AudioStream *stream, int additional_amount, int total_amount); // SDL to Python log function. void _sdl_log_output_function(void *userdata, int category, SDL_LogPriority priority, const char *message); // Generic event watcher callback. -int _sdl_event_watcher(void* userdata, SDL_Event* event); +bool _sdl_event_watcher(void* userdata, SDL_Event* event); } """ -def get_cdef() -> str: +def get_cdef() -> tuple[str, dict[str, str]]: """Return the parsed code of SDL for CFFI.""" - parser = SDLParser() - parser.add_path(SDL2_INCLUDE) - parser.parse( + with TemporaryDirectory() as temp_dir: + # Add a false SDL_oldnames.h to prevent old symbols from being collected + fake_header_dir = Path(temp_dir, "SDL3") + fake_header_dir.mkdir() + (fake_header_dir / "SDL_oldnames.h").write_text("") + + parser = SDLParser() + parser.add_path(temp_dir) + parser.add_path(SDL_INCLUDE) + if Path(SDL_INCLUDE, "../Headers/SDL.h").exists(): # Using MacOS dmg archive + fake_sdl_dir = Path(SDL_INCLUDE, "SDL3") + if not fake_sdl_dir.exists(): + fake_sdl_dir.mkdir(exist_ok=False) + for file in SDL_INCLUDE.glob("SDL*.h"): + shutil.copyfile(file, fake_sdl_dir / file.name) + else: # Regular path + assert Path(SDL_INCLUDE, "SDL3/SDL.h").exists(), SDL_INCLUDE + parser.parse( + """ + // Remove extern keyword. + #define extern + // Ignore some SDL assert statements. + #define DOXYGEN_SHOULD_IGNORE_THIS + #define SDL_COMPILE_TIME_ASSERT(x, y) + + #define _SIZE_T_DEFINED_ + typedef int... size_t; + #define bool _Bool + + #define SDL_oldnames_h_ + + #include """ - // Remove extern keyword. - #define extern - // Ignore some SDL assert statements. - #define DOXYGEN_SHOULD_IGNORE_THIS - - #define _SIZE_T_DEFINED_ - typedef int... size_t; - - // Skip these headers. - #define SDL_atomic_h_ - #define SDL_thread_h_ - - #include - """ - ) - sdl2_cdef = parser.get_output() - sdl2_cdef = RE_VAFUNC.sub("", sdl2_cdef) - sdl2_cdef = RE_INLINE.sub("", sdl2_cdef) - sdl2_cdef = RE_PIXELFORMAT.sub(r"\g = ...", sdl2_cdef) - sdl2_cdef = RE_SDLK.sub(r"\g = ...", sdl2_cdef) - sdl2_cdef = RE_NEWLINES.sub("\n", sdl2_cdef) - sdl2_cdef = RE_ASSERT.sub("", sdl2_cdef) - sdl2_cdef = RE_PADDING.sub("padding[...];", sdl2_cdef) - sdl2_cdef = ( - sdl2_cdef.replace("int SDL_main(int argc, char *argv[]);", "") - .replace("typedef unsigned int uintptr_t;", "typedef int... uintptr_t;") - .replace("typedef unsigned int size_t;", "typedef int... size_t;") + ) + sdl_cdef = parser.get_output() + + sdl_cdef = sdl_cdef.replace("_Bool", "bool") + sdl_cdef = RE_VAFUNC.sub("", sdl_cdef) + sdl_cdef = RE_INLINE.sub("", sdl_cdef) + sdl_cdef = RE_PIXELFORMAT.sub(r"\g = ...", sdl_cdef) + sdl_cdef = RE_SDLK.sub(r"\g = ...", sdl_cdef) + sdl_cdef = RE_NEWLINES.sub("\n", sdl_cdef) + sdl_cdef = RE_PADDING.sub("padding[...];", sdl_cdef) + sdl_cdef = sdl_cdef.replace("typedef unsigned int uintptr_t;", "typedef int... uintptr_t;").replace( + "typedef unsigned int size_t;", "typedef int... size_t;" ) for name in FLEXIBLE_STRUCTS: - sdl2_cdef = sdl2_cdef.replace(f"}} {name};", f"...;}} {name};") - return sdl2_cdef + EXTRA_CDEF + sdl_cdef = sdl_cdef.replace(f"}} {name};", f"...;}} {name};") + return sdl_cdef + EXTRA_CDEF, parser.known_string_defines include_dirs: list[str] = [] @@ -278,35 +389,41 @@ def get_cdef() -> str: libraries: list[str] = [] library_dirs: list[str] = [] - -if sys.platform == "darwin": - extra_link_args += ["-framework", "SDL2"] +if "PYODIDE" in os.environ: + pass +elif sys.platform == "darwin": + extra_link_args += ["-framework", "SDL3"] else: - libraries += ["SDL2"] - -# Bundle the Windows SDL2 DLL. -if sys.platform == "win32": - include_dirs.append(str(SDL2_INCLUDE)) - ARCH_MAPPING = {"32bit": "x86", "64bit": "x64"} - SDL2_LIB_DIR = Path(SDL2_BUNDLE_PATH, "lib/", ARCH_MAPPING[BIT_SIZE]) - library_dirs.append(str(SDL2_LIB_DIR)) - SDL2_LIB_DEST = Path("tcod", ARCH_MAPPING[BIT_SIZE]) - SDL2_LIB_DEST.mkdir(exist_ok=True) - SDL2_LIB_DEST_FILE = SDL2_LIB_DEST / "SDL2.dll" - SDL2_LIB_FILE = SDL2_LIB_DIR / "SDL2.dll" - if not SDL2_LIB_DEST_FILE.exists() or SDL2_LIB_FILE.read_bytes() != SDL2_LIB_DEST_FILE.read_bytes(): - shutil.copy(SDL2_LIB_FILE, SDL2_LIB_DEST_FILE) - -# Link to the SDL2 framework on MacOS. + libraries += ["SDL3"] + +# Bundle the Windows SDL DLL. +if sys.platform == "win32" and SDL_BUNDLE_PATH is not None: + include_dirs.append(str(SDL_INCLUDE)) + ARCH_MAPPING = {"i386": "x86", "AMD64": "x64", "ARM64": "arm64"} + SDL_LIB_DIR = Path(SDL_BUNDLE_PATH, "lib/", ARCH_MAPPING[python_machine()]) + library_dirs.append(str(SDL_LIB_DIR)) + SDL_LIB_DEST = Path("tcod", ARCH_MAPPING[python_machine()]) + SDL_LIB_DEST.mkdir(exist_ok=True) + SDL_LIB_DEST_FILE = SDL_LIB_DEST / "SDL3.dll" + SDL_LIB_FILE = SDL_LIB_DIR / "SDL3.dll" + if not SDL_LIB_DEST_FILE.exists() or SDL_LIB_FILE.read_bytes() != SDL_LIB_DEST_FILE.read_bytes(): + shutil.copy(SDL_LIB_FILE, SDL_LIB_DEST_FILE) + +# Link to the SDL framework on MacOS. # Delocate will bundle the binaries in a later step. -if sys.platform == "darwin": - HEADER_DIR = Path(SDL2_PARSE_PATH, "Headers") - include_dirs.append(HEADER_DIR) - extra_link_args += [f"-F{SDL2_BUNDLE_PATH}/.."] - extra_link_args += ["-rpath", f"{SDL2_BUNDLE_PATH}/.."] +if sys.platform == "darwin" and SDL_BUNDLE_PATH is not None: + include_dirs.append(SDL_INCLUDE) + extra_link_args += [f"-F{SDL_BUNDLE_PATH}/.."] + extra_link_args += ["-rpath", f"{SDL_BUNDLE_PATH}/.."] extra_link_args += ["-rpath", "/usr/local/opt/llvm/lib/"] -# Use sdl2-config to link to SDL2 on Linux. -if sys.platform not in ["win32", "darwin"]: - extra_compile_args += subprocess.check_output(["sdl2-config", "--cflags"], universal_newlines=True).strip().split() - extra_link_args += subprocess.check_output(["sdl2-config", "--libs"], universal_newlines=True).strip().split() +if "PYODIDE" in os.environ: + extra_compile_args += ["--use-port=sdl3"] +elif sys.platform not in ["win32", "darwin"]: + # Use sdl-config to link to SDL on Linux. + extra_compile_args += ( + subprocess.check_output(["pkg-config", "sdl3", "--cflags"], universal_newlines=True).strip().split() + ) + extra_link_args += ( + subprocess.check_output(["pkg-config", "sdl3", "--libs"], universal_newlines=True).strip().split() + ) diff --git a/docs/_templates/page.html b/docs/_templates/page.html new file mode 100644 index 00000000..213c761f --- /dev/null +++ b/docs/_templates/page.html @@ -0,0 +1,21 @@ +{% extends "!page.html" %} +{% block content %} + {{ super() }} + + +{% endblock %} diff --git a/docs/conf.py b/docs/conf.py index 2fece512..70be25bf 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,5 +1,5 @@ -"""Sphinx config file.""" -# tdl documentation build configuration file, created by +"""Sphinx config file.""" # noqa: INP001 +# python-tcod documentation build configuration file, created by # sphinx-quickstart on Fri Nov 25 12:49:46 2016. # # This file is execfile()d with the current directory set to its @@ -25,6 +25,10 @@ sys.path.insert(0, str(Path("..").resolve(strict=True))) +THIS_DIR = Path(__file__).parent + +# ruff: noqa: ERA001 + # -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. @@ -61,7 +65,7 @@ # General information about the project. project = "python-tcod" -copyright = "2009-2023, Kyle Benesch" +copyright = "2009-2026, Kyle Benesch" # noqa: A001 author = "Kyle Benesch" # The version info for the project you're documenting, acts as replacement for @@ -69,20 +73,20 @@ # built documents. # # The full version, including alpha/beta/rc tags. -git_describe = subprocess.run( - ["git", "describe", "--abbrev=0"], # noqa: S603, S607 +release = subprocess.run( + ["git", "describe", "--abbrev=0"], # noqa: S607 stdout=subprocess.PIPE, text=True, check=True, -) -release = git_describe.stdout.strip() +).stdout.strip() assert release -print("release version: %r" % release) +print(f"release version: {release!r}") # The short X.Y version. match_version = re.match(r"([0-9]+\.[0-9]+).*?", release) assert match_version version = match_version.group() +print(f"short version: {version!r}") # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. @@ -103,7 +107,7 @@ # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. # This patterns also effect to html_static_path and html_extra_path -exclude_patterns = ["_build", "Thumbs.db", ".DS_Store", "/epilog.rst"] +exclude_patterns = ["_build", "Thumbs.db", ".DS_Store", "epilog.rst", "prolog.rst"] # The reST default role (used for this markup: `text`) to use for all # documents. @@ -163,9 +167,9 @@ # html_theme_path = [] # The name for this set of Sphinx documents. -# " v documentation" by default. +# " documentation" by default. # -# html_title = u'tdl v1' +html_title = f"{project} {release} documentation" # A shorter title for the navigation bar. Default is the same as html_title. # @@ -378,13 +382,14 @@ napoleon_use_param = True napoleon_use_rtype = True -rst_prolog = ".. include:: /prolog.rst" # Added to the beginning of every source file. -rst_epilog = ".. include:: /epilog.rst" # Added to the end of every source file. +rst_prolog = (THIS_DIR / "prolog.rst").read_text(encoding="utf-8") # Added to the beginning of every source file. +rst_epilog = (THIS_DIR / "epilog.rst").read_text(encoding="utf-8") # Added to the end of every source file. # Example configuration for intersphinx: refer to the Python standard library. intersphinx_mapping = { "python": ("https://docs.python.org/3/", None), "numpy": ("https://numpy.org/doc/stable/", None), + "tcod-ecs": ("https://python-tcod-ecs.readthedocs.io/en/latest/", None), } os.environ["READTHEDOCS"] = "True" diff --git a/docs/epilog.rst b/docs/epilog.rst index e69de29b..c3ee806c 100644 --- a/docs/epilog.rst +++ b/docs/epilog.rst @@ -0,0 +1,3 @@ + +.. _tcod-ecs: https://github.com/HexDecimal/python-tcod-ecs +.. _Flecs: https://github.com/SanderMertens/flecs diff --git a/docs/sdl/joystick.rst b/docs/sdl/joystick.rst index 60c7c26e..397cd1e2 100644 --- a/docs/sdl/joystick.rst +++ b/docs/sdl/joystick.rst @@ -3,9 +3,3 @@ SDL Joystick Support ``tcod.sdl.joystick`` .. automodule:: tcod.sdl.joystick :members: - :exclude-members: - Power - -.. autoclass:: tcod.sdl.joystick.Power - :members: - :member-order: bysource diff --git a/docs/tcod/event.rst b/docs/tcod/event.rst index 10aed249..1fd5c7bb 100644 --- a/docs/tcod/event.rst +++ b/docs/tcod/event.rst @@ -1,8 +1,9 @@ -SDL2 Event Handling ``tcod.event`` +SDL Event Handling ``tcod.event`` ================================== .. automodule:: tcod.event :members: + :inherited-members: object, int, str, tuple :member-order: bysource :exclude-members: KeySym, Scancode, Modifier, get, wait diff --git a/docs/tcod/getting-started.rst b/docs/tcod/getting-started.rst index c7634149..aabb4f7f 100644 --- a/docs/tcod/getting-started.rst +++ b/docs/tcod/getting-started.rst @@ -19,9 +19,9 @@ console will be stretched to fit the window. You can add arguments to :any:`Context.present` to fix the aspect ratio or only scale the console by integer increments. -Example:: +.. code-block:: python - #!/usr/bin/env python3 + #!/usr/bin/env python # Make sure 'dejavu10x10_gs_tc.png' is in the same directory as this script. import tcod.console import tcod.context @@ -38,14 +38,14 @@ Example:: "dejavu10x10_gs_tc.png", 32, 8, tcod.tileset.CHARMAP_TCOD, ) # Create the main console. - console = tcod.console.Console(WIDTH, HEIGHT, order="F") + console = tcod.console.Console(WIDTH, HEIGHT) # Create a window based on this console and tileset. with tcod.context.new( # New window for a console of size columns×rows. columns=console.width, rows=console.height, tileset=tileset, ) as context: while True: # Main loop, runs until SystemExit is raised. console.clear() - console.print(x=0, y=0, string="Hello World!") + console.print(x=0, y=0, text="Hello World!") context.present(console) # Show the console. # This event loop will wait until at least one event is processed before exiting. @@ -53,8 +53,9 @@ Example:: for event in tcod.event.wait(): context.convert_event(event) # Sets tile coordinates for mouse events. print(event) # Print event names and attributes. - if isinstance(event, tcod.event.Quit): - raise SystemExit() + match event: + case tcod.event.Quit(): + raise SystemExit # The window will be closed after the above with-block exits. @@ -87,9 +88,9 @@ You can call :any:`Context.new_console` every frame or only when the window is resized. This example creates a new console every frame instead of clearing the console every frame and replacing it only on resizing the window. -Example:: +.. code-block:: python - #!/usr/bin/env python3 + #!/usr/bin/env python import tcod.context import tcod.event @@ -103,17 +104,18 @@ Example:: width=WIDTH, height=HEIGHT, sdl_window_flags=FLAGS ) as context: while True: - console = context.new_console(order="F") # Console size based on window resolution and tile size. + console = context.new_console() # Console size based on window resolution and tile size. console.print(0, 0, "Hello World") context.present(console, integer_scaling=True) for event in tcod.event.wait(): - context.convert_event(event) # Sets tile coordinates for mouse events. + event = context.convert_event(event) # Sets tile coordinates for mouse events. print(event) # Print event names and attributes. - if isinstance(event, tcod.event.Quit): - raise SystemExit() - elif isinstance(event, tcod.event.WindowResized) and event.type == "WindowSizeChanged": - pass # The next call to context.new_console may return a different size. + match event: + case tcod.event.Quit(): + raise SystemExit + case tcod.event.WindowResized(width=width, height=height): # Size in pixels + pass # The next call to context.new_console may return a different size. if __name__ == "__main__": diff --git a/docs/tutorial/index.rst b/docs/tutorial/index.rst index 2277ab62..b43a8971 100644 --- a/docs/tutorial/index.rst +++ b/docs/tutorial/index.rst @@ -3,6 +3,12 @@ Tutorial .. include:: notice.rst +.. note:: + This a Python tutorial reliant on a Modern ECS implementation. + In this case `tcod-ecs`_ will be used. + Most other Python ECS libraries do not support entity relationships and arbitrary tags required by this tutorial. + If you wish to use this tutorial with another language you may need a Modern ECS implementation on par with `Flecs`_. + .. toctree:: :maxdepth: 1 :glob: diff --git a/docs/tutorial/part-00.rst b/docs/tutorial/part-00.rst index cb97db3f..a00a7fe9 100644 --- a/docs/tutorial/part-00.rst +++ b/docs/tutorial/part-00.rst @@ -23,9 +23,13 @@ First script First start with a modern top-level script. Create a script in the project root folder called ``main.py`` which checks :python:`if __name__ == "__main__":` and calls a ``main`` function. +Any modern script using type-hinting will also have :python:`from __future__ import annotations` near the top. .. code-block:: python + from __future__ import annotations + + def main() -> None: print("Hello World!") diff --git a/docs/tutorial/part-01.rst b/docs/tutorial/part-01.rst index 028fc40a..b18b7531 100644 --- a/docs/tutorial/part-01.rst +++ b/docs/tutorial/part-01.rst @@ -17,6 +17,9 @@ You should have ``main.py`` script from :ref:`part-0`: .. code-block:: python + from __future__ import annotations + + def main() -> None: ... @@ -36,6 +39,7 @@ These kinds of tilesets are always loaded with :python:`columns=16, rows=16, cha Use the string :python:`"data/Alloy_curses_12x12.png"` to refer to the path of the tileset. [#why_not_pathlib]_ Load the tileset with :any:`tcod.tileset.load_tilesheet`. +Pass the tileset to :any:`tcod.tileset.procedural_block_elements` which will fill in most `Block Elements `_ missing from `Code Page 437 `_. Then pass the tileset to :any:`tcod.context.new`, you only need to provide the ``tileset`` parameter. :any:`tcod.context.new` returns a :any:`Context` which will be used with Python's :python:`with` statement. @@ -45,9 +49,10 @@ The new block can't be empty, so add :python:`pass` to the with statement body. These functions are part of modules which have not been imported yet, so new imports for ``tcod.context`` and ``tcod.tileset`` must be added to the top of the script. .. code-block:: python - :emphasize-lines: 2,3,8-12 + :emphasize-lines: 3,4,8-14 + + from __future__ import annotations - ... import tcod.context # Add these imports import tcod.tileset @@ -57,9 +62,13 @@ These functions are part of modules which have not been imported yet, so new imp tileset = tcod.tileset.load_tilesheet( "data/Alloy_curses_12x12.png", columns=16, rows=16, charmap=tcod.tileset.CHARMAP_CP437 ) + tcod.tileset.procedural_block_elements(tileset=tileset) with tcod.context.new(tileset=tileset) as context: pass # The window will stay open for the duration of this block - ... + + + if __name__ == "__main__": + main() If an import fails that means you do not have ``tcod`` installed on the Python environment you just used to run the script. If you use an IDE then make sure the Python environment it is using is correct and then run :shell:`pip install tcod` from the shell terminal within that IDE. @@ -85,14 +94,17 @@ Use the code :python:`for event in tcod.event.wait():` to begin handing events. In the event loop start with the line :python:`print(event)` so that all events can be viewed from the program output. Then test if an event is for closing the window with :python:`if isinstance(event, tcod.event.Quit):`. -If this is True then you should exit the function with :python:`raise SystemExit()`. [#why_raise]_ +If this is True then you should exit the function with :python:`raise SystemExit`. [#why_raise]_ .. code-block:: python - :emphasize-lines: 2,3,11-19 + :emphasize-lines: 3,5,15-23 + + from __future__ import annotations - ... import tcod.console + import tcod.context import tcod.event + import tcod.tileset def main() -> None: @@ -100,6 +112,7 @@ If this is True then you should exit the function with :python:`raise SystemExit tileset = tcod.tileset.load_tilesheet( "data/Alloy_curses_12x12.png", columns=16, rows=16, charmap=tcod.tileset.CHARMAP_CP437 ) + tcod.tileset.procedural_block_elements(tileset=tileset) console = tcod.console.Console(80, 50) console.print(0, 0, "Hello World") # Test text by printing "Hello World" to the console with tcod.context.new(console=console, tileset=tileset) as context: @@ -108,8 +121,11 @@ If this is True then you should exit the function with :python:`raise SystemExit for event in tcod.event.wait(): # Event loop, blocks until pending events exist print(event) if isinstance(event, tcod.event.Quit): - raise SystemExit() - ... + raise SystemExit + + + if __name__ == "__main__": + main() If you run this then you get a window saying :python:`"Hello World"`. The window can be resized and the console will be stretched to fit the new resolution. @@ -124,7 +140,7 @@ The next step is to change state based on user input. Like ``tcod`` you'll need to install ``attrs`` with Pip, such as with :shell:`pip install attrs`. Start by adding an ``attrs`` class called ``ExampleState``. -This a normal class with the :python:`@attrs.define(eq=False)` decorator added. +This a normal class with the :python:`@attrs.define()` decorator added. This class should hold coordinates for the player. It should also have a ``on_draw`` method which takes :any:`tcod.console.Console` as a parameter and marks the player position on it. @@ -135,12 +151,18 @@ The parameters for ``on_draw`` are ``self`` because this is an instance method a Call this method using the players current coordinates and the :python:`"@"` character. .. code-block:: python + :emphasize-lines: 3,10-21 + + from __future__ import annotations - ... import attrs + import tcod.console + import tcod.context + import tcod.event + import tcod.tileset - @attrs.define(eq=False) + @attrs.define() class ExampleState: """Example state with a hard-coded player position.""" @@ -152,6 +174,7 @@ Call this method using the players current coordinates and the :python:`"@"` cha def on_draw(self, console: tcod.console.Console) -> None: """Draw the player glyph.""" console.print(self.player_x, self.player_y, "@") + ... Now remove the :python:`console.print(0, 0, "Hello World")` line from ``main``. @@ -182,8 +205,11 @@ Modify the drawing routine so that the console is cleared, then passed to :pytho for event in tcod.event.wait(): print(event) if isinstance(event, tcod.event.Quit): - raise SystemExit() - ... + raise SystemExit + + + if __name__ == "__main__": + main() Now if you run the script you'll see ``@``. @@ -201,18 +227,39 @@ Make a case for each arrow key: ``LEFT`` ``RIGHT`` ``UP`` ``DOWN`` and move the Since events are printed you can check the :any:`KeySym` of a key by pressing that key and looking at the printed output. See :any:`KeySym` for a list of all keys. +Finally replace the event handling code in ``main`` to defer to the states ``on_event`` method. +The full script so far is: + .. code-block:: python + :emphasize-lines: 23-35,53 - ... - @attrs.define(eq=False) + from __future__ import annotations + + import attrs + import tcod.console + import tcod.context + import tcod.event + import tcod.tileset + + + @attrs.define() class ExampleState: - ... + """Example state with a hard-coded player position.""" + + player_x: int + """Player X position, left-most position is zero.""" + player_y: int + """Player Y position, top-most position is zero.""" + + def on_draw(self, console: tcod.console.Console) -> None: + """Draw the player glyph.""" + console.print(self.player_x, self.player_y, "@") def on_event(self, event: tcod.event.Event) -> None: """Move the player on events and handle exiting. Movement is hard-coded.""" match event: case tcod.event.Quit(): - raise SystemExit() + raise SystemExit case tcod.event.KeyDown(sym=tcod.event.KeySym.LEFT): self.player_x -= 1 case tcod.event.KeyDown(sym=tcod.event.KeySym.RIGHT): @@ -221,16 +268,15 @@ See :any:`KeySym` for a list of all keys. self.player_y -= 1 case tcod.event.KeyDown(sym=tcod.event.KeySym.DOWN): self.player_y += 1 - ... - -Now replace the event handling code in ``main`` to defer to the states ``on_event`` method. -.. code-block:: python - :emphasize-lines: 12 - ... def main() -> None: - ... + """Run ExampleState.""" + tileset = tcod.tileset.load_tilesheet( + "data/Alloy_curses_12x12.png", columns=16, rows=16, charmap=tcod.tileset.CHARMAP_CP437 + ) + tcod.tileset.procedural_block_elements(tileset=tileset) + console = tcod.console.Console(80, 50) state = ExampleState(player_x=console.width // 2, player_y=console.height // 2) with tcod.context.new(console=console, tileset=tileset) as context: while True: @@ -240,7 +286,10 @@ Now replace the event handling code in ``main`` to defer to the states ``on_even for event in tcod.event.wait(): print(event) state.on_event(event) # Pass events to the state - ... + + + if __name__ == "__main__": + main() Now when you run this script you have a player character you can move around with the arrow keys before closing the window. @@ -253,11 +302,11 @@ You can review the part-1 source code `here Self:` to allow this syntax. +Unpack the input with :python:`x, y = direction`. +:python:`self.__class__` is the current class so :python:`self.__class__(self.x + x, self.y + y)` will create a new instance with the direction added to the previous values. + +The new class will look like this: + +.. code-block:: python + + @attrs.define(frozen=True) + class Position: + """An entities position.""" + + x: int + y: int + + def __add__(self, direction: tuple[int, int]) -> Self: + """Add a vector to this position.""" + x, y = direction + return self.__class__(self.x + x, self.y + y) + +Because ``Position`` is immutable, ``tcod-ecs`` is able to reliably track changes to this component. +Normally you can only query entities by which components they have. +A callback can be registered with ``tcod-ecs`` to mirror component values as tags. +This allows querying an entity by its exact position. + +Add :python:`import tcod.ecs.callbacks` and :python:`from tcod.ecs import Entity`. +Then create the new function :python:`def on_position_changed(entity: Entity, old: Position | None, new: Position | None) -> None:` decorated with :python:`@tcod.ecs.callbacks.register_component_changed(component=Position)`. +This function is called when the ``Position`` component is either added, removed, or modified by assignment. +The goal of this function is to mirror the current position to the :class:`set`-like attribute ``entity.tags``. + +:python:`if old == new:` then a position was assigned its own value or an equivalent value. +The cost of discarding and adding the same value can sometimes be high so this case should be guarded and ignored. +:python:`if old is not None:` then the value tracked by ``entity.tags`` is outdated and must be removed. +:python:`if new is not None:` then ``new`` is the up-to-date value to be tracked by ``entity.tags``. + +The function should look like this: + +.. code-block:: python + + @tcod.ecs.callbacks.register_component_changed(component=Position) + def on_position_changed(entity: Entity, old: Position | None, new: Position | None) -> None: + """Mirror position components as a tag.""" + if old == new: # New position is equivalent to its previous value + return # Ignore and return + if old is not None: # Position component removed or changed + entity.tags.discard(old) # Remove old position from tags + if new is not None: # Position component added or changed + entity.tags.add(new) # Add new position to tags + +Next is the ``Graphic`` component. +This will have the attributes :python:`ch: int = ord("!")` and :python:`fg: tuple[int, int, int] = (255, 255, 255)`. +By default all new components should be marked as frozen. + +.. code-block:: python + + @attrs.define(frozen=True) + class Graphic: + """An entities icon and color.""" + + ch: int = ord("!") + fg: tuple[int, int, int] = (255, 255, 255) + +One last component: ``Gold``. +Define this as :python:`Gold: Final = ("Gold", int)`. +``(name, type)`` is tcod-ecs specific syntax to handle multiple components sharing the same type. + +.. code-block:: python + + Gold: Final = ("Gold", int) + """Amount of gold.""" + +That was the last component. +The ``game/components.py`` module should look like this: + +.. code-block:: python + + """Collection of common components.""" + + from __future__ import annotations + + from typing import Final, Self + + import attrs + import tcod.ecs.callbacks + from tcod.ecs import Entity + + + @attrs.define(frozen=True) + class Position: + """An entities position.""" + + x: int + y: int + + def __add__(self, direction: tuple[int, int]) -> Self: + """Add a vector to this position.""" + x, y = direction + return self.__class__(self.x + x, self.y + y) + + + @tcod.ecs.callbacks.register_component_changed(component=Position) + def on_position_changed(entity: Entity, old: Position | None, new: Position | None) -> None: + """Mirror position components as a tag.""" + if old == new: + return + if old is not None: + entity.tags.discard(old) + if new is not None: + entity.tags.add(new) + + + @attrs.define(frozen=True) + class Graphic: + """An entities icon and color.""" + + ch: int = ord("!") + fg: tuple[int, int, int] = (255, 255, 255) + + + Gold: Final = ("Gold", int) + """Amount of gold.""" + +ECS entities and registry +============================================================================== + +Now it is time to create entities. +To do that you need to create the ECS registry. + +Make a new script called ``game/world_tools.py``. +This module will be used to create the ECS registry. + +Random numbers from :mod:`random` will be used. +In this case we want to use ``Random`` as a component so add :python:`from random import Random`. +Get the registry with :python:`from tcod.ecs import Registry`. +Collect all our components and tags with :python:`from game.components import Gold, Graphic, Position` and :python:`from game.tags import IsActor, IsItem, IsPlayer`. + +This module will have one function: :python:`def new_world() -> Registry:`. +Think of the ECS registry as containing the world since this is how it will be used. +Start this function with :python:`world = Registry()`. + +Entities are referenced with the syntax :python:`world[unique_id]`. +If the same ``unique_id`` is used then you will access the same entity. +:python:`new_entity = world[object()]` is the syntax to spawn new entities because ``object()`` is always unique. +Whenever a global entity is needed then :python:`world[None]` will be used. + +Create an instance of :python:`Random()` and assign it to both :python:`world[None].components[Random]` and ``rng``. +This can done on one line with :python:`rng = world[None].components[Random] = Random()`. + +Next create the player entity with :python:`player = world[object()]`. +Assign the following components to the new player entity: :python:`player.components[Position] = Position(5, 5)`, :python:`player.components[Graphic] = Graphic(ord("@"))`, and :python:`player.components[Gold] = 0`. +Then update the players tags with :python:`player.tags |= {IsPlayer, IsActor}`. + +To add some variety we will scatter gold randomly across the world. +Start a for-loop with :python:`for _ in range(10):` then create a ``gold`` entity in this loop. + +The ``Random`` instance ``rng`` has access to functions from Python's random module such as :any:`random.randint`. +Set ``Position`` to :python:`Position(rng.randint(0, 20), rng.randint(0, 20))`. +Set ``Graphic`` to :python:`Graphic(ord("$"), fg=(255, 255, 0))`. +Set ``Gold`` to :python:`rng.randint(1, 10)`. +Then add ``IsItem`` as a tag. + +Once the for-loop exits then :python:`return world`. +Make sure :python:`return` has the correct indentation and is not part of the for-loop or else you will only spawn one gold. + +``game/world_tools.py`` should look like this: + +.. code-block:: python + + """Functions for working with worlds.""" + + from __future__ import annotations + + from random import Random + + from tcod.ecs import Registry + + from game.components import Gold, Graphic, Position + from game.tags import IsActor, IsItem, IsPlayer + + + def new_world() -> Registry: + """Return a freshly generated world.""" + world = Registry() + + rng = world[None].components[Random] = Random() + + player = world[object()] + player.components[Position] = Position(5, 5) + player.components[Graphic] = Graphic(ord("@")) + player.components[Gold] = 0 + player.tags |= {IsPlayer, IsActor} + + for _ in range(10): + gold = world[object()] + gold.components[Position] = Position(rng.randint(0, 20), rng.randint(0, 20)) + gold.components[Graphic] = Graphic(ord("$"), fg=(255, 255, 0)) + gold.components[Gold] = rng.randint(1, 10) + gold.tags |= {IsItem} + + return world + +New InGame state +============================================================================== + +Now there is a new ECS world but the example state does not know how to render it. +A new state needs to be made which is aware of the new entities. + +Before adding a new state it is time to add a more complete set of directional keys. +Create a new module called ``game/constants.py``. +Keys will be mapped to direction using a dictionary which can be reused anytime we want to know how a key translates to a direction. +Use :python:`from tcod.event import KeySym` to make ``KeySym`` enums easier to write. + +``game/constants.py`` should look like this: + +.. code-block:: python + + """Global constants are stored here.""" + + from __future__ import annotations + + from typing import Final + + from tcod.event import KeySym + + DIRECTION_KEYS: Final = { + # Arrow keys + KeySym.LEFT: (-1, 0), + KeySym.RIGHT: (1, 0), + KeySym.UP: (0, -1), + KeySym.DOWN: (0, 1), + # Arrow key diagonals + KeySym.HOME: (-1, -1), + KeySym.END: (-1, 1), + KeySym.PAGEUP: (1, -1), + KeySym.PAGEDOWN: (1, 1), + # Keypad + KeySym.KP_4: (-1, 0), + KeySym.KP_6: (1, 0), + KeySym.KP_8: (0, -1), + KeySym.KP_2: (0, 1), + KeySym.KP_7: (-1, -1), + KeySym.KP_1: (-1, 1), + KeySym.KP_9: (1, -1), + KeySym.KP_3: (1, 1), + # VI keys + KeySym.h: (-1, 0), + KeySym.l: (1, 0), + KeySym.k: (0, -1), + KeySym.j: (0, 1), + KeySym.y: (-1, -1), + KeySym.b: (-1, 1), + KeySym.u: (1, -1), + KeySym.n: (1, 1), + } + +Create a new module called ``game/states.py``. +``states`` is for derived classes, ``state`` is for the abstract class. +New states will be created in this module and this module will be allowed to import many first party modules without issues. + +Create a new :python:`class InGame:` decorated with :python:`@attrs.define()`. +States will always use ``g.world`` to access the ECS registry. + +.. code-block:: python + + @attrs.define() + class InGame: + """Primary in-game state.""" + ... + +Create an ``on_event`` and ``on_draw`` method matching the ``ExampleState`` class. +Copying ``ExampleState`` and modifying it should be enough since this wil replace ``ExampleState``. + +Now to do an tcod-ecs query to fetch the player entity. +In tcod-ecs queries most often start with :python:`g.world.Q.all_of(components=[], tags=[])`. +Which components and tags are asked for will narrow down the returned set of entities to only those matching the requirements. +The query to fetch player entities is :python:`g.world.Q.all_of(tags=[IsPlayer])`. +We expect only one player so the result will be unpacked into a single name: :python:`(player,) = g.world.Q.all_of(tags=[IsPlayer])`. + +Next is to handle the event. +Handling :python:`case tcod.event.Quit():` is the same as before: :python:`raise SystemExit`. + +The case for direction keys will now be done in a single case: :python:`case tcod.event.KeyDown(sym=sym) if sym in DIRECTION_KEYS:`. +``sym=sym`` assigns from the event attribute to a local name. +The left side is the ``event.sym`` attribute and right side is the local name ``sym`` being assigned to. +The case also has a condition which must pass for this branch to be taken and in this case we ensure that only keys from the ``DIRECTION_KEYS`` dictionary are valid ``sym``'s. + +Inside this branch moving the player is simple. +Access the ``(x, y)`` vector with :python:`DIRECTION_KEYS[sym]` and use ``+=`` to add it to the players current ``Position`` component. +This triggers the earlier written ``__add__`` dunder method and ``on_position_changed`` callback. + +Now that the player has moved it would be a good time to interact with the gold entities. +The query to see if the player has stepped on gold is to check for whichever entities have a ``Gold`` component, an ``IsItem`` tag, and the players current position as a tag. +The query for this is :python:`g.world.Q.all_of(components=[Gold], tags=[player.components[Position], IsItem]):`. + +We will iterate over whatever matches this query using a :python:`for gold in ...:` loop. +Add the entities ``Gold`` component to the players similar component. +Keep in mind that ``Gold`` is treated like an ``int`` so its usage is predictable. + +Format the added and total of gold using a Python f-string_: :python:`text = f"Picked up {gold.components[Gold]}g, total: {player.components[Gold]}g"`. +Store ``text`` globally in the ECS registry with :python:`g.world[None].components[("Text", str)] = text`. +This is done as two lines to avoid creating a line with an excessive length. + +Then use :python:`gold.clear()` at the end to remove all components and tags from the gold entity which will effectively delete it. + +.. code-block:: python + + ... + def on_event(self, event: tcod.event.Event) -> None: + """Handle events for the in-game state.""" + (player,) = g.world.Q.all_of(tags=[IsPlayer]) + match event: + case tcod.event.Quit(): + raise SystemExit + case tcod.event.KeyDown(sym=sym) if sym in DIRECTION_KEYS: + player.components[Position] += DIRECTION_KEYS[sym] + # Auto pickup gold + for gold in g.world.Q.all_of(components=[Gold], tags=[player.components[Position], IsItem]): + player.components[Gold] += gold.components[Gold] + text = f"Picked up {gold.components[Gold]}g, total: {player.components[Gold]}g" + g.world[None].components[str] = text + gold.clear() + ... + +Now start with the ``on_draw`` method. +Any entity with both a ``Position`` and a ``Graphic`` is drawable. +Iterate over these entities with :python:`for entity in g.world.Q.all_of(components=[Position, Graphic]):`. +Accessing components can be slow in a loop, so assign components to local names before using them (:python:`pos = entity.components[Position]` and :python:`graphic = entity.components[Graphic]`). + +Check if a components position is in the bounds of the console. +:python:`0 <= pos.x < console.width and 0 <= pos.y < console.height` tells if the position is in bounds. +Instead of nesting this method further, this check should be a guard using :python:`if not (...):` and :python:`continue`. + +Draw the graphic by assigning it to the consoles Numpy array directly with :python:`console.rgb[["ch", "fg"]][pos.y, pos.x] = graphic.ch, graphic.fg`. +``console.rgb`` is a ``ch,fg,bg`` array and :python:`[["ch", "fg"]]` narrows it down to only ``ch,fg``. +The array is in C row-major memory order so you access it with yx (or ij) ordering. + +That ends the entity rendering loop. +Next is to print the ``("Text", str)`` component if it exists. +A normal access will raise ``KeyError`` if the component is accessed before being assigned. +This case will be handled by the ``.get`` method of the ``Entity.components`` attribute. +:python:`g.world[None].components.get(("Text", str))` will return :python:`None` instead of raising ``KeyError``. +Assigning this result to ``text`` and then checking :python:`if text:` will ensure that ``text`` within the branch is not None and that the string is not empty. +We will not use ``text`` outside of the branch, so an assignment expression can be used here to check and assign the name at the same time with :python:`if text := g.world[None].components.get(("Text", str)):`. + +In this branch you will print ``text`` to the bottom of the console with a white foreground and black background. +The call to do this is :python:`console.print(x=0, y=console.height - 1, string=text, fg=(255, 255, 255), bg=(0, 0, 0))`. + +.. code-block:: python + + ... + def on_draw(self, console: tcod.console.Console) -> None: + """Draw the standard screen.""" + for entity in g.world.Q.all_of(components=[Position, Graphic]): + pos = entity.components[Position] + if not (0 <= pos.x < console.width and 0 <= pos.y < console.height): + continue + graphic = entity.components[Graphic] + console.rgb[["ch", "fg"]][pos.y, pos.x] = graphic.ch, graphic.fg + + if text := g.world[None].components.get(("Text", str)): + console.print(x=0, y=console.height - 1, string=text, fg=(255, 255, 255), bg=(0, 0, 0)) + +Verify the indentation of the ``if`` branch is correct. +It should be at the same level as the ``for`` loop and not inside of it. + +``game/states.py`` should now look like this: + +.. code-block:: python + + """A collection of game states.""" + + from __future__ import annotations + + import attrs + import tcod.console + import tcod.event + + import g + from game.components import Gold, Graphic, Position + from game.constants import DIRECTION_KEYS + from game.tags import IsItem, IsPlayer + + + @attrs.define() + class InGame: + """Primary in-game state.""" + + def on_event(self, event: tcod.event.Event) -> None: + """Handle events for the in-game state.""" + (player,) = g.world.Q.all_of(tags=[IsPlayer]) + match event: + case tcod.event.Quit(): + raise SystemExit + case tcod.event.KeyDown(sym=sym) if sym in DIRECTION_KEYS: + player.components[Position] += DIRECTION_KEYS[sym] + # Auto pickup gold + for gold in g.world.Q.all_of(components=[Gold], tags=[player.components[Position], IsItem]): + player.components[Gold] += gold.components[Gold] + text = f"Picked up {gold.components[Gold]}g, total: {player.components[Gold]}g" + g.world[None].components[("Text", str)] = text + gold.clear() + + def on_draw(self, console: tcod.console.Console) -> None: + """Draw the standard screen.""" + for entity in g.world.Q.all_of(components=[Position, Graphic]): + pos = entity.components[Position] + if not (0 <= pos.x < console.width and 0 <= pos.y < console.height): + continue + graphic = entity.components[Graphic] + console.rgb[["ch", "fg"]][pos.y, pos.x] = graphic.ch, graphic.fg + + if text := g.world[None].components.get(("Text", str)): + console.print(x=0, y=console.height - 1, string=text, fg=(255, 255, 255), bg=(0, 0, 0)) + +Main script update +============================================================================== + +Back to ``main.py``. +At this point you should know to import the modules needed. + +The ``ExampleState`` class is obsolete and will be removed. +``state`` will be created with :python:`game.states.InGame()` instead. + +If you have not replaced ``context`` with ``g.context`` yet then do it now. + +Add :python:`g.world = game.world_tools.new_world()` before the main loop. + +``main.py`` will look like this: + +.. code-block:: python + :emphasize-lines: 10-12,22-24,28 + + #!/usr/bin/env python3 + """Main entry-point module. This script is used to start the program.""" + + from __future__ import annotations + + import tcod.console + import tcod.context + import tcod.event + import tcod.tileset + + import g + import game.states + import game.world_tools + + + def main() -> None: + """Entry point function.""" + tileset = tcod.tileset.load_tilesheet( + "data/Alloy_curses_12x12.png", columns=16, rows=16, charmap=tcod.tileset.CHARMAP_CP437 + ) + tcod.tileset.procedural_block_elements(tileset=tileset) + console = tcod.console.Console(80, 50) + state = game.states.InGame() + g.world = game.world_tools.new_world() + with tcod.context.new(console=console, tileset=tileset) as g.context: + while True: # Main loop + console.clear() # Clear the console before any drawing + state.on_draw(console) # Draw the current state + g.context.present(console) # Render the console to the window and show it + for event in tcod.event.wait(): # Event loop, blocks until pending events exist + print(event) + state.on_event(event) # Dispatch events to the state + + + if __name__ == "__main__": + main() + +Now you can play a simple game where you wander around collecting gold. + +You can review the part-2 source code `here `_. + +.. rubric:: Footnotes + +.. [#g] ``global``, ``globals``, and ``glob`` were already taken by keywords, built-ins, and the standard library. + The alternatives are to either put this in the ``game`` namespace or to add an underscore such as ``globals_.py``. + +.. _f-string: https://docs.python.org/3/tutorial/inputoutput.html#formatted-string-literals diff --git a/docs/tutorial/part-03.rst b/docs/tutorial/part-03.rst new file mode 100644 index 00000000..528a39ae --- /dev/null +++ b/docs/tutorial/part-03.rst @@ -0,0 +1,459 @@ +.. _part-3: + +Part 3 - UI State +############################################################################## + +.. include:: notice.rst + +.. warning:: + + **This part is still a draft and is being worked on. + Sections here will be incorrect as these examples were hastily moved from an earlier part.** + + +State protocol +============================================================================== + +To have more states than ``ExampleState`` one must use an abstract type which can be used to refer to any state. +In this case a `Protocol`_ will be used, called ``State``. + +Create a new module: ``game/state.py``. +In this module add the class :python:`class State(Protocol):`. +``Protocol`` is from Python's ``typing`` module. +``State`` should have the ``on_event`` and ``on_draw`` methods from ``ExampleState`` but these methods will be empty other than the docstrings describing what they are for. +These methods refer to types from ``tcod`` and those types will need to be imported. +``State`` should also have :python:`__slots__ = ()` [#slots]_ in case the class is used for a subclass. + +Now add a few small classes using :python:`@attrs.define()`: +A ``Push`` class with a :python:`state: State` attribute. +A ``Pop`` class with no attributes. +A ``Reset`` class with a :python:`state: State` attribute. + +Then add a :python:`StateResult: TypeAlias = "Push | Pop | Reset | None"`. +This is a type which combines all of the previous classes. + +Edit ``State``'s ``on_event`` method to return ``StateResult``. + +``game/state.py`` should look like this: + +.. code-block:: python + + """Base classes for states.""" + + from __future__ import annotations + + from typing import Protocol, TypeAlias + + import attrs + import tcod.console + import tcod.event + + + class State(Protocol): + """An abstract game state.""" + + __slots__ = () + + def on_event(self, event: tcod.event.Event) -> StateResult: + """Called on events.""" + + def on_draw(self, console: tcod.console.Console) -> None: + """Called when the state is being drawn.""" + + + @attrs.define() + class Push: + """Push a new state on top of the stack.""" + + state: State + + + @attrs.define() + class Pop: + """Remove the current state from the stack.""" + + + @attrs.define() + class Reset: + """Replace the entire stack with a new state.""" + + state: State + + + StateResult: TypeAlias = "Push | Pop | Reset | None" + """Union of state results.""" + +The ``InGame`` class does not need to be updated since it is already a structural subtype of ``State``. +Note that subclasses of ``State`` will never be in same module as ``State``, this will be the same for all abstract classes. + +New globals +============================================================================== + +A new global will be added: :python:`states: list[game.state.State] = []`. +States are implemented as a list/stack to support `pushdown automata `_. +Representing states as a stack makes it easier to implement popup windows, sub-menus, and other prompts. + +The ``console`` variable from ``main.py`` will be moved to ``g.py``. +Add :python:`console: tcod.console.Console` and replace all references to ``console`` in ``main.py`` with ``g.console``. + +.. code-block:: python + :emphasize-lines: 9,17-21 + + """This module stores globally mutable variables used by this program.""" + + from __future__ import annotations + + import tcod.console + import tcod.context + import tcod.ecs + + import game.state + + context: tcod.context.Context + """The window managed by tcod.""" + + world: tcod.ecs.Registry + """The active ECS registry and current session.""" + + states: list[game.state.State] = [] + """A stack of states with the last item being the active state.""" + + console: tcod.console.Console + """The current main console.""" + + +State functions +============================================================================== + +Create a new module: ``game/state_tools.py``. +This module will handle events and rendering of the global state. + +In this module add the function :python:`def main_draw() -> None:`. +This will hold the "clear, draw, present" logic from the ``main`` function which will be moved to this function. +Render the active state with :python:`g.states[-1].on_draw(g.console)`. +If ``g.states`` is empty then this function should immediately :python:`return` instead of doing anything. +Empty containers in Python are :python:`False` when checked for truthiness. + +Next is to handle the ``StateResult`` type. +Start by adding the :python:`def apply_state_result(result: StateResult) -> None:` function. +This function will :python:`match result:` to decide on what to do. + +:python:`case Push(state=state):` should append ``state`` to ``g.states``. + +:python:`case Pop():` should simply call :python:`g.states.pop()`. + +:python:`case Reset(state=state):` should call :python:`apply_state_result(Pop())` until ``g.state`` is empty then call :python:`apply_state_result(Push(state))`. + +:python:`case None:` should be handled by explicitly ignoring it. + +:python:`case _:` handles anything else and should invoke :python:`raise TypeError(result)` since no other types are expected. + +Now the function :python:`def main_loop() -> None:` is created. +The :python:`while` loop from ``main`` will be moved to this function. +The while loop will be replaced by :python:`while g.states:` so that this function will exit if no state exists. +Drawing will be replaced by a call to ``main_draw``. +Events with mouse coordinates should be converted to tiles using :python:`tile_event = g.context.convert_event(event)` before being passed to a state. +:python:`apply_state_result(g.states[-1].on_event(tile_event))` will pass the event and handle the return result at the same time. +``g.states`` must be checked to be non-empty inside the event handing for-loop because ``apply_state_result`` could cause ``g.states`` to become empty. + +Next is the utility function :python:`def get_previous_state(state: State) -> State | None:`. +Get the index of ``state`` in ``g.states`` by identity [#identity]_ using :python:`current_index = next(index for index, value in enumerate(g.states) if value is state)`. +Return the previous state if :python:`current_index > 0` or else return None using :python:`return g.states[current_index - 1] if current_index > 0 else None`. + +Next is :python:`def draw_previous_state(state: State, console: tcod.console.Console, dim: bool = True) -> None:`. +Call ``get_previous_state`` to get the previous state and return early if the result is :python:`None`. +Then call the previous states :python:`State.on_draw` method as normal. +Afterwards test :python:`dim and state is g.states[-1]` to see if the console should be dimmed. +If it should be dimmed then reduce the color values of the console with :python:`console.rgb["fg"] //= 4` and :python:`console.rgb["bg"] //= 4`. +This is used to indicate that any graphics behind the active state are non-interactable. + + +.. code-block:: python + + """State handling functions.""" + + from __future__ import annotations + + import tcod.console + + import g + from game.state import Pop, Push, Reset, StateResult + + + def main_draw() -> None: + """Render and present the active state.""" + if not g.states: + return + g.console.clear() + g.states[-1].on_draw(g.console) + g.context.present(g.console) + + + def apply_state_result(result: StateResult) -> None: + """Apply a StateResult to `g.states`.""" + match result: + case Push(state=state): + g.states.append(state) + case Pop(): + g.states.pop() + case Reset(state=state): + while g.states: + apply_state_result(Pop()) + apply_state_result(Push(state)) + case None: + pass + case _: + raise TypeError(result) + + + def main_loop() -> None: + """Run the active state forever.""" + while g.states: + main_draw() + for event in tcod.event.wait(): + tile_event = g.context.convert_event(event) + if g.states: + apply_state_result(g.states[-1].on_event(tile_event)) + + + def get_previous_state(state: State) -> State | None: + """Return the state before `state` in the stack if it exists.""" + current_index = next(index for index, value in enumerate(g.states) if value is state) + return g.states[current_index - 1] if current_index > 0 else None + + + def draw_previous_state(state: State, console: tcod.console.Console, dim: bool = True) -> None: + """Draw previous states, optionally dimming all but the active state.""" + prev_state = get_previous_state(state) + if prev_state is None: + return + prev_state.on_draw(console) + if dim and state is g.states[-1]: + console.rgb["fg"] //= 4 + console.rgb["bg"] //= 4 + +Menus +============================================================================== + +.. code-block:: python + + """Menu UI classes.""" + + from __future__ import annotations + + from collections.abc import Callable + from typing import Protocol + + import attrs + import tcod.console + import tcod.event + from tcod.event import KeySym + + import game.state_tools + from game.constants import DIRECTION_KEYS + from game.state import Pop, State, StateResult + + + class MenuItem(Protocol): + """Menu item protocol.""" + + __slots__ = () + + def on_event(self, event: tcod.event.Event) -> StateResult: + """Handle events passed to the menu item.""" + + def on_draw(self, console: tcod.console.Console, x: int, y: int, highlight: bool) -> None: + """Draw is item at the given position.""" + + + @attrs.define() + class SelectItem(MenuItem): + """Clickable menu item.""" + + label: str + callback: Callable[[], StateResult] + + def on_event(self, event: tcod.event.Event) -> StateResult: + """Handle events selecting this item.""" + match event: + case tcod.event.KeyDown(sym=sym) if sym in {KeySym.RETURN, KeySym.RETURN2, KeySym.KP_ENTER}: + return self.callback() + case tcod.event.MouseButtonUp(button=tcod.event.MouseButton.LEFT): + return self.callback() + case _: + return None + + def on_draw(self, console: tcod.console.Console, x: int, y: int, highlight: bool) -> None: + """Render this items label.""" + console.print(x, y, self.label, fg=(255, 255, 255), bg=(64, 64, 64) if highlight else (0, 0, 0)) + + + @attrs.define() + class ListMenu(State): + """Simple list menu state.""" + + items: tuple[MenuItem, ...] + selected: int | None = 0 + x: int = 0 + y: int = 0 + + def on_event(self, event: tcod.event.Event) -> StateResult: + """Handle events for menus.""" + match event: + case tcod.event.Quit(): + raise SystemExit + case tcod.event.KeyDown(sym=sym) if sym in DIRECTION_KEYS: + dx, dy = DIRECTION_KEYS[sym] + if dx != 0 or dy == 0: + return self.activate_selected(event) + if self.selected is not None: + self.selected += dy + self.selected %= len(self.items) + else: + self.selected = 0 if dy == 1 else len(self.items) - 1 + return None + case tcod.event.MouseMotion(position=(_, y)): + y -= self.y + self.selected = y if 0 <= y < len(self.items) else None + return None + case tcod.event.KeyDown(sym=KeySym.ESCAPE): + return self.on_cancel() + case tcod.event.MouseButtonUp(button=tcod.event.MouseButton.RIGHT): + return self.on_cancel() + case _: + return self.activate_selected(event) + + def activate_selected(self, event: tcod.event.Event) -> StateResult: + """Call the selected menu items callback.""" + if self.selected is not None: + return self.items[self.selected].on_event(event) + return None + + def on_cancel(self) -> StateResult: + """Handle escape or right click being pressed on menus.""" + return Pop() + + def on_draw(self, console: tcod.console.Console) -> None: + """Render the menu.""" + game.state_tools.draw_previous_state(self, console) + for i, item in enumerate(self.items): + item.on_draw(console, x=self.x, y=self.y + i, highlight=i == self.selected) + +Update states +============================================================================== + +.. code-block:: python + + class MainMenu(game.menus.ListMenu): + """Main/escape menu.""" + + __slots__ = () + + def __init__(self) -> None: + """Initialize the main menu.""" + items = [ + game.menus.SelectItem("New game", self.new_game), + game.menus.SelectItem("Quit", self.quit), + ] + if hasattr(g, "world"): + items.insert(0, game.menus.SelectItem("Continue", self.continue_)) + + super().__init__( + items=tuple(items), + selected=0, + x=5, + y=5, + ) + + @staticmethod + def continue_() -> StateResult: + """Return to the game.""" + return Reset(InGame()) + + @staticmethod + def new_game() -> StateResult: + """Begin a new game.""" + g.world = game.world_tools.new_world() + return Reset(InGame()) + + @staticmethod + def quit() -> StateResult: + """Close the program.""" + raise SystemExit + +.. code-block:: python + :emphasize-lines: 2,5,19-23 + + @attrs.define() + class InGame(State): + """Primary in-game state.""" + + def on_event(self, event: tcod.event.Event) -> StateResult: + """Handle events for the in-game state.""" + (player,) = g.world.Q.all_of(tags=[IsPlayer]) + match event: + case tcod.event.Quit(): + raise SystemExit + case tcod.event.KeyDown(sym=sym) if sym in DIRECTION_KEYS: + player.components[Position] += DIRECTION_KEYS[sym] + # Auto pickup gold + for gold in g.world.Q.all_of(components=[Gold], tags=[player.components[Position], IsItem]): + player.components[Gold] += gold.components[Gold] + text = f"Picked up {gold.components[Gold]}g, total: {player.components[Gold]}g" + g.world[None].components[("Text", str)] = text + gold.clear() + return None + case tcod.event.KeyDown(sym=KeySym.ESCAPE): + return Push(MainMenu()) + case _: + return None + + ... + +Update main.py +============================================================================== + +Now ``main.py`` can be edited to use the global variables and the new game loop. + +Add :python:`import g` and :python:`import game.state_tools`. +Replace references to ``console`` with ``g.console``. + +States are initialed by assigning a list with the initial state to ``g.states``. +The previous game loop is replaced by a call to :python:`game.state_tools.main_loop()`. + +.. code-block:: python + :emphasize-lines: 3-4,13-16 + + ... + + import g + import game.state_tools + import game.states + + def main() -> None: + """Entry point function.""" + tileset = tcod.tileset.load_tilesheet( + "data/Alloy_curses_12x12.png", columns=16, rows=16, charmap=tcod.tileset.CHARMAP_CP437 + ) + tcod.tileset.procedural_block_elements(tileset=tileset) + g.console = tcod.console.Console(80, 50) + g.states = [game.states.MainMenu()] + with tcod.context.new(console=g.console, tileset=tileset) as g.context: + game.state_tools.main_loop() + ... + +After this you can test the game. +There should be no visible differences from before. + +You can review the part-3 source code `here `_. + +.. rubric:: Footnotes + +.. [#slots] This is done to prevent subclasses from requiring a ``__dict__`` attribute. + See :any:`slots` for a detailed explanation of what they are. + +.. [#identity] See :any:`is`. + Since ``State`` classes use ``attrs`` they might compare equal when they're not the same object. + This means :python:`list.index` won't work for this case. + +.. _Protocol: https://mypy.readthedocs.io/en/stable/protocols.html diff --git a/examples/README.md b/examples/README.md index 0594efe1..8f13fa79 100644 --- a/examples/README.md +++ b/examples/README.md @@ -1,6 +1,6 @@ This directory contains a few example scripts for using python-tcod. -`samples_tcod.py` is the mail example which uses most of the newer API. This +`samples_tcod.py` is the mail example which uses most of the newer API. This can be compared to `samples_libtcodpy.py` which mostly uses deprecated functions from the old API. @@ -8,5 +8,5 @@ Examples in the `distribution/` folder show how to distribute projects made using python-tcod. Examples in the `experimental/` folder show off features that might later be -added the python-tcod API. You can use those features by copying those modules +added the python-tcod API. You can use those features by copying those modules into your own project. diff --git a/examples/audio_tone.py b/examples/audio_tone.py new file mode 100755 index 00000000..b65bb732 --- /dev/null +++ b/examples/audio_tone.py @@ -0,0 +1,75 @@ +#!/usr/bin/env python +"""Shows how to use tcod.sdl.audio to play audio. + +Opens an audio device using SDL then plays tones using various methods. +""" + +import math +import time + +import attrs +import numpy as np +from scipy import signal # type: ignore[import-untyped] + +import tcod.sdl.audio + +VOLUME = 10 ** (-12 / 10) # -12dB, square waves can be loud + + +@attrs.define +class PullWave: + """Square wave stream generator for an SDL audio device in pull mode.""" + + frequency: float + time: float = 0.0 + + def __call__(self, stream: tcod.sdl.audio.AudioStream, request: tcod.sdl.audio.AudioStreamCallbackData) -> None: + """Stream a square wave to SDL on demand. + + This function must run faster than the stream duration. + Numpy is used to keep performance within these limits. + """ + duration = request.additional_samples / self.frequency + + t = np.linspace(self.time, self.time + duration, request.additional_samples, endpoint=False) + self.time += duration + wave = signal.square(t * (math.tau * 440)).astype(np.float32) + stream.queue_audio(wave) + + +if __name__ == "__main__": + device = tcod.sdl.audio.get_default_playback().open(channels=1, frequency=44100) + print(f"{device.name=}") + device.gain = VOLUME + print(device) + + print("Sawtooth wave queued with AudioStream.queue_audio") + stream = device.new_stream(format=np.float32, channels=1, frequency=44100) + t = np.linspace(0, 1.0, 44100, endpoint=False) + wave = signal.sawtooth(t * (math.tau * 440)).astype(np.float32) + stream.queue_audio(wave) + stream.flush() + while stream.queued_samples: + time.sleep(0.01) + + print("---") + time.sleep(0.5) + + print("Square wave attached to AudioStream.getter_callback") + stream = device.new_stream(format=np.float32, channels=1, frequency=44100) + stream.getter_callback = PullWave(device.frequency) + + time.sleep(1) + stream.getter_callback = None + + print("---") + time.sleep(0.5) + + print("Sawtooth wave played with BasicMixer.play") + mixer = tcod.sdl.audio.BasicMixer(device, frequency=44100, channels=2) + channel = mixer.play(wave) + while channel.busy: + time.sleep(0.01) + + print("---") + device.close() diff --git a/examples/cavegen.py b/examples/cavegen.py index eedf0575..ace6bb98 100755 --- a/examples/cavegen.py +++ b/examples/cavegen.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python3 +#!/usr/bin/env python """A basic cellular automata cave generation example using SciPy. http://www.roguebasin.com/index.php?title=Cellular_Automata_Method_for_Generating_Random_Cave-Like_Levels @@ -6,10 +6,11 @@ This will print the result to the console, so be sure to run this from the command line. """ + from typing import Any import numpy as np -import scipy.signal # type: ignore +import scipy.signal # type: ignore[import-untyped] from numpy.typing import NDArray diff --git a/examples/distribution/PyInstaller/main.py b/examples/distribution/PyInstaller/main.py index e1c6090d..ce500635 100755 --- a/examples/distribution/PyInstaller/main.py +++ b/examples/distribution/PyInstaller/main.py @@ -1,10 +1,10 @@ -#!/usr/bin/env python3 +#!/usr/bin/env python # To the extent possible under law, the libtcod maintainers have waived all # copyright and related or neighboring rights for the "hello world" PyInstaller # example script. This work is published from: United States. # https://creativecommons.org/publicdomain/zero/1.0/ """PyInstaller main script example.""" -import sys + from pathlib import Path import tcod.console @@ -14,8 +14,8 @@ WIDTH, HEIGHT = 80, 60 -# The base directory, this is sys._MEIPASS when in one-file mode. -BASE_DIR = Path(getattr(sys, "_MEIPASS", ".")) +BASE_DIR = Path(__file__).parent +"""The directory of this script.""" FONT_PATH = BASE_DIR / "data/terminal8x8_gs_ro.png" @@ -30,7 +30,7 @@ def main() -> None: context.present(console) for event in tcod.event.wait(): if isinstance(event, tcod.event.Quit): - raise SystemExit() + raise SystemExit if __name__ == "__main__": diff --git a/examples/distribution/PyInstaller/requirements.txt b/examples/distribution/PyInstaller/requirements.txt index f28443fd..24379896 100644 --- a/examples/distribution/PyInstaller/requirements.txt +++ b/examples/distribution/PyInstaller/requirements.txt @@ -1,3 +1,3 @@ -tcod==12.2.0 -pyinstaller==4.3 +tcod==16.2.3 +pyinstaller==6.14.2 pypiwin32; sys_platform=="win32" diff --git a/examples/distribution/cx_Freeze/main.py b/examples/distribution/cx_Freeze/main.py index 8f608af7..569dddad 100755 --- a/examples/distribution/cx_Freeze/main.py +++ b/examples/distribution/cx_Freeze/main.py @@ -1,5 +1,6 @@ -#!/usr/bin/env python3 +#!/usr/bin/env python """cx_Freeze main script example.""" + import tcod.console import tcod.context import tcod.event @@ -19,7 +20,7 @@ def main() -> None: context.present(console) for event in tcod.event.wait(): if isinstance(event, tcod.event.Quit): - raise SystemExit() + raise SystemExit if __name__ == "__main__": diff --git a/examples/distribution/cx_Freeze/setup.py b/examples/distribution/cx_Freeze/setup.py index 446a0f42..50a588db 100755 --- a/examples/distribution/cx_Freeze/setup.py +++ b/examples/distribution/cx_Freeze/setup.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python3 +#!/usr/bin/env python import sys from cx_Freeze import Executable, setup # type: ignore diff --git a/examples/eventget.py b/examples/eventget.py index 0895e90f..9dc82ca3 100755 --- a/examples/eventget.py +++ b/examples/eventget.py @@ -1,28 +1,28 @@ -#!/usr/bin/env python3 +#!/usr/bin/env python # To the extent possible under law, the libtcod maintainers have waived all # copyright and related or neighboring rights for this example. This work is # published from: United States. # https://creativecommons.org/publicdomain/zero/1.0/ """An demonstration of event handling using the tcod.event module.""" -from typing import List, Set import tcod.context import tcod.event import tcod.sdl.joystick -import tcod.sdl.sys -WIDTH, HEIGHT = 720, 480 +WIDTH, HEIGHT = 1280, 720 -def main() -> None: +def main() -> None: # noqa: C901, PLR0912 """Example program for tcod.event.""" - event_log: List[str] = [] + event_log: list[str] = [] motion_desc = "" tcod.sdl.joystick.init() - controllers: Set[tcod.sdl.joystick.GameController] = set() - joysticks: Set[tcod.sdl.joystick.Joystick] = set() + controllers: set[tcod.sdl.joystick.GameController] = set() + joysticks: set[tcod.sdl.joystick.Joystick] = set() with tcod.context.new(width=WIDTH, height=HEIGHT) as context: + if context.sdl_window: + context.sdl_window.start_text_input() console = context.new_console() while True: # Display all event items. @@ -39,24 +39,23 @@ def main() -> None: for event in tcod.event.wait(): context.convert_event(event) # Set tile coordinates for event. print(repr(event)) - if isinstance(event, tcod.event.Quit): - raise SystemExit() - if isinstance(event, tcod.event.WindowResized) and event.type == "WindowSizeChanged": - console = context.new_console() - if isinstance(event, tcod.event.ControllerDevice): - if event.type == "CONTROLLERDEVICEADDED": - controllers.add(event.controller) - elif event.type == "CONTROLLERDEVICEREMOVED": - controllers.remove(event.controller) - if isinstance(event, tcod.event.JoystickDevice): - if event.type == "JOYDEVICEADDED": - joysticks.add(event.joystick) - elif event.type == "JOYDEVICEREMOVED": - joysticks.remove(event.joystick) - if isinstance(event, tcod.event.MouseMotion): - motion_desc = str(event) - else: # Log all events other than MouseMotion. - event_log.append(str(event)) + match event: + case tcod.event.Quit(): + raise SystemExit + case tcod.event.WindowResized(type="WindowResized"): + console = context.new_console() + case tcod.event.ControllerDevice(type="CONTROLLERDEVICEADDED", controller=controller): + controllers.add(controller) + case tcod.event.ControllerDevice(type="CONTROLLERDEVICEREMOVED", controller=controller): + controllers.remove(controller) + case tcod.event.JoystickDevice(type="JOYDEVICEADDED", joystick=joystick): + joysticks.add(joystick) + case tcod.event.JoystickDevice(type="JOYDEVICEREMOVED", joystick=joystick): + joysticks.remove(joystick) + case tcod.event.MouseMotion(): + motion_desc = str(event) + if not isinstance(event, tcod.event.MouseMotion): # Log all events other than MouseMotion + event_log.append(repr(event)) if __name__ == "__main__": diff --git a/examples/framerate.py b/examples/framerate.py index 3a9e9f81..cc1195de 100755 --- a/examples/framerate.py +++ b/examples/framerate.py @@ -1,13 +1,13 @@ -#!/usr/bin/env python3 +#!/usr/bin/env python # To the extent possible under law, the libtcod maintainers have waived all # copyright and related or neighboring rights for this example. This work is # published from: United States. # https://creativecommons.org/publicdomain/zero/1.0/ """A system to control time since the original libtcod tools are deprecated.""" + import statistics import time from collections import deque -from typing import Deque, Optional import tcod @@ -24,11 +24,11 @@ class Clock: def __init__(self) -> None: """Initialize this object with empty data.""" self.last_time = time.perf_counter() # Last time this was synced. - self.time_samples: Deque[float] = deque() # Delta time samples. + self.time_samples: deque[float] = deque() # Delta time samples. self.max_samples = 64 # Number of fps samples to log. Can be changed. self.drift_time = 0.0 # Tracks how much the last frame was overshot. - def sync(self, fps: Optional[float] = None) -> float: + def sync(self, fps: float | None = None) -> float: """Sync to a given framerate and return the delta time. `fps` is the desired framerate in frames-per-second. If None is given @@ -137,7 +137,7 @@ def main() -> None: for event in tcod.event.get(): context.convert_event(event) # Set tile coordinates for event. if isinstance(event, tcod.event.Quit): - raise SystemExit() + raise SystemExit if isinstance(event, tcod.event.MouseWheel): desired_fps = max(1, desired_fps + event.y) diff --git a/examples/samples_libtcodpy.py b/examples/samples_libtcodpy.py index ab68eb91..71780d97 100755 --- a/examples/samples_libtcodpy.py +++ b/examples/samples_libtcodpy.py @@ -12,14 +12,7 @@ import sys import warnings -import tcod as libtcod - -try: # Import Psyco if available - import psyco - - psyco.full() -except ImportError: - pass +from tcod import tcod as libtcod if not sys.warnoptions: warnings.simplefilter("ignore") # Prevent flood of deprecation warnings. @@ -220,7 +213,7 @@ def render_colors(first, key, mouse): SAMPLE_SCREEN_HEIGHT - 1, libtcod.BKGND_MULTIPLY, libtcod.CENTER, - "The Doryen library uses 24 bits " "colors, for both background and " "foreground.", + "The Doryen library uses 24 bits colors, for both background and foreground.", ) if key.c == ord("f"): @@ -270,10 +263,7 @@ def render_offscreen(first, key, mouse): SAMPLE_SCREEN_HEIGHT // 2, libtcod.BKGND_NONE, libtcod.CENTER, - b"You can render to an offscreen " - b"console and blit in on another " - b"one, simulating alpha " - b"transparency.", + b"You can render to an offscreen console and blit in on another one, simulating alpha transparency.", ) if first: libtcod.sys_set_fps(30) diff --git a/examples/samples_tcod.py b/examples/samples_tcod.py index 58d35a15..bae9acd6 100755 --- a/examples/samples_tcod.py +++ b/examples/samples_tcod.py @@ -1,5 +1,6 @@ -#!/usr/bin/env python3 +#!/usr/bin/env python """This code demonstrates various usages of python-tcod.""" + # To the extent possible under law, the libtcod maintainers have waived all # copyright and related or neighboring rights to these samples. # https://creativecommons.org/publicdomain/zero/1.0/ @@ -12,25 +13,33 @@ import sys import time import warnings +from dataclasses import dataclass from pathlib import Path -from typing import Any +from typing import TYPE_CHECKING, Any import numpy as np -from numpy.typing import NDArray +import tcod.bsp import tcod.cffi +import tcod.console +import tcod.constants import tcod.context import tcod.event +import tcod.image +import tcod.los +import tcod.map import tcod.noise +import tcod.path import tcod.render import tcod.sdl.mouse import tcod.sdl.render +import tcod.tileset from tcod import libtcodpy +from tcod.event import KeySym -# ruff: noqa: S311 +if TYPE_CHECKING: + from numpy.typing import NDArray -if not sys.warnoptions: - warnings.simplefilter("default") # Show all warnings. DATA_DIR = Path(__file__).parent / "../libtcod/data" """Path of the samples data directory.""" @@ -54,122 +63,138 @@ tileset: tcod.tileset.Tileset console_render: tcod.render.SDLConsoleRender # Optional SDL renderer. sample_minimap: tcod.sdl.render.Texture # Optional minimap texture. -root_console = tcod.console.Console(80, 50, order="F") -sample_console = tcod.console.Console(SAMPLE_SCREEN_WIDTH, SAMPLE_SCREEN_HEIGHT, order="F") +root_console = tcod.console.Console(80, 50) +sample_console = tcod.console.Console(SAMPLE_SCREEN_WIDTH, SAMPLE_SCREEN_HEIGHT) cur_sample = 0 # Current selected sample. frame_times = [time.perf_counter()] frame_length = [0.0] +START_TIME = time.perf_counter() + + +def _get_elapsed_time() -> float: + """Return time passed since the start of the program.""" + return time.perf_counter() - START_TIME -class Sample(tcod.event.EventDispatch[None]): - def __init__(self, name: str = "") -> None: - self.name = name +class Sample: + """Samples base class.""" + + name: str = "???" def on_enter(self) -> None: - pass + """Called when entering a sample.""" def on_draw(self) -> None: - pass + """Called every frame.""" - def ev_keydown(self, event: tcod.event.KeyDown) -> None: + def on_event(self, event: tcod.event.Event) -> None: + """Called for each event.""" global cur_sample - if event.sym == tcod.event.KeySym.DOWN: - cur_sample = (cur_sample + 1) % len(SAMPLES) - SAMPLES[cur_sample].on_enter() - draw_samples_menu() - elif event.sym == tcod.event.KeySym.UP: - cur_sample = (cur_sample - 1) % len(SAMPLES) - SAMPLES[cur_sample].on_enter() - draw_samples_menu() - elif event.sym == tcod.event.KeySym.RETURN and event.mod & tcod.event.KMOD_LALT: - libtcodpy.console_set_fullscreen(not libtcodpy.console_is_fullscreen()) - elif event.sym == tcod.event.KeySym.PRINTSCREEN or event.sym == tcod.event.KeySym.p: - print("screenshot") - if event.mod & tcod.event.KMOD_LALT: - libtcodpy.console_save_apf(root_console, "samples.apf") - print("apf") - else: - libtcodpy.sys_save_screenshot() - print("png") - elif event.sym == tcod.event.KeySym.ESCAPE: - raise SystemExit() - elif event.sym in RENDERER_KEYS: - # Swap the active context for one with a different renderer. - init_context(RENDERER_KEYS[event.sym]) - - def ev_quit(self, event: tcod.event.Quit) -> None: - raise SystemExit() + match event: + case tcod.event.Quit() | tcod.event.KeyDown(sym=KeySym.ESCAPE): + raise SystemExit + case tcod.event.KeyDown(sym=KeySym.DOWN): + cur_sample = (cur_sample + 1) % len(SAMPLES) + SAMPLES[cur_sample].on_enter() + draw_samples_menu() + case tcod.event.KeyDown(sym=KeySym.UP): + cur_sample = (cur_sample - 1) % len(SAMPLES) + SAMPLES[cur_sample].on_enter() + draw_samples_menu() + case tcod.event.KeyDown(sym=KeySym.RETURN, mod=mod) if mod & tcod.event.Modifier.ALT: + sdl_window = context.sdl_window + if sdl_window: + sdl_window.fullscreen = not sdl_window.fullscreen + case tcod.event.KeyDown(sym=tcod.event.KeySym.PRINTSCREEN | tcod.event.KeySym.P): + print("screenshot") + if event.mod & tcod.event.Modifier.ALT: + libtcodpy.console_save_apf(root_console, "samples.apf") + print("apf") + else: + libtcodpy.sys_save_screenshot() + print("png") + case tcod.event.KeyDown(sym=sym) if sym in RENDERER_KEYS: + init_context(RENDERER_KEYS[sym]) # Swap the active context for one with a different renderer class TrueColorSample(Sample): + """Simple performance benchmark.""" + + name = "True colors" + def __init__(self) -> None: - self.name = "True colors" - # corner colors - self.colors: NDArray[np.int16] = np.array( - [(50, 40, 150), (240, 85, 5), (50, 35, 240), (10, 200, 130)], - dtype=np.int16, - ) - # color shift direction - self.slide_dir: NDArray[np.int16] = np.array([[1, 1, 1], [-1, -1, 1], [1, -1, 1], [1, 1, -1]], dtype=np.int16) - # corner indexes - self.corners: NDArray[np.int16] = np.array([0, 1, 2, 3], dtype=np.int16) + """Initialize random generators.""" + self.noise = tcod.noise.Noise(2, tcod.noise.Algorithm.SIMPLEX) + """Noise for generating color.""" + + self.generator = np.random.default_rng() + """Numpy generator for random text.""" def on_draw(self) -> None: - self.slide_corner_colors() + """Draw this sample.""" self.interpolate_corner_colors() self.darken_background_characters() - self.randomize_sample_conole() - self.print_banner() - - def slide_corner_colors(self) -> None: - # pick random RGB channels for each corner - rand_channels = np.random.randint(low=0, high=3, size=4) - - # shift picked color channels in the direction of slide_dir - self.colors[self.corners, rand_channels] += self.slide_dir[self.corners, rand_channels] * 5 + self.randomize_sample_console() + sample_console.print( + x=1, + y=5, + width=sample_console.width - 2, + height=sample_console.height - 1, + text="The Doryen library uses 24 bits colors, for both background and foreground.", + fg=WHITE, + bg=GREY, + bg_blend=libtcodpy.BKGND_MULTIPLY, + alignment=libtcodpy.CENTER, + ) - # reverse slide_dir values when limits are reached - self.slide_dir[self.colors[:] == 255] = -1 - self.slide_dir[self.colors[:] == 0] = 1 + def get_corner_colors(self) -> NDArray[np.uint8]: + """Return 4 random 8-bit colors, smoothed over time.""" + noise_samples_ij = ( + [ # i coordinates are per color channel per color + [0, 1, 2], + [3, 4, 5], + [6, 7, 8], + [9, 10, 11], + ], + time.perf_counter(), # j coordinate is time broadcast to all samples + ) + colors = self.noise[noise_samples_ij] + colors = ((colors + 1.0) * (0.5 * 255.0)).clip(min=0, max=255) # Convert -1..1 to 0..255 + return colors.astype(np.uint8) def interpolate_corner_colors(self) -> None: - # interpolate corner colors across the sample console - left = np.linspace(self.colors[0], self.colors[2], SAMPLE_SCREEN_HEIGHT) - right = np.linspace(self.colors[1], self.colors[3], SAMPLE_SCREEN_HEIGHT) - sample_console.bg[:] = np.linspace(left, right, SAMPLE_SCREEN_WIDTH) + """Interpolate corner colors across the sample console.""" + colors = self.get_corner_colors() + top = np.linspace(colors[0], colors[1], SAMPLE_SCREEN_WIDTH) + bottom = np.linspace(colors[2], colors[3], SAMPLE_SCREEN_WIDTH) + sample_console.bg[:] = np.linspace(top, bottom, SAMPLE_SCREEN_HEIGHT) def darken_background_characters(self) -> None: - # darken background characters + """Darken background characters.""" sample_console.fg[:] = sample_console.bg[:] sample_console.fg[:] //= 2 - def randomize_sample_conole(self) -> None: - # randomize sample console characters - sample_console.ch[:] = np.random.randint( + def randomize_sample_console(self) -> None: + """Randomize sample console characters.""" + sample_console.ch[:] = self.generator.integers( low=ord("a"), - high=ord("z") + 1, + high=ord("z"), + endpoint=True, size=sample_console.ch.size, dtype=np.intc, ).reshape(sample_console.ch.shape) - def print_banner(self) -> None: - # print text on top of samples - sample_console.print_box( - x=1, - y=5, - width=sample_console.width - 2, - height=sample_console.height - 1, - string="The Doryen library uses 24 bits colors, for both " "background and foreground.", - fg=WHITE, - bg=GREY, - bg_blend=libtcodpy.BKGND_MULTIPLY, - alignment=libtcodpy.CENTER, - ) - class OffscreenConsoleSample(Sample): + """Console blit example.""" + + name = "Offscreen console" + + CONSOLE_MOVE_RATE = 1 / 2 + CONSOLE_MOVE_MARGIN = 5 + def __init__(self) -> None: - self.name = "Offscreen console" + """Initialize the offscreen console.""" self.secondary = tcod.console.Console(sample_console.width // 2, sample_console.height // 2) self.screenshot = tcod.console.Console(sample_console.width, sample_console.height) self.counter = 0.0 @@ -178,62 +203,58 @@ def __init__(self) -> None: self.x_dir = 1 self.y_dir = 1 - self.secondary.draw_frame( + self.secondary.draw_frame(0, 0, self.secondary.width, self.secondary.height, clear=False, fg=WHITE, bg=BLACK) + self.secondary.print( 0, 0, - sample_console.width // 2, - sample_console.height // 2, - "Offscreen console", - False, - fg=WHITE, - bg=BLACK, + width=self.secondary.width, + height=self.secondary.height, + text=" Offscreen console ", + fg=BLACK, + bg=WHITE, + alignment=tcod.constants.CENTER, ) - self.secondary.print_box( - 1, - 2, - sample_console.width // 2 - 2, - sample_console.height // 2, - "You can render to an offscreen console and blit in on another " "one, simulating alpha transparency.", + self.secondary.print( + x=1, + y=2, + width=sample_console.width // 2 - 2, + height=sample_console.height // 2, + text="You can render to an offscreen console and blit in on another one, simulating alpha transparency.", fg=WHITE, bg=None, alignment=libtcodpy.CENTER, ) def on_enter(self) -> None: - self.counter = time.perf_counter() - # get a "screenshot" of the current sample screen - sample_console.blit(dest=self.screenshot) + """Capture the previous sample screen as this samples background.""" + self.counter = _get_elapsed_time() + sample_console.blit(dest=self.screenshot) # get a "screenshot" of the current sample screen def on_draw(self) -> None: - if time.perf_counter() - self.counter >= 1: - self.counter = time.perf_counter() + """Draw and animate the offscreen console.""" + if _get_elapsed_time() - self.counter >= self.CONSOLE_MOVE_RATE: + self.counter = _get_elapsed_time() self.x += self.x_dir self.y += self.y_dir - if self.x == sample_console.width / 2 + 5: + if self.x == sample_console.width / 2 + self.CONSOLE_MOVE_MARGIN: self.x_dir = -1 - elif self.x == -5: + elif self.x == -self.CONSOLE_MOVE_MARGIN: self.x_dir = 1 - if self.y == sample_console.height / 2 + 5: + if self.y == sample_console.height / 2 + self.CONSOLE_MOVE_MARGIN: self.y_dir = -1 - elif self.y == -5: + elif self.y == -self.CONSOLE_MOVE_MARGIN: self.y_dir = 1 self.screenshot.blit(sample_console) self.secondary.blit( - sample_console, - self.x, - self.y, - 0, - 0, - sample_console.width // 2, - sample_console.height // 2, - 1.0, - 0.75, + sample_console, self.x, self.y, 0, 0, sample_console.width // 2, sample_console.height // 2, 1.0, 0.75 ) class LineDrawingSample(Sample): - FLAG_NAMES = [ + name = "Line drawing" + + FLAG_NAMES = ( "BKGND_NONE", "BKGND_SET", "BKGND_MULTIPLY", @@ -247,27 +268,27 @@ class LineDrawingSample(Sample): "BKGND_BURN", "BKGND_OVERLAY", "BKGND_ALPHA", - ] + ) def __init__(self) -> None: - self.name = "Line drawing" self.mk_flag = libtcodpy.BKGND_SET self.bk_flag = libtcodpy.BKGND_SET - self.bk = tcod.console.Console(sample_console.width, sample_console.height, order="F") + self.background = tcod.console.Console(sample_console.width, sample_console.height) # initialize the colored background - self.bk.bg[:, :, 0] = np.linspace(0, 255, self.bk.width)[:, np.newaxis] - self.bk.bg[:, :, 2] = np.linspace(0, 255, self.bk.height) - self.bk.bg[:, :, 1] = (self.bk.bg[:, :, 0].astype(int) + self.bk.bg[:, :, 2]) / 2 - self.bk.ch[:] = ord(" ") - - def ev_keydown(self, event: tcod.event.KeyDown) -> None: - if event.sym in (tcod.event.KeySym.RETURN, tcod.event.KeySym.KP_ENTER): - self.bk_flag += 1 - if (self.bk_flag & 0xFF) > libtcodpy.BKGND_ALPH: - self.bk_flag = libtcodpy.BKGND_NONE - else: - super().ev_keydown(event) + self.background.bg[:, :, 0] = np.linspace(0, 255, self.background.width) + self.background.bg[:, :, 2] = np.linspace(0, 255, self.background.height)[:, np.newaxis] + self.background.bg[:, :, 1] = (self.background.bg[:, :, 0].astype(int) + self.background.bg[:, :, 2]) / 2 + self.background.ch[:] = ord(" ") + + def on_event(self, event: tcod.event.Event) -> None: + match event: + case tcod.event.KeyDown(sym=KeySym.RETURN | KeySym.KP_ENTER): + self.bk_flag += 1 + if (self.bk_flag & 0xFF) > libtcodpy.BKGND_ALPH: + self.bk_flag = libtcodpy.BKGND_NONE + case _: + super().on_event(event) def on_draw(self) -> None: alpha = 0.0 @@ -280,14 +301,12 @@ def on_draw(self) -> None: alpha = (1.0 + math.cos(time.time() * 2)) / 2.0 self.bk_flag = libtcodpy.BKGND_ADDALPHA(int(alpha)) - self.bk.blit(sample_console) + self.background.blit(sample_console) rect_y = int((sample_console.height - 2) * ((1.0 + math.cos(time.time())) / 2.0)) for x in range(sample_console.width): value = x * 255 // sample_console.width col = (value, value, value) - libtcodpy.console_set_char_background(sample_console, x, rect_y, col, self.bk_flag) - libtcodpy.console_set_char_background(sample_console, x, rect_y + 1, col, self.bk_flag) - libtcodpy.console_set_char_background(sample_console, x, rect_y + 2, col, self.bk_flag) + sample_console.draw_rect(x=x, y=rect_y, width=1, height=3, ch=0, fg=None, bg=col, bg_blend=self.bk_flag) angle = time.time() * 2.0 cos_angle = math.cos(angle) sin_angle = math.sin(angle) @@ -299,18 +318,14 @@ def on_draw(self) -> None: # in python the easiest way is to use the line iterator for x, y in tcod.los.bresenham((xo, yo), (xd, yd)).tolist(): if 0 <= x < sample_console.width and 0 <= y < sample_console.height: - libtcodpy.console_set_char_background(sample_console, x, y, LIGHT_BLUE, self.bk_flag) - sample_console.print( - 2, - 2, - "%s (ENTER to change)" % self.FLAG_NAMES[self.bk_flag & 0xFF], - fg=WHITE, - bg=None, - ) + sample_console.draw_rect(x, y, width=1, height=1, ch=0, fg=None, bg=LIGHT_BLUE, bg_blend=self.bk_flag) + sample_console.print(2, 2, f"{self.FLAG_NAMES[self.bk_flag & 0xFF]} (ENTER to change)", fg=WHITE, bg=None) class NoiseSample(Sample): - NOISE_OPTIONS = [ # (name, algorithm, implementation) + name = "Noise" + + NOISE_OPTIONS = ( # (name, algorithm, implementation) ( "perlin noise", tcod.noise.Algorithm.PERLIN, @@ -356,10 +371,9 @@ class NoiseSample(Sample): tcod.noise.Algorithm.WAVELET, tcod.noise.Implementation.TURBULENCE, ), - ] + ) def __init__(self) -> None: - self.name = "Noise" self.func = 0 self.dx = 0.0 self.dy = 0.0 @@ -390,8 +404,8 @@ def get_noise(self) -> tcod.noise.Noise: ) def on_draw(self) -> None: - self.dx = time.perf_counter() * 0.25 - self.dy = time.perf_counter() * 0.25 + self.dx = _get_elapsed_time() * 0.25 + self.dy = _get_elapsed_time() * 0.25 for y in range(2 * sample_console.height): for x in range(2 * sample_console.width): f = [ @@ -406,7 +420,7 @@ def on_draw(self) -> None: rect_h = 13 if self.implementation == tcod.noise.Implementation.SIMPLE: rect_h = 10 - sample_console.draw_semigraphics(self.img) + sample_console.draw_semigraphics(np.asarray(self.img)) sample_console.draw_rect( 2, 2, @@ -417,68 +431,69 @@ def on_draw(self) -> None: bg=GREY, bg_blend=libtcodpy.BKGND_MULTIPLY, ) - sample_console.fg[2 : 2 + rect_w, 2 : 2 + rect_h] = ( - sample_console.fg[2 : 2 + rect_w, 2 : 2 + rect_h] * GREY / 255 + sample_console.fg[2 : 2 + rect_h, 2 : 2 + rect_w] = ( + sample_console.fg[2 : 2 + rect_h, 2 : 2 + rect_w] * GREY / 255 ) for cur_func in range(len(self.NOISE_OPTIONS)): - text = "%i : %s" % (cur_func + 1, self.NOISE_OPTIONS[cur_func][0]) + text = f"{cur_func + 1} : {self.NOISE_OPTIONS[cur_func][0]}" if cur_func == self.func: sample_console.print(2, 2 + cur_func, text, fg=WHITE, bg=LIGHT_BLUE) else: sample_console.print(2, 2 + cur_func, text, fg=GREY, bg=None) - sample_console.print(2, 11, "Y/H : zoom (%2.1f)" % self.zoom, fg=WHITE, bg=None) + sample_console.print(2, 11, f"Y/H : zoom ({self.zoom:2.1f})", fg=WHITE, bg=None) if self.implementation != tcod.noise.Implementation.SIMPLE: sample_console.print( 2, 12, - "E/D : hurst (%2.1f)" % self.hurst, + f"E/D : hurst ({self.hurst:2.1f})", fg=WHITE, bg=None, ) sample_console.print( 2, 13, - "R/F : lacunarity (%2.1f)" % self.lacunarity, + f"R/F : lacunarity ({self.lacunarity:2.1f})", fg=WHITE, bg=None, ) sample_console.print( 2, 14, - "T/G : octaves (%2.1f)" % self.octaves, + f"T/G : octaves ({self.octaves:2.1f})", fg=WHITE, bg=None, ) - def ev_keydown(self, event: tcod.event.KeyDown) -> None: - if tcod.event.KeySym.N9 >= event.sym >= tcod.event.KeySym.N1: - self.func = event.sym - tcod.event.KeySym.N1 - self.noise = self.get_noise() - elif event.sym == tcod.event.KeySym.e: - self.hurst += 0.1 - self.noise = self.get_noise() - elif event.sym == tcod.event.KeySym.d: - self.hurst -= 0.1 - self.noise = self.get_noise() - elif event.sym == tcod.event.KeySym.r: - self.lacunarity += 0.5 - self.noise = self.get_noise() - elif event.sym == tcod.event.KeySym.f: - self.lacunarity -= 0.5 - self.noise = self.get_noise() - elif event.sym == tcod.event.KeySym.t: - self.octaves += 0.5 - self.noise.octaves = self.octaves - elif event.sym == tcod.event.KeySym.g: - self.octaves -= 0.5 - self.noise.octaves = self.octaves - elif event.sym == tcod.event.KeySym.y: - self.zoom += 0.2 - elif event.sym == tcod.event.KeySym.h: - self.zoom -= 0.2 - else: - super().ev_keydown(event) + def on_event(self, event: tcod.event.Event) -> None: + match event: + case tcod.event.KeyDown(sym=sym) if KeySym.N9 >= sym >= KeySym.N1: + self.func = sym - tcod.event.KeySym.N1 + self.noise = self.get_noise() + case tcod.event.KeyDown(sym=KeySym.E): + self.hurst += 0.1 + self.noise = self.get_noise() + case tcod.event.KeyDown(sym=KeySym.D): + self.hurst -= 0.1 + self.noise = self.get_noise() + case tcod.event.KeyDown(sym=KeySym.R): + self.lacunarity += 0.5 + self.noise = self.get_noise() + case tcod.event.KeyDown(sym=KeySym.F): + self.lacunarity -= 0.5 + self.noise = self.get_noise() + case tcod.event.KeyDown(sym=KeySym.T): + self.octaves += 0.5 + self.noise.octaves = self.octaves + case tcod.event.KeyDown(sym=KeySym.G): + self.octaves -= 0.5 + self.noise.octaves = self.octaves + case tcod.event.KeyDown(sym=KeySym.Y): + self.zoom += 0.2 + case tcod.event.KeyDown(sym=KeySym.H): + self.zoom -= 0.2 + case _: + super().on_event(event) ############################################# @@ -489,7 +504,7 @@ def ev_keydown(self, event: tcod.event.KeyDown) -> None: DARK_GROUND = (50, 50, 150) LIGHT_GROUND = (200, 180, 50) -SAMPLE_MAP_ = [ +SAMPLE_MAP_ = ( "##############################################", "####################### #################", "##################### # ###############", @@ -510,11 +525,11 @@ def ev_keydown(self, event: tcod.event.KeyDown) -> None: "######## # #### ##### #####", "######## ##### ####################", "##############################################", -] +) -SAMPLE_MAP: NDArray[Any] = np.array([list(line) for line in SAMPLE_MAP_]).transpose() +SAMPLE_MAP: NDArray[Any] = np.array([[ord(c) for c in line] for line in SAMPLE_MAP_]) -FOV_ALGO_NAMES = [ +FOV_ALGO_NAMES = ( "BASIC ", "DIAMOND ", "SHADOW ", @@ -529,16 +544,16 @@ def ev_keydown(self, event: tcod.event.KeyDown) -> None: "PERMISSIVE8", "RESTRICTIVE", "SYMMETRIC_SHADOWCAST", -] +) TORCH_RADIUS = 10 SQUARED_TORCH_RADIUS = TORCH_RADIUS * TORCH_RADIUS class FOVSample(Sample): - def __init__(self) -> None: - self.name = "Field of view" + name = "Field of view" + def __init__(self) -> None: self.player_x = 20 self.player_y = 10 self.torch = False @@ -546,29 +561,26 @@ def __init__(self) -> None: self.algo_num = libtcodpy.FOV_SYMMETRIC_SHADOWCAST self.noise = tcod.noise.Noise(1) # 1D noise for the torch flickering. - map_shape = (SAMPLE_SCREEN_WIDTH, SAMPLE_SCREEN_HEIGHT) + map_shape = (SAMPLE_SCREEN_HEIGHT, SAMPLE_SCREEN_WIDTH) - self.walkable: NDArray[np.bool_] = np.zeros(map_shape, dtype=bool, order="F") - self.walkable[:] = SAMPLE_MAP[:] == " " + self.walkable: NDArray[np.bool_] = np.zeros(map_shape, dtype=bool) + self.walkable[:] = SAMPLE_MAP[:] == ord(" ") - self.transparent: NDArray[np.bool_] = np.zeros(map_shape, dtype=bool, order="F") - self.transparent[:] = self.walkable[:] | (SAMPLE_MAP == "=") + self.transparent: NDArray[np.bool_] = np.zeros(map_shape, dtype=bool) + self.transparent[:] = self.walkable[:] | (SAMPLE_MAP[:] == ord("=")) # Lit background colors for the map. self.light_map_bg: NDArray[np.uint8] = np.full(SAMPLE_MAP.shape, LIGHT_GROUND, dtype="3B") - self.light_map_bg[SAMPLE_MAP[:] == "#"] = LIGHT_WALL + self.light_map_bg[SAMPLE_MAP[:] == ord("#")] = LIGHT_WALL # Dark background colors for the map. self.dark_map_bg: NDArray[np.uint8] = np.full(SAMPLE_MAP.shape, DARK_GROUND, dtype="3B") - self.dark_map_bg[SAMPLE_MAP[:] == "#"] = DARK_WALL + self.dark_map_bg[SAMPLE_MAP[:] == ord("#")] = DARK_WALL def draw_ui(self) -> None: sample_console.print( 1, 1, - "IJKL : move around\n" - "T : torch fx {}\n" - "W : light walls {}\n" - "+-: algo {}".format( + "IJKL : move around\nT : torch fx {}\nW : light walls {}\n+-: algo {}".format( "on " if self.torch else "off", "on " if self.light_walls else "off", FOV_ALGO_NAMES[self.algo_num], @@ -583,13 +595,13 @@ def on_draw(self) -> None: self.draw_ui() sample_console.print(self.player_x, self.player_y, "@") # Draw windows. - sample_console.rgb["ch"][SAMPLE_MAP == "="] = 0x2550 # BOX DRAWINGS DOUBLE HORIZONTAL - sample_console.rgb["fg"][SAMPLE_MAP == "="] = BLACK + sample_console.rgb["ch"][SAMPLE_MAP[:] == ord("=")] = 0x2550 # BOX DRAWINGS DOUBLE HORIZONTAL + sample_console.rgb["fg"][SAMPLE_MAP[:] == ord("=")] = BLACK # Get a 2D boolean array of visible cells. fov = tcod.map.compute_fov( transparency=self.transparent, - pov=(self.player_x, self.player_y), + pov=(self.player_y, self.player_x), radius=TORCH_RADIUS if self.torch else 0, light_walls=self.light_walls, algorithm=self.algo_num, @@ -597,7 +609,7 @@ def on_draw(self) -> None: if self.torch: # Derive the touch from noise based on the current time. - torch_t = time.perf_counter() * 5 + torch_t = _get_elapsed_time() * 5 # Randomize the light position between -1.5 and 1.5 torch_x = self.player_x + self.noise.get_point(torch_t) * 1.5 torch_y = self.player_y + self.noise.get_point(torch_t + 11) * 1.5 @@ -605,7 +617,7 @@ def on_draw(self) -> None: brightness = 0.2 * self.noise.get_point(torch_t + 17) # Get the squared distance using a mesh grid. - x, y = np.mgrid[:SAMPLE_SCREEN_WIDTH, :SAMPLE_SCREEN_HEIGHT] + y, x = np.mgrid[:SAMPLE_SCREEN_HEIGHT, :SAMPLE_SCREEN_WIDTH] # Center the mesh grid on the torch position. x = x.astype(np.float32) - torch_x y = y.astype(np.float32) - torch_y @@ -629,268 +641,186 @@ def on_draw(self) -> None: # Linear interpolation between colors. sample_console.rgb["bg"] = dark_bg + (light_bg - dark_bg) * light[..., np.newaxis] else: - sample_console.bg[...] = np.where(fov[:, :, np.newaxis], self.light_map_bg, self.dark_map_bg) - - def ev_keydown(self, event: tcod.event.KeyDown) -> None: - MOVE_KEYS = { - tcod.event.KeySym.i: (0, -1), - tcod.event.KeySym.j: (-1, 0), - tcod.event.KeySym.k: (0, 1), - tcod.event.KeySym.l: (1, 0), + sample_console.bg[...] = np.select( + condlist=[fov[:, :, np.newaxis]], + choicelist=[self.light_map_bg], + default=self.dark_map_bg, + ) + + def on_event(self, event: tcod.event.Event) -> None: + MOVE_KEYS = { # noqa: N806 + tcod.event.KeySym.I: (0, -1), + tcod.event.KeySym.J: (-1, 0), + tcod.event.KeySym.K: (0, 1), + tcod.event.KeySym.L: (1, 0), } - FOV_SELECT_KEYS = { + FOV_SELECT_KEYS = { # noqa: N806 tcod.event.KeySym.MINUS: -1, tcod.event.KeySym.EQUALS: 1, tcod.event.KeySym.KP_MINUS: -1, tcod.event.KeySym.KP_PLUS: 1, } - if event.sym in MOVE_KEYS: - x, y = MOVE_KEYS[event.sym] - if self.walkable[self.player_x + x, self.player_y + y]: - self.player_x += x - self.player_y += y - elif event.sym == tcod.event.KeySym.t: - self.torch = not self.torch - elif event.sym == tcod.event.KeySym.w: - self.light_walls = not self.light_walls - elif event.sym in FOV_SELECT_KEYS: - self.algo_num += FOV_SELECT_KEYS[event.sym] - self.algo_num %= len(FOV_ALGO_NAMES) - else: - super().ev_keydown(event) + match event: + case tcod.event.KeyDown(sym=sym) if sym in MOVE_KEYS: + x, y = MOVE_KEYS[sym] + if self.walkable[self.player_y + y, self.player_x + x]: + self.player_x += x + self.player_y += y + case tcod.event.KeyDown(sym=KeySym.T): + self.torch = not self.torch + case tcod.event.KeyDown(sym=KeySym.W): + self.light_walls = not self.light_walls + case tcod.event.KeyDown(sym=sym) if sym in FOV_SELECT_KEYS: + self.algo_num += FOV_SELECT_KEYS[sym] + self.algo_num %= len(FOV_ALGO_NAMES) + case _: + super().on_event(event) class PathfindingSample(Sample): - def __init__(self) -> None: - self.name = "Path finding" + name = "Path finding" - self.px = 20 - self.py = 10 - self.dx = 24 - self.dy = 1 - self.dijkstra_dist = 0.0 + def __init__(self) -> None: + """Initialize this sample.""" + self.player_x = 20 + self.player_y = 10 + self.dest_x = 24 + self.dest_y = 1 self.using_astar = True - self.recalculate = False self.busy = 0.0 - self.oldchar = " " + self.cost = SAMPLE_MAP[:] == ord(" ") + self.graph = tcod.path.SimpleGraph(cost=self.cost, cardinal=70, diagonal=99) + self.pathfinder = tcod.path.Pathfinder(graph=self.graph) - self.map = tcod.map.Map(SAMPLE_SCREEN_WIDTH, SAMPLE_SCREEN_HEIGHT) - for y in range(SAMPLE_SCREEN_HEIGHT): - for x in range(SAMPLE_SCREEN_WIDTH): - if SAMPLE_MAP[x, y] == " ": - # ground - self.map.walkable[y, x] = True - self.map.transparent[y, x] = True - elif SAMPLE_MAP[x, y] == "=": - # window - self.map.walkable[y, x] = False - self.map.transparent[y, x] = True - self.path = tcod.path.AStar(self.map) - self.dijkstra = tcod.path.Dijkstra(self.map) + self.background_console = tcod.console.Console(SAMPLE_SCREEN_WIDTH, SAMPLE_SCREEN_HEIGHT) + + # draw the dungeon + self.background_console.rgb["fg"] = BLACK + self.background_console.rgb["bg"] = DARK_GROUND + self.background_console.rgb["bg"][SAMPLE_MAP[:] == ord("#")] = DARK_WALL + self.background_console.rgb["ch"][SAMPLE_MAP[:] == ord("=")] = ord("═") def on_enter(self) -> None: - # we draw the foreground only the first time. - # during the player movement, only the @ is redrawn. - # the rest impacts only the background color - # draw the help text & player @ - sample_console.clear() - sample_console.ch[self.dx, self.dy] = ord("+") - sample_console.fg[self.dx, self.dy] = WHITE - sample_console.ch[self.px, self.py] = ord("@") - sample_console.fg[self.px, self.py] = WHITE - sample_console.print( - 1, - 1, - "IJKL / mouse :\nmove destination\nTAB : A*/dijkstra", - fg=WHITE, - bg=None, - ) - sample_console.print(1, 4, "Using : A*", fg=WHITE, bg=None) - # draw windows - for y in range(SAMPLE_SCREEN_HEIGHT): - for x in range(SAMPLE_SCREEN_WIDTH): - if SAMPLE_MAP[x, y] == "=": - libtcodpy.console_put_char(sample_console, x, y, libtcodpy.CHAR_DHLINE, libtcodpy.BKGND_NONE) - self.recalculate = True + """Do nothing.""" def on_draw(self) -> None: - if self.recalculate: - if self.using_astar: - libtcodpy.path_compute(self.path, self.px, self.py, self.dx, self.dy) - else: - self.dijkstra_dist = 0.0 - # compute dijkstra grid (distance from px,py) - libtcodpy.dijkstra_compute(self.dijkstra, self.px, self.py) - # get the maximum distance (needed for rendering) - for y in range(SAMPLE_SCREEN_HEIGHT): - for x in range(SAMPLE_SCREEN_WIDTH): - d = libtcodpy.dijkstra_get_distance(self.dijkstra, x, y) - if d > self.dijkstra_dist: - self.dijkstra_dist = d - # compute path from px,py to dx,dy - libtcodpy.dijkstra_path_set(self.dijkstra, self.dx, self.dy) - self.recalculate = False - self.busy = 0.2 + """Recompute and render pathfinding.""" + self.pathfinder = tcod.path.Pathfinder(graph=self.graph) + # self.pathfinder.clear() # Known issues, needs fixing # noqa: ERA001 + self.pathfinder.add_root((self.player_y, self.player_x)) + # draw the dungeon - for y in range(SAMPLE_SCREEN_HEIGHT): - for x in range(SAMPLE_SCREEN_WIDTH): - if SAMPLE_MAP[x, y] == "#": - libtcodpy.console_set_char_background(sample_console, x, y, DARK_WALL, libtcodpy.BKGND_SET) - else: - libtcodpy.console_set_char_background(sample_console, x, y, DARK_GROUND, libtcodpy.BKGND_SET) + self.background_console.blit(dest=sample_console) + + sample_console.print(self.dest_x, self.dest_y, "+", fg=WHITE) + sample_console.print(self.player_x, self.player_y, "@", fg=WHITE) + sample_console.print(1, 1, "IJKL / mouse :\nmove destination\nTAB : A*/dijkstra", fg=WHITE, bg=None) + sample_console.print(1, 4, "Using : A*", fg=WHITE, bg=None) + + if not self.using_astar: + self.pathfinder.resolve(goal=None) + reachable = self.pathfinder.distance != np.iinfo(self.pathfinder.distance.dtype).max + + # draw distance from player + dijkstra_max_dist = float(self.pathfinder.distance[reachable].max()) + np.array(self.pathfinder.distance, copy=True, dtype=np.float32) + interpolate = self.pathfinder.distance[reachable] * 0.9 / dijkstra_max_dist + color_delta = (np.array(DARK_GROUND) - np.array(LIGHT_GROUND)).astype(np.float32) + sample_console.rgb["bg"][reachable] = np.array(LIGHT_GROUND) + interpolate[:, np.newaxis] * color_delta + # draw the path - if self.using_astar: - for i in range(libtcodpy.path_size(self.path)): - x, y = libtcodpy.path_get(self.path, i) - libtcodpy.console_set_char_background(sample_console, x, y, LIGHT_GROUND, libtcodpy.BKGND_SET) - else: - for y in range(SAMPLE_SCREEN_HEIGHT): - for x in range(SAMPLE_SCREEN_WIDTH): - if SAMPLE_MAP[x, y] != "#": - libtcodpy.console_set_char_background( - sample_console, - x, - y, - libtcodpy.color_lerp( # type: ignore - LIGHT_GROUND, - DARK_GROUND, - 0.9 * libtcodpy.dijkstra_get_distance(self.dijkstra, x, y) / self.dijkstra_dist, - ), - libtcodpy.BKGND_SET, - ) - for i in range(libtcodpy.dijkstra_size(self.dijkstra)): - x, y = libtcodpy.dijkstra_get(self.dijkstra, i) - libtcodpy.console_set_char_background(sample_console, x, y, LIGHT_GROUND, libtcodpy.BKGND_SET) + path = self.pathfinder.path_to((self.dest_y, self.dest_x))[1:] + sample_console.rgb["bg"][tuple(path.T)] = LIGHT_GROUND # move the creature self.busy -= frame_length[-1] if self.busy <= 0.0: self.busy = 0.2 - if self.using_astar: - if not libtcodpy.path_is_empty(self.path): - libtcodpy.console_put_char(sample_console, self.px, self.py, " ", libtcodpy.BKGND_NONE) - self.px, self.py = libtcodpy.path_walk(self.path, True) # type: ignore - libtcodpy.console_put_char(sample_console, self.px, self.py, "@", libtcodpy.BKGND_NONE) - else: - if not libtcodpy.dijkstra_is_empty(self.dijkstra): - libtcodpy.console_put_char(sample_console, self.px, self.py, " ", libtcodpy.BKGND_NONE) - self.px, self.py = libtcodpy.dijkstra_path_walk(self.dijkstra) # type: ignore - libtcodpy.console_put_char(sample_console, self.px, self.py, "@", libtcodpy.BKGND_NONE) - self.recalculate = True - - def ev_keydown(self, event: tcod.event.KeyDown) -> None: - if event.sym == tcod.event.KeySym.i and self.dy > 0: - # destination move north - libtcodpy.console_put_char(sample_console, self.dx, self.dy, self.oldchar, libtcodpy.BKGND_NONE) - self.dy -= 1 - self.oldchar = sample_console.ch[self.dx, self.dy] - libtcodpy.console_put_char(sample_console, self.dx, self.dy, "+", libtcodpy.BKGND_NONE) - if SAMPLE_MAP[self.dx, self.dy] == " ": - self.recalculate = True - elif event.sym == tcod.event.KeySym.k and self.dy < SAMPLE_SCREEN_HEIGHT - 1: - # destination move south - libtcodpy.console_put_char(sample_console, self.dx, self.dy, self.oldchar, libtcodpy.BKGND_NONE) - self.dy += 1 - self.oldchar = sample_console.ch[self.dx, self.dy] - libtcodpy.console_put_char(sample_console, self.dx, self.dy, "+", libtcodpy.BKGND_NONE) - if SAMPLE_MAP[self.dx, self.dy] == " ": - self.recalculate = True - elif event.sym == tcod.event.KeySym.j and self.dx > 0: - # destination move west - libtcodpy.console_put_char(sample_console, self.dx, self.dy, self.oldchar, libtcodpy.BKGND_NONE) - self.dx -= 1 - self.oldchar = sample_console.ch[self.dx, self.dy] - libtcodpy.console_put_char(sample_console, self.dx, self.dy, "+", libtcodpy.BKGND_NONE) - if SAMPLE_MAP[self.dx, self.dy] == " ": - self.recalculate = True - elif event.sym == tcod.event.KeySym.l and self.dx < SAMPLE_SCREEN_WIDTH - 1: - # destination move east - libtcodpy.console_put_char(sample_console, self.dx, self.dy, self.oldchar, libtcodpy.BKGND_NONE) - self.dx += 1 - self.oldchar = sample_console.ch[self.dx, self.dy] - libtcodpy.console_put_char(sample_console, self.dx, self.dy, "+", libtcodpy.BKGND_NONE) - if SAMPLE_MAP[self.dx, self.dy] == " ": - self.recalculate = True - elif event.sym == tcod.event.KeySym.TAB: - self.using_astar = not self.using_astar - if self.using_astar: - libtcodpy.console_print(sample_console, 1, 4, "Using : A* ") - else: - libtcodpy.console_print(sample_console, 1, 4, "Using : Dijkstra") - self.recalculate = True - else: - super().ev_keydown(event) - - def ev_mousemotion(self, event: tcod.event.MouseMotion) -> None: - mx = event.tile.x - SAMPLE_SCREEN_X - my = event.tile.y - SAMPLE_SCREEN_Y - if 0 <= mx < SAMPLE_SCREEN_WIDTH and 0 <= my < SAMPLE_SCREEN_HEIGHT and (self.dx != mx or self.dy != my): - libtcodpy.console_put_char(sample_console, self.dx, self.dy, self.oldchar, libtcodpy.BKGND_NONE) - self.dx = mx - self.dy = my - self.oldchar = sample_console.ch[self.dx, self.dy] - libtcodpy.console_put_char(sample_console, self.dx, self.dy, "+", libtcodpy.BKGND_NONE) - if SAMPLE_MAP[self.dx, self.dy] == " ": - self.recalculate = True + if len(path): + self.player_y = int(path.item(0, 0)) + self.player_x = int(path.item(0, 1)) + + def on_event(self, event: tcod.event.Event) -> None: + """Handle movement and UI.""" + match event: + case tcod.event.KeyDown(sym=KeySym.I) if self.dest_y > 0: # destination move north + self.dest_y -= 1 + case tcod.event.KeyDown(sym=KeySym.K) if self.dest_y < SAMPLE_SCREEN_HEIGHT - 1: # destination move south + self.dest_y += 1 + case tcod.event.KeyDown(sym=KeySym.J) if self.dest_x > 0: # destination move west + self.dest_x -= 1 + case tcod.event.KeyDown(sym=KeySym.L) if self.dest_x < SAMPLE_SCREEN_WIDTH - 1: # destination move east + self.dest_x += 1 + case tcod.event.KeyDown(sym=KeySym.TAB): + self.using_astar = not self.using_astar + case tcod.event.MouseMotion(): # Move destination via mouseover + mx = event.tile.x - SAMPLE_SCREEN_X + my = event.tile.y - SAMPLE_SCREEN_Y + if 0 <= mx < SAMPLE_SCREEN_WIDTH and 0 <= my < SAMPLE_SCREEN_HEIGHT: + self.dest_x = int(mx) + self.dest_y = int(my) + case _: + super().on_event(event) ############################################# # bsp sample ############################################# -bsp_depth = 8 -bsp_min_room_size = 4 -# a room fills a random part of the node or the maximum available space ? -bsp_random_room = False -# if true, there is always a wall on north & west side of a room -bsp_room_walls = True # draw a vertical line -def vline(m: NDArray[np.bool_], x: int, y1: int, y2: int) -> None: +def vline(map_: NDArray[np.bool_], x: int, y1: int, y2: int) -> None: if y1 > y2: y1, y2 = y2, y1 for y in range(y1, y2 + 1): - m[x, y] = True + map_[y, x] = True # draw a vertical line up until we reach an empty space -def vline_up(m: NDArray[np.bool_], x: int, y: int) -> None: - while y >= 0 and not m[x, y]: - m[x, y] = True +def vline_up(map_: NDArray[np.bool_], x: int, y: int) -> None: + while y >= 0 and not map_[y, x]: + map_[y, x] = True y -= 1 # draw a vertical line down until we reach an empty space -def vline_down(m: NDArray[np.bool_], x: int, y: int) -> None: - while y < SAMPLE_SCREEN_HEIGHT and not m[x, y]: - m[x, y] = True +def vline_down(map_: NDArray[np.bool_], x: int, y: int) -> None: + while y < SAMPLE_SCREEN_HEIGHT and not map_[y, x]: + map_[y, x] = True y += 1 # draw a horizontal line -def hline(m: NDArray[np.bool_], x1: int, y: int, x2: int) -> None: +def hline(map_: NDArray[np.bool_], x1: int, y: int, x2: int) -> None: if x1 > x2: x1, x2 = x2, x1 for x in range(x1, x2 + 1): - m[x, y] = True + map_[y, x] = True # draw a horizontal line left until we reach an empty space -def hline_left(m: NDArray[np.bool_], x: int, y: int) -> None: - while x >= 0 and not m[x, y]: - m[x, y] = True +def hline_left(map_: NDArray[np.bool_], x: int, y: int) -> None: + while x >= 0 and not map_[y, x]: + map_[y, x] = True x -= 1 # draw a horizontal line right until we reach an empty space -def hline_right(m: NDArray[np.bool_], x: int, y: int) -> None: - while x < SAMPLE_SCREEN_WIDTH and not m[x, y]: - m[x, y] = True +def hline_right(map_: NDArray[np.bool_], x: int, y: int) -> None: + while x < SAMPLE_SCREEN_WIDTH and not map_[y, x]: + map_[y, x] = True x += 1 # the class building the dungeon from the bsp nodes -def traverse_node(bsp_map: NDArray[np.bool_], node: tcod.bsp.BSP) -> None: +def traverse_node( + bsp_map: NDArray[np.bool_], + node: tcod.bsp.BSP, + *, + bsp_min_room_size: int, + bsp_random_room: bool, + bsp_room_walls: bool, +) -> None: if not node.children: # calculate the room size if bsp_room_walls: @@ -903,7 +833,7 @@ def traverse_node(bsp_map: NDArray[np.bool_], node: tcod.bsp.BSP) -> None: node.y += random.randint(0, node.height - new_height) node.width, node.height = new_width, new_height # dig the room - bsp_map[node.x : node.x + node.width, node.y : node.y + node.height] = True + bsp_map[node.y : node.y + node.height, node.x : node.x + node.width] = True else: # resize the node to fit its sons left, right = node.children @@ -947,92 +877,105 @@ def traverse_node(bsp_map: NDArray[np.bool_], node: tcod.bsp.BSP) -> None: class BSPSample(Sample): + name = "Bsp toolkit" + def __init__(self) -> None: - self.name = "Bsp toolkit" self.bsp = tcod.bsp.BSP(1, 1, SAMPLE_SCREEN_WIDTH - 1, SAMPLE_SCREEN_HEIGHT - 1) - self.bsp_map: NDArray[np.bool_] = np.zeros((SAMPLE_SCREEN_WIDTH, SAMPLE_SCREEN_HEIGHT), dtype=bool, order="F") + self.bsp_map: NDArray[np.bool_] = np.zeros((SAMPLE_SCREEN_HEIGHT, SAMPLE_SCREEN_WIDTH), dtype=bool) + + self.bsp_depth = 8 + self.bsp_min_room_size = 4 + self.bsp_random_room = False # a room fills a random part of the node or the maximum available space ? + self.bsp_room_walls = True # if true, there is always a wall on north & west side of a room + self.bsp_generate() def bsp_generate(self) -> None: self.bsp.children = () - if bsp_room_walls: + if self.bsp_room_walls: self.bsp.split_recursive( - bsp_depth, - bsp_min_room_size + 1, - bsp_min_room_size + 1, + self.bsp_depth, + self.bsp_min_room_size + 1, + self.bsp_min_room_size + 1, 1.5, 1.5, ) else: - self.bsp.split_recursive(bsp_depth, bsp_min_room_size, bsp_min_room_size, 1.5, 1.5) + self.bsp.split_recursive(self.bsp_depth, self.bsp_min_room_size, self.bsp_min_room_size, 1.5, 1.5) self.bsp_refresh() def bsp_refresh(self) -> None: self.bsp_map[...] = False for node in copy.deepcopy(self.bsp).inverted_level_order(): - traverse_node(self.bsp_map, node) + traverse_node( + self.bsp_map, + node, + bsp_min_room_size=self.bsp_min_room_size, + bsp_random_room=self.bsp_random_room, + bsp_room_walls=self.bsp_room_walls, + ) def on_draw(self) -> None: sample_console.clear() rooms = "OFF" - if bsp_random_room: + if self.bsp_random_room: rooms = "ON" sample_console.print( 1, 1, "ENTER : rebuild bsp\n" "SPACE : rebuild dungeon\n" - "+-: bsp depth %d\n" - "*/: room size %d\n" - "1 : random room size %s" % (bsp_depth, bsp_min_room_size, rooms), + f"+-: bsp depth {self.bsp_depth}\n" + f"*/: room size {self.bsp_min_room_size}\n" + f"1 : random room size {rooms}", fg=WHITE, bg=None, ) - if bsp_random_room: + if self.bsp_random_room: walls = "OFF" - if bsp_room_walls: + if self.bsp_room_walls: walls = "ON" - sample_console.print(1, 6, "2 : room walls %s" % walls, fg=WHITE, bg=None) + sample_console.print(1, 6, f"2 : room walls {walls}", fg=WHITE, bg=None) # render the level for y in range(SAMPLE_SCREEN_HEIGHT): for x in range(SAMPLE_SCREEN_WIDTH): - color = DARK_GROUND if self.bsp_map[x][y] else DARK_WALL + color = DARK_GROUND if self.bsp_map[y, x] else DARK_WALL libtcodpy.console_set_char_background(sample_console, x, y, color, libtcodpy.BKGND_SET) - def ev_keydown(self, event: tcod.event.KeyDown) -> None: - global bsp_random_room, bsp_room_walls, bsp_depth, bsp_min_room_size - if event.sym in (tcod.event.KeySym.RETURN, tcod.event.KeySym.KP_ENTER): - self.bsp_generate() - elif event.sym == tcod.event.KeySym.SPACE: - self.bsp_refresh() - elif event.sym in (tcod.event.KeySym.EQUALS, tcod.event.KeySym.KP_PLUS): - bsp_depth += 1 - self.bsp_generate() - elif event.sym in (tcod.event.KeySym.MINUS, tcod.event.KeySym.KP_MINUS): - bsp_depth = max(1, bsp_depth - 1) - self.bsp_generate() - elif event.sym in (tcod.event.KeySym.N8, tcod.event.KeySym.KP_MULTIPLY): - bsp_min_room_size += 1 - self.bsp_generate() - elif event.sym in (tcod.event.KeySym.SLASH, tcod.event.KeySym.KP_DIVIDE): - bsp_min_room_size = max(2, bsp_min_room_size - 1) - self.bsp_generate() - elif event.sym in (tcod.event.KeySym.N1, tcod.event.KeySym.KP_1): - bsp_random_room = not bsp_random_room - if not bsp_random_room: - bsp_room_walls = True - self.bsp_refresh() - elif event.sym in (tcod.event.KeySym.N2, tcod.event.KeySym.KP_2): - bsp_room_walls = not bsp_room_walls - self.bsp_refresh() - else: - super().ev_keydown(event) + def on_event(self, event: tcod.event.Event) -> None: + match event: + case tcod.event.KeyDown(sym=KeySym.RETURN | KeySym.KP_ENTER): + self.bsp_generate() + case tcod.event.KeyDown(sym=KeySym.SPACE): + self.bsp_refresh() + case tcod.event.KeyDown(sym=KeySym.EQUALS | KeySym.KP_PLUS): + self.bsp_depth += 1 + self.bsp_generate() + case tcod.event.KeyDown(sym=KeySym.MINUS | KeySym.KP_MINUS): + self.bsp_depth = max(1, self.bsp_depth - 1) + self.bsp_generate() + case tcod.event.KeyDown(sym=KeySym.N8 | KeySym.KP_MULTIPLY): + self.bsp_min_room_size += 1 + self.bsp_generate() + case tcod.event.KeyDown(sym=KeySym.SLASH | KeySym.KP_DIVIDE): + self.bsp_min_room_size = max(2, self.bsp_min_room_size - 1) + self.bsp_generate() + case tcod.event.KeyDown(sym=KeySym.N1 | KeySym.KP_1): + self.bsp_random_room = not self.bsp_random_room + if not self.bsp_random_room: + self.bsp_room_walls = True + self.bsp_refresh() + case tcod.event.KeyDown(sym=KeySym.N2 | KeySym.KP_2): + self.bsp_room_walls = not self.bsp_room_walls + self.bsp_refresh() + case _: + super().on_event(event) class ImageSample(Sample): - def __init__(self) -> None: - self.name = "Image toolkit" + name = "Image toolkit" + def __init__(self) -> None: self.img = tcod.image.Image.from_file(DATA_DIR / "img/skull.png") self.img.set_key_color(BLACK) self.circle = tcod.image.Image.from_file(DATA_DIR / "img/circle.png") @@ -1043,7 +986,7 @@ def on_draw(self) -> None: y = sample_console.height / 2 scalex = 0.2 + 1.8 * (1.0 + math.cos(time.time() / 2)) / 2.0 scaley = scalex - angle = time.perf_counter() + angle = _get_elapsed_time() if int(time.time()) % 2: # split the color channels of circle.png # the red channel @@ -1064,11 +1007,10 @@ def on_draw(self) -> None: class MouseSample(Sample): - def __init__(self) -> None: - self.name = "Mouse support" + name = "Mouse support" + def __init__(self) -> None: self.motion = tcod.event.MouseMotion() - self.mouse_left = self.mouse_middle = self.mouse_right = 0 self.log: list[str] = [] def on_enter(self) -> None: @@ -1077,47 +1019,20 @@ def on_enter(self) -> None: tcod.sdl.mouse.warp_in_window(sdl_window, 320, 200) tcod.sdl.mouse.show(True) - def ev_mousemotion(self, event: tcod.event.MouseMotion) -> None: - self.motion = event - - def ev_mousebuttondown(self, event: tcod.event.MouseButtonDown) -> None: - if event.button == tcod.event.BUTTON_LEFT: - self.mouse_left = True - elif event.button == tcod.event.BUTTON_MIDDLE: - self.mouse_middle = True - elif event.button == tcod.event.BUTTON_RIGHT: - self.mouse_right = True - - def ev_mousebuttonup(self, event: tcod.event.MouseButtonUp) -> None: - if event.button == tcod.event.BUTTON_LEFT: - self.mouse_left = False - elif event.button == tcod.event.BUTTON_MIDDLE: - self.mouse_middle = False - elif event.button == tcod.event.BUTTON_RIGHT: - self.mouse_right = False - def on_draw(self) -> None: sample_console.clear(bg=GREY) + mouse_state = tcod.event.get_mouse_state() sample_console.print( 1, 1, - "Pixel position : %4dx%4d\n" - "Tile position : %4dx%4d\n" - "Tile movement : %4dx%4d\n" - "Left button : %s\n" - "Right button : %s\n" - "Middle button : %s\n" - % ( - self.motion.position.x, - self.motion.position.y, - self.motion.tile.x, - self.motion.tile.y, - self.motion.tile_motion.x, - self.motion.tile_motion.y, - ("OFF", "ON")[self.mouse_left], - ("OFF", "ON")[self.mouse_right], - ("OFF", "ON")[self.mouse_middle], - ), + f"Pixel position : {mouse_state.position.x:4.0f}x{mouse_state.position.y:4.0f}\n" + f"Tile position : {self.motion.tile.x:4d}x{self.motion.tile.y:4d}\n" + f"Tile movement : {self.motion.tile_motion.x:4d}x{self.motion.tile_motion.y:4d}\n" + f"Left button : {'ON' if mouse_state.state & tcod.event.MouseButtonMask.LEFT else 'OFF'}\n" + f"Middle button : {'ON' if mouse_state.state & tcod.event.MouseButtonMask.MIDDLE else 'OFF'}\n" + f"Right button : {'ON' if mouse_state.state & tcod.event.MouseButtonMask.RIGHT else 'OFF'}\n" + f"X1 button : {'ON' if mouse_state.state & tcod.event.MouseButtonMask.X1 else 'OFF'}\n" + f"X2 button : {'ON' if mouse_state.state & tcod.event.MouseButtonMask.X2 else 'OFF'}\n", fg=LIGHT_YELLOW, bg=None, ) @@ -1129,20 +1044,23 @@ def on_draw(self) -> None: bg=None, ) - def ev_keydown(self, event: tcod.event.KeyDown) -> None: - if event.sym == tcod.event.KeySym.N1: - tcod.sdl.mouse.show(False) - elif event.sym == tcod.event.KeySym.N2: - tcod.sdl.mouse.show(True) - else: - super().ev_keydown(event) + def on_event(self, event: tcod.event.Event) -> None: + match event: + case tcod.event.MouseMotion(): + self.motion = event + case tcod.event.KeyDown(sym=KeySym.N1): + tcod.sdl.mouse.show(visible=False) + case tcod.event.KeyDown(sym=KeySym.N2): + tcod.sdl.mouse.show(visible=True) + case _: + super().on_event(event) class NameGeneratorSample(Sample): - def __init__(self) -> None: - self.name = "Name generator" + name = "Name generator" - self.curset = 0 + def __init__(self) -> None: + self.current_set = 0 self.delay = 0.0 self.names: list[str] = [] self.sets: list[str] = [] @@ -1162,13 +1080,13 @@ def on_draw(self) -> None: sample_console.print( 1, 1, - "%s\n\n+ : next generator\n- : prev generator" % self.sets[self.curset], + f"{self.sets[self.current_set]}\n\n+ : next generator\n- : prev generator", fg=WHITE, bg=None, ) for i in range(len(self.names)): sample_console.print( - SAMPLE_SCREEN_WIDTH - 2, + SAMPLE_SCREEN_WIDTH - 1, 2 + i, self.names[i], fg=WHITE, @@ -1178,26 +1096,24 @@ def on_draw(self) -> None: self.delay += frame_length[-1] if self.delay > 0.5: self.delay -= 0.5 - self.names.append(libtcodpy.namegen_generate(self.sets[self.curset])) - - def ev_keydown(self, event: tcod.event.KeyDown) -> None: - if event.sym == tcod.event.KeySym.EQUALS: - self.curset += 1 - self.names.append("======") - elif event.sym == tcod.event.KeySym.MINUS: - self.curset -= 1 - self.names.append("======") - else: - super().ev_keydown(event) - self.curset %= len(self.sets) + self.names.append(libtcodpy.namegen_generate(self.sets[self.current_set])) + + def on_event(self, event: tcod.event.Event) -> None: + match event: + case tcod.event.KeyDown(sym=KeySym.EQUALS): + self.current_set += 1 + self.names.append("======") + case tcod.event.KeyDown(sym=KeySym.MINUS): + self.current_set -= 1 + self.names.append("======") + case _: + super().on_event(event) + self.current_set %= len(self.sets) ############################################# # python fast render sample ############################################# -numpy_available = True - -use_numpy = numpy_available # default option SCREEN_W = SAMPLE_SCREEN_WIDTH SCREEN_H = SAMPLE_SCREEN_HEIGHT HALF_W = SCREEN_W // 2 @@ -1215,38 +1131,33 @@ def ev_keydown(self, event: tcod.event.KeyDown) -> None: # the coordinates of all tiles in the screen, as numpy arrays. # example: (4x3 pixels screen) -# xc = [[1, 2, 3, 4], [1, 2, 3, 4], [1, 2, 3, 4]] -# yc = [[1, 1, 1, 1], [2, 2, 2, 2], [3, 3, 3, 3]] -if numpy_available: - (xc, yc) = np.meshgrid(range(SCREEN_W), range(SCREEN_H)) - # translate coordinates of all pixels to center - xc = xc - HALF_W - yc = yc - HALF_H - -noise2d = tcod.noise.Noise(2, hurst=0.5, lacunarity=2.0) -if numpy_available: # the texture starts empty - texture = np.zeros((RES_U, RES_V)) +# xc = [[1, 2, 3, 4], [1, 2, 3, 4], [1, 2, 3, 4]] # noqa: ERA001 +# yc = [[1, 1, 1, 1], [2, 2, 2, 2], [3, 3, 3, 3]] # noqa: ERA001 +(xc, yc) = np.meshgrid(range(SCREEN_W), range(SCREEN_H)) +# translate coordinates of all pixels to center +xc = xc - HALF_W +yc = yc - HALF_H +@dataclass(frozen=False, slots=True) class Light: - def __init__( - self, - x: float, - y: float, - z: float, - r: int, - g: int, - b: int, - strength: float, - ) -> None: - self.x, self.y, self.z = x, y, z # pos. - self.r, self.g, self.b = r, g, b # color - self.strength = strength # between 0 and 1, defines brightness + """Lighting effect entity.""" + + x: float # pos + y: float + z: float + r: int # color + g: int + b: int + strength: float # between 0 and 1, defines brightness class FastRenderSample(Sample): + name = "Python fast render" + def __init__(self) -> None: - self.name = "Python fast render" + self.texture = np.zeros((RES_U, RES_V)) + self.noise2d = tcod.noise.Noise(2, hurst=0.5, lacunarity=2.0) def on_enter(self) -> None: sample_console.clear() # render status message @@ -1263,8 +1174,6 @@ def on_enter(self) -> None: self.tex_b = 0.0 def on_draw(self) -> None: - global texture - time_delta = frame_length[-1] * SPEED # advance time self.frac_t += time_delta # increase fractional (always < 1.0) time self.abs_t += time_delta # increase absolute elapsed time @@ -1292,14 +1201,14 @@ def on_draw(self) -> None: # new pixels are based on absolute elapsed time int_abs_t = int(self.abs_t) - texture = np.roll(texture, -int_t, 1) + self.texture = np.roll(self.texture, -int_t, 1) # replace new stretch of texture with new values for v in range(RES_V - int_t, RES_V): - for u in range(0, RES_U): + for u in range(RES_U): tex_v = (v + int_abs_t) / float(RES_V) - texture[u, v] = tcod.noise_get_fbm(noise2d, [u / float(RES_U), tex_v], 32.0) + tcod.noise_get_fbm( - noise2d, [1 - u / float(RES_U), tex_v], 32.0 - ) + self.texture[u, v] = libtcodpy.noise_get_fbm( + self.noise2d, [u / float(RES_U), tex_v], 32.0 + ) + libtcodpy.noise_get_fbm(self.noise2d, [1 - u / float(RES_U), tex_v], 32.0) # squared distance from center, # clipped to sensible minimum and maximum values @@ -1314,12 +1223,12 @@ def on_draw(self) -> None: uu = np.mod(RES_U * (np.arctan2(yc, xc) / (2 * np.pi) + 0.5), RES_U) # retrieve corresponding pixels from texture - brightness = texture[uu.astype(int), vv.astype(int)] / 4.0 + 0.5 + brightness = self.texture[uu.astype(int), vv.astype(int)] / 4.0 + 0.5 # use the brightness map to compose the final color of the tunnel - R = brightness * self.tex_r - G = brightness * self.tex_g - B = brightness * self.tex_b + rr = brightness * self.tex_r + gg = brightness * self.tex_g + bb = brightness * self.tex_b # create new light source if random.random() <= time_delta * LIGHTS_CHANCE and len(self.lights) < MAX_LIGHTS: @@ -1327,9 +1236,9 @@ def on_draw(self) -> None: y = random.uniform(-0.5, 0.5) strength = random.uniform(MIN_LIGHT_STRENGTH, 1.0) - color = tcod.Color(0, 0, 0) # create bright colors with random hue + color = libtcodpy.Color(0, 0, 0) # create bright colors with random hue hue = random.uniform(0, 360) - tcod.color_set_hsv(color, hue, 0.5, strength) + libtcodpy.color_set_hsv(color, hue, 0.5, strength) self.lights.append(Light(x, y, TEX_STRETCH, color.r, color.g, color.b, strength)) # eliminate lights that are going to be out of view @@ -1338,7 +1247,7 @@ def on_draw(self) -> None: for light in self.lights: # render lights # move light's Z coordinate with time, then project its XYZ # coordinates to screen-space - light.z -= float(time_delta) / TEX_STRETCH + light.z -= time_delta / TEX_STRETCH xl = light.x / light.z * SCREEN_H yl = light.y / light.z * SCREEN_H @@ -1349,17 +1258,17 @@ def on_draw(self) -> None: brightness = light_brightness / ((xc - xl) ** 2 + (yc - yl) ** 2) # make all pixels shine around this light - R += brightness * light.r - G += brightness * light.g - B += brightness * light.b + rr += brightness * light.r + gg += brightness * light.g + bb += brightness * light.b # truncate values - R = R.clip(0, 255) - G = G.clip(0, 255) - B = B.clip(0, 255) + rr = rr.clip(0, 255) + gg = gg.clip(0, 255) + bb = bb.clip(0, 255) # fill the screen with these background colors - sample_console.bg.transpose(2, 1, 0)[...] = (R, G, B) + sample_console.bg.transpose(2, 0, 1)[...] = (rr, gg, bb) ############################################# @@ -1405,10 +1314,8 @@ def init_context(renderer: int) -> None: global context, console_render, sample_minimap if "context" in globals(): context.close() - libtcod_version = "%i.%i.%i" % ( - tcod.cffi.lib.TCOD_MAJOR_VERSION, - tcod.cffi.lib.TCOD_MINOR_VERSION, - tcod.cffi.lib.TCOD_PATCHLEVEL, + libtcod_version = ( + f"{tcod.cffi.lib.TCOD_MAJOR_VERSION}.{tcod.cffi.lib.TCOD_MINOR_VERSION}.{tcod.cffi.lib.TCOD_PATCHLEVEL}" ) context = tcod.context.new( columns=root_console.width, @@ -1419,9 +1326,9 @@ def init_context(renderer: int) -> None: ) if context.sdl_renderer: # If this context supports SDL rendering. # Start by setting the logical size so that window resizing doesn't break anything. - context.sdl_renderer.logical_size = ( - tileset.tile_width * root_console.width, - tileset.tile_height * root_console.height, + context.sdl_renderer.set_logical_presentation( + resolution=(tileset.tile_width * root_console.width, tileset.tile_height * root_console.height), + mode=tcod.sdl.render.LogicalPresentation.STRETCH, ) assert context.sdl_atlas # Generate the console renderer and minimap. @@ -1435,40 +1342,14 @@ def init_context(renderer: int) -> None: def main() -> None: - global context, tileset + global tileset tileset = tcod.tileset.load_tilesheet(FONT, 32, 8, tcod.tileset.CHARMAP_TCOD) init_context(libtcodpy.RENDERER_SDL2) try: SAMPLES[cur_sample].on_enter() while True: - root_console.clear() - draw_samples_menu() - draw_renderer_menu() - - # render the sample - SAMPLES[cur_sample].on_draw() - sample_console.blit(root_console, SAMPLE_SCREEN_X, SAMPLE_SCREEN_Y) - draw_stats() - if context.sdl_renderer: - # SDL renderer support, upload the sample console background to a minimap texture. - sample_minimap.update(sample_console.rgb.T["bg"]) - # Render the root_console normally, this is the drawing step of context.present without presenting. - context.sdl_renderer.copy(console_render.render(root_console)) - # Render the minimap to the screen. - context.sdl_renderer.copy( - sample_minimap, - dest=( - tileset.tile_width * 24, - tileset.tile_height * 36, - SAMPLE_SCREEN_WIDTH * 3, - SAMPLE_SCREEN_HEIGHT * 3, - ), - ) - context.sdl_renderer.present() - else: # No SDL renderer, just use plain context rendering. - context.present(root_console) - + redraw_display() handle_time() handle_events() finally: @@ -1478,6 +1359,41 @@ def main() -> None: context.close() +def redraw_display() -> None: + """Full clear-draw-present of the screen.""" + root_console.clear() + draw_samples_menu() + draw_renderer_menu() + + # render the sample + SAMPLES[cur_sample].on_draw() + sample_console.blit(root_console, SAMPLE_SCREEN_X, SAMPLE_SCREEN_Y) + draw_stats() + if 0 <= mouse_tile_xy[0] < root_console.width and 0 <= mouse_tile_xy[1] < root_console.height: + root_console.rgb[["fg", "bg"]].T[mouse_tile_xy] = (0, 0, 0), (255, 255, 255) # Highlight mouse tile + if context.sdl_renderer: + # Clear the screen to ensure no garbage data outside of the logical area is displayed + context.sdl_renderer.draw_color = (0, 0, 0, 255) + context.sdl_renderer.clear() + # SDL renderer support, upload the sample console background to a minimap texture. + sample_minimap.update(sample_console.rgb["bg"]) + # Render the root_console normally, this is the drawing step of context.present without presenting. + context.sdl_renderer.copy(console_render.render(root_console)) + # Render the minimap to the screen. + context.sdl_renderer.copy( + sample_minimap, + dest=( + tileset.tile_width * 24, + tileset.tile_height * 36, + SAMPLE_SCREEN_WIDTH * 3, + SAMPLE_SCREEN_HEIGHT * 3, + ), + ) + context.sdl_renderer.present() + else: # No SDL renderer, just use plain context rendering. + context.present(root_console) + + def handle_time() -> None: if len(frame_times) > 100: frame_times.pop(0) @@ -1486,26 +1402,20 @@ def handle_time() -> None: frame_length.append(frame_times[-1] - frame_times[-2]) +mouse_tile_xy = (-1, -1) +"""Last known mouse tile position.""" + + def handle_events() -> None: + global mouse_tile_xy for event in tcod.event.get(): - if context.sdl_renderer: - # Manual handing of tile coordinates since context.present is skipped. - if isinstance(event, (tcod.event.MouseState, tcod.event.MouseMotion)): - event.tile = tcod.event.Point( - event.position.x // tileset.tile_width, event.position.y // tileset.tile_height - ) - if isinstance(event, tcod.event.MouseMotion): - prev_tile = ( - (event.position[0] - event.motion[0]) // tileset.tile_width, - (event.position[1] - event.motion[1]) // tileset.tile_height, - ) - event.tile_motion = tcod.event.Point(event.tile[0] - prev_tile[0], event.tile[1] - prev_tile[1]) - else: - context.convert_event(event) - - SAMPLES[cur_sample].dispatch(event) - if isinstance(event, tcod.event.Quit): - raise SystemExit() + tile_event = tcod.event.convert_coordinates_from_window(event, context, root_console) + SAMPLES[cur_sample].on_event(tile_event) + match tile_event: + case tcod.event.MouseMotion(integer_position=(x, y)): + mouse_tile_xy = x, y + case tcod.event.WindowEvent(type="WindowLeave"): + mouse_tile_xy = -1, -1 def draw_samples_menu() -> None: @@ -1519,7 +1429,7 @@ def draw_samples_menu() -> None: root_console.print( 2, 46 - (len(SAMPLES) - i), - " %s" % sample.name.ljust(19), + f" {sample.name.ljust(19)}", fg, bg, alignment=libtcodpy.LEFT, @@ -1532,16 +1442,16 @@ def draw_stats() -> None: except ZeroDivisionError: fps = 0 root_console.print( - 79, + root_console.width, 46, - "last frame :%5.1f ms (%4d fps)" % (frame_length[-1] * 1000.0, fps), + f"last frame :{frame_length[-1] * 1000.0:5.1f} ms ({int(fps):4d} fps)", fg=GREY, alignment=libtcodpy.RIGHT, ) root_console.print( - 79, + root_console.width, 47, - "elapsed : %8d ms %5.2fs" % (time.perf_counter() * 1000, time.perf_counter()), + f"elapsed : {int(_get_elapsed_time() * 1000):8d} ms {_get_elapsed_time():5.2f}s", fg=GREY, alignment=libtcodpy.RIGHT, ) @@ -1566,4 +1476,15 @@ def draw_renderer_menu() -> None: if __name__ == "__main__": + if not sys.warnoptions: + warnings.simplefilter("default") # Show all warnings. + + @tcod.event.add_watch + def _handle_events(event: tcod.event.Event) -> None: + """Keep window responsive during resize events.""" + match event: + case tcod.event.WindowEvent(type="WindowExposed"): + redraw_display() + handle_time() + main() diff --git a/examples/sdl-hello-world.py b/examples/sdl-hello-world.py index b9c07547..02017f12 100644 --- a/examples/sdl-hello-world.py +++ b/examples/sdl-hello-world.py @@ -1,8 +1,9 @@ """Hello world using tcod's SDL API and using Pillow for the TTF rendering.""" + from pathlib import Path import numpy as np -from PIL import Image, ImageDraw, ImageFont # type: ignore # pip install Pillow +from PIL import Image, ImageDraw, ImageFont # pip install Pillow import tcod.event import tcod.sdl.render @@ -14,8 +15,9 @@ def render_text(renderer: tcod.sdl.render.Renderer, text: str) -> tcod.sdl.render.Texture: """Render text, upload it to VRAM, then return it as an SDL Texture.""" - # Use Pillow normally to render the font. This code is standard. - width, height = font.getsize(text) + # Use Pillow to render the font. + _left, _top, right, bottom = font.getbbox(text) + width, height = int(right), int(bottom) image = Image.new("RGBA", (width, height)) draw = ImageDraw.Draw(image) draw.text((0, 0), text, font=font) @@ -29,7 +31,7 @@ def main() -> None: """Show hello world until the window is closed.""" # Open an SDL window and renderer. window = tcod.sdl.video.new_window(720, 480, flags=tcod.sdl.video.WindowFlags.RESIZABLE) - renderer = tcod.sdl.render.new_renderer(window, target_textures=True) + renderer = tcod.sdl.render.new_renderer(window) # Render the text once, then reuse the texture. hello_world = render_text(renderer, "Hello World") hello_world.color_mod = (64, 255, 64) # Set the color when copied. @@ -41,7 +43,7 @@ def main() -> None: renderer.present() for event in tcod.event.get(): if isinstance(event, tcod.event.Quit): - raise SystemExit() + raise SystemExit if __name__ == "__main__": diff --git a/examples/termbox/README.md b/examples/termbox/README.md deleted file mode 100644 index 3ec77f56..00000000 --- a/examples/termbox/README.md +++ /dev/null @@ -1,59 +0,0 @@ -API of `termbox` Python module implemented in `tld`. - -The code here are modified files from -[termbox repository](https://github.com/nsf/termbox/), so please consult -it for the license and other info. - - -The code consists of two part - `termbox.py` module with API, translation -of official binding form the description below into `tld`: - -https://github.com/nsf/termbox/blob/b20c0a11/src/python/termboxmodule.pyx - -And the example `termboxtest.py` which is copied verbatim from: - -https://github.com/nsf/termbox/blob/b20c0a11/test_termboxmodule.py - - -### API Mapping Notes - -Notes taken while mapping the Termbox class: - - tb_init() // initialization console = tdl.init(132, 60) - tb_shutdown() // shutdown - - tb_width() // width of the terminal screen console.width - tb_height() // height of the terminal screen console.height - - tb_clear() // clear buffer console.clear() - tb_present() // sync internal buffer with terminal tdl.flush() - - tb_put_cell() - tb_change_cell() console.draw_char(x, y, ch, fg, bg) - tb_blit() // drawing functions - - tb_select_input_mode() // change input mode - tb_peek_event() // peek a keyboard event - tb_poll_event() // wait for a keyboard event * tdl.event.get() - - - * - means the translation is not direct - - - - init... - tdl doesn't allow to resize window (or rather libtcod) - tb works in existing terminal window and queries it rather than making own - - colors... - tdl uses RGB values - tb uses it own constants - - event... - tb returns event one by one - tdl return an event iterator - - - tb Event tdl Event - .type .type - EVENT_KEY KEYDOWN diff --git a/examples/termbox/termbox.py b/examples/termbox/termbox.py deleted file mode 100755 index 26ab2d7a..00000000 --- a/examples/termbox/termbox.py +++ /dev/null @@ -1,294 +0,0 @@ -"""Implementation of Termbox Python API in tdl. - -See README.md for details. -""" - -import tdl - -""" -Implementation status: - [ ] tdl.init() needs a window, made 132x60 - [ ] Termbox.close() is not implemented, does nothing - [ ] poll_event needs review, because it does not - completely follows the original logic - [ ] peek is stubbed, but not implemented - [ ] not all keys/events are mapped -""" - - -class TermboxException(Exception): - def __init__(self, msg) -> None: - self.msg = msg - - def __str__(self) -> str: - return self.msg - - -_instance = None - -# keys ---------------------------------- -KEY_F1 = 0xFFFF - 0 -KEY_F2 = 0xFFFF - 1 -KEY_F3 = 0xFFFF - 2 -KEY_F4 = 0xFFFF - 3 -KEY_F5 = 0xFFFF - 4 -KEY_F6 = 0xFFFF - 5 -KEY_F7 = 0xFFFF - 6 -KEY_F8 = 0xFFFF - 7 -KEY_F9 = 0xFFFF - 8 -KEY_F10 = 0xFFFF - 9 -KEY_F11 = 0xFFFF - 10 -KEY_F12 = 0xFFFF - 11 -KEY_INSERT = 0xFFFF - 12 -KEY_DELETE = 0xFFFF - 13 - -KEY_PGUP = 0xFFFF - 16 -KEY_PGDN = 0xFFFF - 17 - -KEY_MOUSE_LEFT = 0xFFFF - 22 -KEY_MOUSE_RIGHT = 0xFFFF - 23 -KEY_MOUSE_MIDDLE = 0xFFFF - 24 -KEY_MOUSE_RELEASE = 0xFFFF - 25 -KEY_MOUSE_WHEEL_UP = 0xFFFF - 26 -KEY_MOUSE_WHEEL_DOWN = 0xFFFF - 27 - -KEY_CTRL_TILDE = 0x00 -KEY_CTRL_2 = 0x00 -KEY_CTRL_A = 0x01 -KEY_CTRL_B = 0x02 -KEY_CTRL_C = 0x03 -KEY_CTRL_D = 0x04 -KEY_CTRL_E = 0x05 -KEY_CTRL_F = 0x06 -KEY_CTRL_G = 0x07 -KEY_BACKSPACE = 0x08 -KEY_CTRL_H = 0x08 -KEY_TAB = 0x09 -KEY_CTRL_I = 0x09 -KEY_CTRL_J = 0x0A -KEY_CTRL_K = 0x0B -KEY_CTRL_L = 0x0C -KEY_ENTER = 0x0D -KEY_CTRL_M = 0x0D -KEY_CTRL_N = 0x0E -KEY_CTRL_O = 0x0F -KEY_CTRL_P = 0x10 -KEY_CTRL_Q = 0x11 -KEY_CTRL_R = 0x12 -KEY_CTRL_S = 0x13 -KEY_CTRL_T = 0x14 -KEY_CTRL_U = 0x15 -KEY_CTRL_V = 0x16 -KEY_CTRL_W = 0x17 -KEY_CTRL_X = 0x18 -KEY_CTRL_Y = 0x19 -KEY_CTRL_Z = 0x1A - - -# -- mapped to tdl -KEY_HOME = "HOME" -KEY_END = "END" -KEY_ARROW_UP = "UP" -KEY_ARROW_DOWN = "DOWN" -KEY_ARROW_LEFT = "LEFT" -KEY_ARROW_RIGHT = "RIGHT" -KEY_ESC = "ESCAPE" -# /-- - - -KEY_CTRL_LSQ_BRACKET = 0x1B -KEY_CTRL_3 = 0x1B -KEY_CTRL_4 = 0x1C -KEY_CTRL_BACKSLASH = 0x1C -KEY_CTRL_5 = 0x1D -KEY_CTRL_RSQ_BRACKET = 0x1D -KEY_CTRL_6 = 0x1E -KEY_CTRL_7 = 0x1F -KEY_CTRL_SLASH = 0x1F -KEY_CTRL_UNDERSCORE = 0x1F -KEY_SPACE = 0x20 -KEY_BACKSPACE2 = 0x7F -KEY_CTRL_8 = 0x7F - -MOD_ALT = 0x01 - -# attributes ---------------------- - -# -- mapped to tdl -DEFAULT = Ellipsis - -BLACK = 0x000000 -RED = 0xFF0000 -GREEN = 0x00FF00 -YELLOW = 0xFFFF00 -BLUE = 0x0000FF -MAGENTA = 0xFF00FF -CYAN = 0x00FFFF -WHITE = 0xFFFFFF -# /-- - -BOLD = 0x10 -UNDERLINE = 0x20 -REVERSE = 0x40 - -# misc ---------------------------- - -HIDE_CURSOR = -1 -INPUT_CURRENT = 0 -INPUT_ESC = 1 -INPUT_ALT = 2 -OUTPUT_CURRENT = 0 -OUTPUT_NORMAL = 1 -OUTPUT_256 = 2 -OUTPUT_216 = 3 -OUTPUT_GRAYSCALE = 4 - - -# -- mapped to tdl -EVENT_KEY = "KEYDOWN" -# /-- -EVENT_RESIZE = 2 -EVENT_MOUSE = 3 - - -class Event: - """Aggregate for Termbox Event structure.""" - - type = None - ch = None - key = None - mod = None - width = None - height = None - mousex = None - mousey = None - - def gettuple(self): - return (self.type, self.ch, self.key, self.mod, self.width, self.height, self.mousex, self.mousey) - - -class Termbox: - def __init__(self, width=132, height=60) -> None: - global _instance - if _instance: - msg = "It is possible to create only one instance of Termbox" - raise TermboxException(msg) - - try: - self.console = tdl.init(width, height) - except tdl.TDLException as e: - raise TermboxException(e) - - self.e = Event() # cache for event data - - _instance = self - - def __del__(self) -> None: - self.close() - - def __exit__(self, *args): # t, value, traceback): - self.close() - - def __enter__(self): - return self - - def close(self): - global _instance - # tb_shutdown() - _instance = None - # TBD, does nothing - - def present(self): - """Sync state of the internal cell buffer with the terminal.""" - tdl.flush() - - def change_cell(self, x, y, ch, fg, bg): - """Change cell in position (x;y).""" - self.console.draw_char(x, y, ch, fg, bg) - - def width(self): - """Returns width of the terminal screen.""" - return self.console.width - - def height(self): - """Return height of the terminal screen.""" - return self.console.height - - def clear(self): - """Clear the internal cell buffer.""" - self.console.clear() - - def set_cursor(self, x, y): - """Set cursor position to (x;y). - - Set both arguments to HIDE_CURSOR or use 'hide_cursor' function to - hide it. - """ - tb_set_cursor(x, y) - - def hide_cursor(self): - """Hide cursor.""" - tb_set_cursor(-1, -1) - - def select_input_mode(self, mode): - """Select preferred input mode: INPUT_ESC or INPUT_ALT. - - INPUT_CURRENT returns the selected mode without changing anything. - """ - return int(tb_select_input_mode(mode)) - - def select_output_mode(self, mode): - """Select preferred output mode: one of OUTPUT_* constants. - - OUTPUT_CURRENT returns the selected mode without changing anything. - """ - return int(tb_select_output_mode(mode)) - - def peek_event(self, timeout=0): - """Wait for an event up to 'timeout' milliseconds and return it. - - Returns None if there was no event and timeout is expired. - Returns a tuple otherwise: (type, unicode character, key, mod, - width, height, mousex, mousey). - """ - """ - cdef tb_event e - with self._poll_lock: - with nogil: - result = tb_peek_event(&e, timeout) - assert(result >= 0) - if result == 0: - return None - if e.ch: - uch = unichr(e.ch) - else: - uch = None - """ - pass # return (e.type, uch, e.key, e.mod, e.w, e.h, e.x, e.y) - - def poll_event(self): - """Wait for an event and return it. - - Returns a tuple: (type, unicode character, key, mod, width, height, - mousex, mousey). - """ - """ - cdef tb_event e - with self._poll_lock: - with nogil: - result = tb_poll_event(&e) - assert(result >= 0) - if e.ch: - uch = unichr(e.ch) - else: - uch = None - """ - for e in tdl.event.get(): - # [ ] not all events are passed thru - self.e.type = e.type - if e.type == "KEYDOWN": - self.e.key = e.key - return self.e.gettuple() - return None - - # return (e.type, uch, e.key, e.mod, e.w, e.h, e.x, e.y) diff --git a/examples/termbox/termboxtest.py b/examples/termbox/termboxtest.py deleted file mode 100755 index c0d57065..00000000 --- a/examples/termbox/termboxtest.py +++ /dev/null @@ -1,135 +0,0 @@ -#!/usr/bin/python - -import termbox - -spaceord = ord(" ") - - -def print_line(t, msg, y, fg, bg): - w = t.width() - l = len(msg) - x = 0 - for i in range(w): - c = spaceord - if i < l: - c = ord(msg[i]) - t.change_cell(x + i, y, c, fg, bg) - - -class SelectBox: - def __init__(self, tb, choices, active=-1) -> None: - self.tb = tb - self.active = active - self.choices = choices - self.color_active = (termbox.BLACK, termbox.CYAN) - self.color_normal = (termbox.WHITE, termbox.BLACK) - - def draw(self): - for i, c in enumerate(self.choices): - color = self.color_normal - if i == self.active: - color = self.color_active - print_line(self.tb, c, i, *color) - - def validate_active(self): - if self.active < 0: - self.active = 0 - if self.active >= len(self.choices): - self.active = len(self.choices) - 1 - - def set_active(self, i): - self.active = i - self.validate_active() - - def move_up(self): - self.active -= 1 - self.validate_active() - - def move_down(self): - self.active += 1 - self.validate_active() - - -choices = [ - "This instructs Psyco", - "to compile and run as", - "much of your application", - "code as possible. This is the", - "simplest interface to Psyco.", - "In good cases you can just add", - "these two lines and enjoy the speed-up.", - "If your application does a lot", - "of initialization stuff before", - "the real work begins, you can put", - "the above two lines after this", - "initialization - e.g. after importing", - "modules, creating constant global objects, etc.", - "This instructs Psyco", - "to compile and run as", - "much of your application", - "code as possible. This is the", - "simplest interface to Psyco.", - "In good cases you can just add", - "these two lines and enjoy the speed-up.", - "If your application does a lot", - "of initialization stuff before", - "the real work begins, you can put", - "the above two lines after this", - "initialization - e.g. after importing", - "modules, creating constant global objects, etc.", -] - - -def draw_bottom_line(t, i): - i = i % 8 - w = t.width() - h = t.height() - c = i - palette = [ - termbox.DEFAULT, - termbox.BLACK, - termbox.RED, - termbox.GREEN, - termbox.YELLOW, - termbox.BLUE, - termbox.MAGENTA, - termbox.CYAN, - termbox.WHITE, - ] - for x in range(w): - t.change_cell(x, h - 1, ord(" "), termbox.BLACK, palette[c]) - t.change_cell(x, h - 2, ord(" "), termbox.BLACK, palette[c]) - c += 1 - if c > 7: - c = 0 - - -with termbox.Termbox() as t: - sb = SelectBox(t, choices, 0) - t.clear() - sb.draw() - t.present() - i = 0 - run_app = True - while run_app: - event_here = t.poll_event() - while event_here: - (type, ch, key, mod, w, h, x, y) = event_here - if type == termbox.EVENT_KEY and key == termbox.KEY_ESC: - run_app = False - if type == termbox.EVENT_KEY: - if key == termbox.KEY_ARROW_DOWN: - sb.move_down() - elif key == termbox.KEY_ARROW_UP: - sb.move_up() - elif key == termbox.KEY_HOME: - sb.set_active(-1) - elif key == termbox.KEY_END: - sb.set_active(999) - event_here = t.peek_event() - - t.clear() - sb.draw() - draw_bottom_line(t, i) - t.present() - i += 1 diff --git a/examples/thread_jobs.py b/examples/thread_jobs.py index d6fa54fd..9f6cf6c1 100755 --- a/examples/thread_jobs.py +++ b/examples/thread_jobs.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python3 +#!/usr/bin/env python # To the extent possible under law, the libtcod maintainers have waived all # copyright and related or neighboring rights for this example. This work is # published from: United States. @@ -13,12 +13,13 @@ Typically the field-of-view tasks run good but not great, and the path-finding tasks run poorly. """ + import concurrent.futures import multiprocessing import platform import sys import timeit -from typing import Callable, List, Tuple +from collections.abc import Callable import tcod.map @@ -36,35 +37,35 @@ def test_fov(map_: tcod.map.Map) -> tcod.map.Map: return map_ -def test_fov_single(maps: List[tcod.map.Map]) -> None: +def test_fov_single(maps: list[tcod.map.Map]) -> None: for map_ in maps: test_fov(map_) -def test_fov_threads(executor: concurrent.futures.Executor, maps: List[tcod.map.Map]) -> None: +def test_fov_threads(executor: concurrent.futures.Executor, maps: list[tcod.map.Map]) -> None: for _result in executor.map(test_fov, maps): pass -def test_astar(map_: tcod.map.Map) -> List[Tuple[int, int]]: +def test_astar(map_: tcod.map.Map) -> list[tuple[int, int]]: astar = tcod.path.AStar(map_) return astar.get_path(0, 0, MAP_WIDTH - 1, MAP_HEIGHT - 1) -def test_astar_single(maps: List[tcod.map.Map]) -> None: +def test_astar_single(maps: list[tcod.map.Map]) -> None: for map_ in maps: test_astar(map_) -def test_astar_threads(executor: concurrent.futures.Executor, maps: List[tcod.map.Map]) -> None: +def test_astar_threads(executor: concurrent.futures.Executor, maps: list[tcod.map.Map]) -> None: for _result in executor.map(test_astar, maps): pass def run_test( - maps: List[tcod.map.Map], - single_func: Callable[[List[tcod.map.Map]], None], - multi_func: Callable[[concurrent.futures.Executor, List[tcod.map.Map]], None], + maps: list[tcod.map.Map], + single_func: Callable[[list[tcod.map.Map]], None], + multi_func: Callable[[concurrent.futures.Executor, list[tcod.map.Map]], None], ) -> None: """Run a function designed for a single thread and compare it to a threaded version. @@ -76,7 +77,7 @@ def run_test( for i in range(1, THREADS + 1): executor = concurrent.futures.ThreadPoolExecutor(i) multi_time = min(timeit.repeat(lambda: multi_func(executor, maps), number=1, repeat=REPEAT)) - print(f"{i} threads: {multi_time * 1000:.2f}ms, " f"{single_time / (multi_time * i) * 100:.2f}% efficiency") + print(f"{i} threads: {multi_time * 1000:.2f}ms, {single_time / (multi_time * i) * 100:.2f}% efficiency") def main() -> None: @@ -88,13 +89,10 @@ def main() -> None: print(f"Python {sys.version}\n{platform.platform()}\n{platform.processor()}") - print(f"\nComputing field-of-view for " f"{len(maps)} empty {MAP_WIDTH}x{MAP_HEIGHT} maps.") + print(f"\nComputing field-of-view for {len(maps)} empty {MAP_WIDTH}x{MAP_HEIGHT} maps.") run_test(maps, test_fov_single, test_fov_threads) - print( - f"\nComputing AStar from corner to corner {len(maps)} times " - f"on separate empty {MAP_WIDTH}x{MAP_HEIGHT} maps." - ) + print(f"\nComputing AStar from corner to corner {len(maps)} times on separate empty {MAP_WIDTH}x{MAP_HEIGHT} maps.") run_test(maps, test_astar_single, test_astar_threads) diff --git a/examples/ttf.py b/examples/ttf.py index da412b4b..4a6041a8 100755 --- a/examples/ttf.py +++ b/examples/ttf.py @@ -1,28 +1,31 @@ -#!/usr/bin/env python3 +#!/usr/bin/env python """A TrueType Font example using the FreeType library. You will need to get this external library from PyPI: pip install freetype-py """ + # To the extent possible under law, the libtcod maintainers have waived all # copyright and related or neighboring rights to this example script. # https://creativecommons.org/publicdomain/zero/1.0/ -from typing import Tuple +from typing import TYPE_CHECKING import freetype # type: ignore # pip install freetype-py import numpy as np -from numpy.typing import NDArray import tcod.console import tcod.context import tcod.event import tcod.tileset +if TYPE_CHECKING: + from numpy.typing import NDArray + FONT = "VeraMono.ttf" -def load_ttf(path: str, size: Tuple[int, int]) -> tcod.tileset.Tileset: +def load_ttf(path: str, size: tuple[int, int]) -> tcod.tileset.Tileset: """Load a TTF file and return a tcod Tileset. `path` is the file path to the font, this can be any font supported by the @@ -80,8 +83,8 @@ def main() -> None: context.present(console, integer_scaling=True) for event in tcod.event.wait(): if isinstance(event, tcod.event.Quit): - raise SystemExit() - if isinstance(event, tcod.event.WindowResized) and event.type == "WindowSizeChanged": + raise SystemExit + if isinstance(event, tcod.event.WindowResized): # Resize the Tileset to match the new screen size. context.change_tileset( load_ttf( diff --git a/libtcod b/libtcod index 747b2e9d..27c2dbc9 160000 --- a/libtcod +++ b/libtcod @@ -1 +1 @@ -Subproject commit 747b2e9db06fca0f1281ba0ca6de173fbcb32eec +Subproject commit 27c2dbc9d97bacb18b9fd43a5c7f070dc34339ed diff --git a/libtcodpy.py b/libtcodpy.py index 7947f394..76317e99 100644 --- a/libtcodpy.py +++ b/libtcodpy.py @@ -1,4 +1,5 @@ """Deprecated module alias for tcod.libtcodpy, use 'import tcod as libtcodpy' instead.""" + import warnings from tcod.libtcodpy import * # noqa: F403 diff --git a/pyproject.toml b/pyproject.toml index 325c0baa..64438ee6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,14 +1,16 @@ [build-system] requires = [ - # Newer versions of setuptools break editable installs + # setuptools >=64.0.0 might break editable installs # https://github.com/pypa/setuptools/issues/3548 - "setuptools >=61.0.0, <64.0.0", + "setuptools >=77.0.3", "setuptools_scm[toml]>=6.2", + "packaging>=24.2", "wheel>=0.37.1", "cffi>=1.15", "pycparser>=2.14", "pcpp==1.30", "requests>=2.28.1", + "attrs", ] build-backend = "setuptools.build_meta" @@ -18,37 +20,42 @@ dynamic = ["version"] description = "The official Python port of libtcod." authors = [{ name = "Kyle Benesch", email = "4b796c65+tcod@gmail.com" }] readme = "README.rst" -requires-python = ">=3.8" -license = { text = "Simplified BSD License" } +requires-python = ">=3.10" +license = "BSD-2-Clause" +license-files = [ + "LICENSE.txt", + "libtcod/LICENSE.txt", + "libtcod/LIBTCOD-CREDITS.txt", +] dependencies = [ + "attrs>=25.2.0", "cffi>=1.15", 'numpy>=1.21.4; implementation_name != "pypy"', - "typing_extensions", + "typing_extensions>=4.12.2", ] keywords = [ "roguelike", - "cffi", + "roguelikedev", + "gamedev", "Unicode", "libtcod", + "libtcodpy", "field-of-view", "pathfinding", ] -classifiers = [ +classifiers = [ # https://pypi.org/classifiers/ "Development Status :: 5 - Production/Stable", "Environment :: Win32 (MS Windows)", "Environment :: MacOS X", "Environment :: X11 Applications", "Intended Audience :: Developers", - "License :: OSI Approved :: BSD License", "Natural Language :: English", "Operating System :: POSIX", "Operating System :: MacOS :: MacOS X", "Operating System :: Microsoft :: Windows", + "Programming Language :: C", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.8", - "Programming Language :: Python :: 3.9", - "Programming Language :: Python :: 3.10", - "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: Free Threading :: 2 - Beta", "Programming Language :: Python :: Implementation :: CPython", "Programming Language :: Python :: Implementation :: PyPy", "Topic :: Games/Entertainment", @@ -68,22 +75,9 @@ Source = "https://github.com/libtcod/python-tcod" Tracker = "https://github.com/libtcod/python-tcod/issues" Forum = "https://github.com/libtcod/python-tcod/discussions" -[tool.distutils.bdist_wheel] -py-limited-api = "cp38" - [tool.setuptools_scm] write_to = "tcod/version.py" -[tool.black] -line-length = 120 -target-version = ["py38"] - -[tool.isort] -profile = "black" -py_version = "38" -skip_gitignore = true -line_length = 120 - [tool.pytest.ini_options] minversion = "6.0" required_plugins = ["pytest-cov", "pytest-benchmark"] @@ -98,15 +92,21 @@ addopts = [ log_file_level = "DEBUG" faulthandler_timeout = 5 filterwarnings = [ - "ignore::DeprecationWarning:tcod.libtcodpy", - "ignore::PendingDeprecationWarning:tcod.libtcodpy", + "ignore:This function may be deprecated in the future:PendingDeprecationWarning", "ignore:This class may perform poorly and is no longer needed.::tcod.map", "ignore:'import tcod as libtcodpy' is preferred.", ] +[tool.coverage.report] # https://coverage.readthedocs.io/en/latest/config.html +exclude_lines = ['^\s*\.\.\.', "if TYPE_CHECKING:", "# pragma: no cover"] +omit = ["tcod/__pyinstaller/*"] + +[tool.cibuildwheel] # https://cibuildwheel.pypa.io/en/stable/options/ +enable = ["pypy", "pyodide-prerelease"] + [tool.mypy] files = ["."] -python_version = 3.9 +python_version = "3.12" warn_unused_configs = true show_error_codes = true disallow_subclassing_any = true @@ -122,6 +122,8 @@ warn_unused_ignores = true warn_return_any = true implicit_reexport = false strict_equality = true +strict_bytes = true +extra_checks = true exclude = [ "build/", "venv/", @@ -145,51 +147,28 @@ module = "tcod._libtcod" ignore_missing_imports = true [tool.ruff] -# https://beta.ruff.rs/docs/rules/ -select = [ - "C90", # mccabe - "E", # pycodestyle - "W", # pycodestyle - "F", # Pyflakes - "I", # isort - "UP", # pyupgrade - "YTT", # flake8-2020 - "ANN", # flake8-annotations - "S", # flake8-bandit - "B", # flake8-bugbear - "C4", # flake8-comprehensions - "DTZ", # flake8-datetimez - "EM", # flake8-errmsg - "EXE", # flake8-executable - "RET", # flake8-return - "ICN", # flake8-import-conventions - "PIE", # flake8-pie - "PT", # flake8-pytest-style - "SIM", # flake8-simplify - "PTH", # flake8-use-pathlib - "PL", # Pylint - "TRY", # tryceratops - "RUF", # NumPy-specific rules - "G", # flake8-logging-format - "D", # pydocstyle -] +extend-exclude = ["libtcod"] # Ignore submodule +line-length = 120 + +[tool.ruff.lint] # https://docs.astral.sh/ruff/rules/ +select = ["ALL"] ignore = [ + "COM", # flake8-commas "E501", # line-too-long + "PYI064", # redundant-final-literal "S101", # assert "S301", # suspicious-pickle-usage "S311", # suspicious-non-cryptographic-random-usage - "ANN101", # missing-type-self - "ANN102", # missing-type-cls - "D203", # one-blank-line-before-class - "D204", # one-blank-line-after-class - "D213", # multi-line-summary-second-line - "D407", # dashed-underline-after-section - "D408", # section-underline-after-name - "D409", # section-underline-matches-section-length + "SLF001", # private-member-access +] +[tool.ruff.lint.per-file-ignores] +"**/{tests}/*" = [ + "D103", # undocumented-public-function +] +"**/{tests,docs,examples,scripts}/*" = [ + "D103", # undocumented-public-function + "T201", # print ] -extend-exclude = ["libtcod"] # Ignore submodule -line-length = 120 -[tool.ruff.pydocstyle] -# Use Google-style docstrings. +[tool.ruff.lint.pydocstyle] # https://docs.astral.sh/ruff/settings/#lintpydocstyle convention = "google" diff --git a/requirements.txt b/requirements.txt index ee1ea8a0..06667de0 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,10 +1,12 @@ +attrs>=23.1.0 cffi>=1.15 numpy>=1.21.4 pycparser>=2.14 requests>=2.28.1 -setuptools==65.5.1 +setuptools>=80.8.0 types-cffi types-requests +types-Pillow types-setuptools types-tabulate typing_extensions diff --git a/scripts/generate_charmap_table.py b/scripts/generate_charmap_table.py index 0591cb9b..bc7efb19 100755 --- a/scripts/generate_charmap_table.py +++ b/scripts/generate_charmap_table.py @@ -1,21 +1,22 @@ -#!/usr/bin/env python3 +#!/usr/bin/env python """This script is used to generate the tables for `charmap-reference.rst`. Uses the tabulate module from PyPI. """ + from __future__ import annotations import argparse import unicodedata -from typing import Iterable, Iterator +from collections.abc import Iterable, Iterator # noqa: TC003 from tabulate import tabulate import tcod.tileset -def get_charmaps() -> Iterator[str]: - """Return an iterator of the current character maps from tcod.tilest.""" +def get_character_maps() -> Iterator[str]: + """Return an iterator of the current character maps from tcod.tileset.""" for name in dir(tcod.tileset): if name.startswith("CHARMAP_"): yield name[len("CHARMAP_") :].lower() @@ -60,7 +61,7 @@ def main() -> None: parser.add_argument( "charmap", action="store", - choices=list(get_charmaps()), + choices=list(get_character_maps()), type=str, help="which character map to generate a table from", ) diff --git a/scripts/get_release_description.py b/scripts/get_release_description.py index 7d47d9f9..889feced 100755 --- a/scripts/get_release_description.py +++ b/scripts/get_release_description.py @@ -1,5 +1,6 @@ -#!/usr/bin/env python3 +#!/usr/bin/env python """Print the description used for GitHub Releases.""" + from __future__ import annotations import re diff --git a/scripts/tag_release.py b/scripts/tag_release.py index 70b77fb7..5f39e4b2 100755 --- a/scripts/tag_release.py +++ b/scripts/tag_release.py @@ -1,5 +1,6 @@ -#!/usr/bin/env python3 +#!/usr/bin/env python """Automate tagged releases of this project.""" + from __future__ import annotations import argparse @@ -34,19 +35,17 @@ def parse_changelog(args: argparse.Namespace) -> tuple[str, str]: ) assert match header, changes, tail = match.groups() - tagged = "\n## [{}] - {}\n{}".format( - args.tag, - datetime.date.today().isoformat(), # Local timezone is fine, probably. # noqa: DTZ011 - changes, - ) + + iso_date = datetime.datetime.now(tz=datetime.timezone.utc).date().isoformat() + tagged = f"\n## [{args.tag}] - {iso_date}\n{changes}" if args.verbose: print("--- Tagged section:") print(tagged) - return "".join((header, tagged, tail)), changes + return f"{header}{tagged}{tail}", changes -def replace_unreleased_tags(tag: str, dry_run: bool) -> None: +def replace_unreleased_tags(tag: str, *, dry_run: bool) -> None: """Walk though sources and replace pending tags with the new tag.""" match = re.match(r"\d+\.\d+", tag) assert match @@ -78,7 +77,7 @@ def main() -> None: print("--- New changelog:") print(new_changelog) - replace_unreleased_tags(args.tag, args.dry_run) + replace_unreleased_tags(args.tag, dry_run=args.dry_run) if not args.dry_run: (PROJECT_DIR / "CHANGELOG.md").write_text(new_changelog, encoding="utf-8") diff --git a/setup.py b/setup.py index dc2139e7..3ff98114 100755 --- a/setup.py +++ b/setup.py @@ -1,64 +1,52 @@ -#!/usr/bin/env python3 +#!/usr/bin/env python """Python-tcod setup script.""" + from __future__ import annotations -import platform -import subprocess import sys from pathlib import Path from setuptools import setup -SDL_VERSION_NEEDED = (2, 0, 5) +# ruff: noqa: T201 + +SDL_VERSION_NEEDED = (3, 2, 0) SETUP_DIR = Path(__file__).parent # setup.py current directory def get_package_data() -> list[str]: """Get data files which will be included in the main tcod/ directory.""" - BIT_SIZE, _ = platform.architecture() files = [ "py.typed", "lib/LIBTCOD-CREDITS.txt", "lib/LIBTCOD-LICENSE.txt", "lib/README-SDL.txt", ] - if "win32" in sys.platform: - if BIT_SIZE == "32bit": - files += ["x86/SDL2.dll"] + if sys.platform == "win32": + if "ARM64" in sys.version: + files += ["arm64/SDL3.dll"] + elif "AMD64" in sys.version: + files += ["x64/SDL3.dll"] else: - files += ["x64/SDL2.dll"] + files += ["x86/SDL3.dll"] if sys.platform == "darwin": - files += ["SDL2.framework/Versions/A/SDL2"] + files += ["SDL3.framework/Versions/A/SDL3"] return files -def check_sdl_version() -> None: - """Check the local SDL version on Linux distributions.""" - if not sys.platform.startswith("linux"): - return - needed_version = "{}.{}.{}".format(*SDL_VERSION_NEEDED) - try: - sdl_version_str = subprocess.check_output(["sdl2-config", "--version"], universal_newlines=True).strip() - except FileNotFoundError as exc: - msg = ( - f"libsdl2-dev or equivalent must be installed on your system and must be at least version {needed_version}." - "\nsdl2-config must be on PATH." - ) - raise RuntimeError(msg) from exc - print(f"Found SDL {sdl_version_str}.") - sdl_version = tuple(int(s) for s in sdl_version_str.split(".")) - if sdl_version < SDL_VERSION_NEEDED: - msg = f"SDL version must be at least {needed_version}, (found {sdl_version_str})" - raise RuntimeError(msg) - - if not (SETUP_DIR / "libtcod/src").exists(): print("Libtcod submodule is uninitialized.") print("Did you forget to run 'git submodule update --init'?") sys.exit(1) -check_sdl_version() +options = { + "bdist_wheel": { + "py_limited_api": "cp310", + } +} +if "free-threading build" in sys.version: + del options["bdist_wheel"]["py_limited_api"] setup( py_modules=["libtcodpy"], @@ -66,4 +54,5 @@ def check_sdl_version() -> None: package_data={"tcod": get_package_data()}, cffi_modules=["build_libtcod.py:ffi"], platforms=["Windows", "MacOS", "Linux"], + options=options, ) diff --git a/tcod/__init__.py b/tcod/__init__.py index b8ea0d25..2db18f54 100644 --- a/tcod/__init__.py +++ b/tcod/__init__.py @@ -6,34 +6,35 @@ Read the documentation online: https://python-tcod.readthedocs.io/en/latest/ """ + from __future__ import annotations from pkgutil import extend_path __path__ = extend_path(__path__, __name__) -from tcod import bsp, color, console, context, event, image, los, map, noise, path, random, tileset +from tcod import bsp, color, console, context, event, image, los, map, noise, path, random, tileset # noqa: A004 from tcod.cffi import __sdl_version__, ffi, lib from tcod.tcod import __getattr__ # noqa: F401 from tcod.version import __version__ __all__ = [ - "__version__", + "Console", "__sdl_version__", - "lib", - "ffi", + "__version__", "bsp", "color", "console", "context", "event", - "tileset", + "ffi", "image", + "lib", "los", "map", "noise", "path", "random", "tileset", - "Console", + "tileset", ] diff --git a/tcod/__pyinstaller/__init__.py b/tcod/__pyinstaller/__init__.py index 7224bb10..2b67c1c0 100644 --- a/tcod/__pyinstaller/__init__.py +++ b/tcod/__pyinstaller/__init__.py @@ -1,4 +1,5 @@ """PyInstaller entry point for tcod.""" + from __future__ import annotations from pathlib import Path diff --git a/tcod/__pyinstaller/hook-tcod.py b/tcod/__pyinstaller/hook-tcod.py index 9790d583..b0cabf38 100644 --- a/tcod/__pyinstaller/hook-tcod.py +++ b/tcod/__pyinstaller/hook-tcod.py @@ -5,6 +5,7 @@ If this hook is ever modified then the contributed hook needs to be removed from: https://github.com/pyinstaller/pyinstaller-hooks-contrib """ + from PyInstaller.utils.hooks import collect_dynamic_libs # type: ignore hiddenimports = ["_cffi_backend"] diff --git a/tcod/_internal.py b/tcod/_internal.py index cd92a45c..b67242b8 100644 --- a/tcod/_internal.py +++ b/tcod/_internal.py @@ -1,52 +1,48 @@ """Internal helper functions used by the rest of the library.""" + from __future__ import annotations -import functools +import locale +import sys import warnings -from types import TracebackType -from typing import Any, AnyStr, Callable, NoReturn, SupportsInt, TypeVar, cast +from collections.abc import Callable +from typing import TYPE_CHECKING, Any, AnyStr, Literal, NoReturn, SupportsInt, TypeVar -import numpy as np -from numpy.typing import ArrayLike, NDArray -from typing_extensions import Literal +from typing_extensions import deprecated from tcod.cffi import ffi, lib +if TYPE_CHECKING: + from pathlib import Path + from types import TracebackType + + FuncType = Callable[..., Any] F = TypeVar("F", bound=FuncType) T = TypeVar("T") -def deprecate(message: str, category: type[Warning] = DeprecationWarning, stacklevel: int = 0) -> Callable[[F], F]: - """Return a decorator which adds a warning to functions.""" +def _deprecate_passthrough( + message: str, # noqa: ARG001 + /, + *, + category: type[Warning] = DeprecationWarning, # noqa: ARG001 + stacklevel: int = 0, # noqa: ARG001 +) -> Callable[[F], F]: + """Return a decorator which skips wrapping a warning onto functions. This is used for non-debug runs.""" def decorator(func: F) -> F: - if not __debug__: - return func - - @functools.wraps(func) - def wrapper(*args: Any, **kwargs: Any) -> Any: # noqa: ANN401 - warnings.warn(message, category, stacklevel=stacklevel + 2) - return func(*args, **kwargs) - - return cast(F, wrapper) + return func return decorator -def pending_deprecate( - message: str = "This function may be deprecated in the future." - " Consider raising an issue on GitHub if you need this feature.", - category: type[Warning] = PendingDeprecationWarning, - stacklevel: int = 0, -) -> Callable[[F], F]: - """Like deprecate, but the default parameters are filled out for a generic pending deprecation warning.""" - return deprecate(message, category, stacklevel) +deprecate = deprecated if __debug__ or TYPE_CHECKING else _deprecate_passthrough def verify_order(order: Literal["C", "F"]) -> Literal["C", "F"]: """Verify and return a Numpy order string.""" - order = order.upper() # type: ignore + order = order.upper() # type: ignore[assignment] if order not in ("C", "F"): msg = f"order must be 'C' or 'F', not {order!r}" raise TypeError(msg) @@ -86,7 +82,7 @@ def _check_warn(error: int, stacklevel: int = 2) -> int: def _unpack_char_p(char_p: Any) -> str: # noqa: ANN401 if char_p == ffi.NULL: return "" - return ffi.string(char_p).decode() # type: ignore + return str(ffi.string(char_p), encoding="utf-8") def _int(int_or_str: SupportsInt | str | bytes) -> int: @@ -112,7 +108,7 @@ def _unicode(string: AnyStr, stacklevel: int = 2) -> str: stacklevel=stacklevel + 1, ) return string.decode("latin-1") - return string + return str(string) def _fmt(string: str, stacklevel: int = 2) -> bytes: @@ -126,6 +122,18 @@ def _fmt(string: str, stacklevel: int = 2) -> bytes: return string.encode("utf-8").replace(b"%", b"%%") +def _path_encode(path: Path) -> bytes: + """Return a bytes file path for the current locale when on Windows, uses fsdecode for other platforms.""" + if sys.platform != "win32": + return bytes(path) # Sane and expected behavior for converting Path into bytes + try: + return str(path).encode(locale.getlocale()[1] or "utf-8") # Stay classy, Windows + except UnicodeEncodeError as exc: + if sys.version_info >= (3, 11): + exc.add_note("""Consider calling 'locale.setlocale(locale.LC_CTYPES, ".UTF8")' to support Unicode paths.""") + raise + + class _PropagateException: """Context manager designed to propagate exceptions outside of a cffi callback context. @@ -154,7 +162,7 @@ def __enter__(self) -> Callable[[Any], None]: return self.propagate def __exit__( - self, type: type[BaseException] | None, value: BaseException | None, traceback: TracebackType | None + self, _type: type[BaseException] | None, value: BaseException | None, traceback: TracebackType | None ) -> None: """If we're holding on to an exception, raise it now. @@ -183,18 +191,17 @@ def _get_cdata_from_args(*args: Any, **kwargs: Any) -> Any: # noqa: ANN401 def __hash__(self) -> int: return hash(self.cdata) - def __eq__(self, other: Any) -> Any: - try: - return self.cdata == other.cdata - except AttributeError: + def __eq__(self, other: object) -> bool: + if not isinstance(other, _CDataWrapper): return NotImplemented + return bool(self.cdata == other.cdata) - def __getattr__(self, attr: str) -> Any: + def __getattr__(self, attr: str) -> Any: # noqa: ANN401 if "cdata" in self.__dict__: return getattr(self.__dict__["cdata"], attr) raise AttributeError(attr) - def __setattr__(self, attr: str, value: Any) -> None: + def __setattr__(self, attr: str, value: Any) -> None: # noqa: ANN401 if hasattr(self, "cdata") and hasattr(self.cdata, attr): setattr(self.cdata, attr, value) else: @@ -216,42 +223,3 @@ def _console(console: Any) -> Any: # noqa: ANN401 stacklevel=3, ) return ffi.NULL - - -class TempImage: - """An Image-like container for NumPy arrays.""" - - def __init__(self, array: ArrayLike) -> None: - """Initialize an image from the given array. May copy or reference the array.""" - self._array: NDArray[np.uint8] = np.ascontiguousarray(array, dtype=np.uint8) - height, width, depth = self._array.shape - if depth != 3: - msg = f"Array must have RGB channels. Shape is: {self._array.shape!r}" - raise TypeError(msg) - self._buffer = ffi.from_buffer("TCOD_color_t[]", self._array) - self._mipmaps = ffi.new( - "struct TCOD_mipmap_*", - { - "width": width, - "height": height, - "fwidth": width, - "fheight": height, - "buf": self._buffer, - "dirty": True, - }, - ) - self.image_c = ffi.new( - "TCOD_Image*", - { - "nb_mipmaps": 1, - "mipmaps": self._mipmaps, - "has_key_color": False, - }, - ) - - -def _as_image(image: Any) -> TempImage: - """Convert this input into an Image-like object.""" - if hasattr(image, "image_c"): - return image # type: ignore - return TempImage(image) diff --git a/tcod/_libtcod.pyi b/tcod/_libtcod.pyi new file mode 100644 index 00000000..04fba29b --- /dev/null +++ b/tcod/_libtcod.pyi @@ -0,0 +1,9593 @@ +# Autogenerated with build_libtcod.py +from typing import Any, Final, Literal + +# pyi files for CFFI ports are not standard +# ruff: noqa: A002, ANN401, D402, D403, D415, N801, N802, N803, N815, PLW0211, PYI021 + +class _lib: + @staticmethod + def NoiseGetSample(noise: Any, xyzw: Any, /) -> float: + """float NoiseGetSample(TDLNoise *noise, float *xyzw)""" + + @staticmethod + def NoiseSampleMeshGrid(noise: Any, len: Any, in_: Any, out: Any, /) -> None: + """void NoiseSampleMeshGrid(TDLNoise *noise, const long len, const float *in, float *out)""" + + @staticmethod + def NoiseSampleOpenMeshGrid(noise: Any, ndim: int, shape: Any, ogrid_in: Any, out: Any, /) -> None: + """void NoiseSampleOpenMeshGrid(TDLNoise *noise, const int ndim, const long *shape, const float **ogrid_in, float *out)""" + + @staticmethod + def PathCostArrayFloat32(x1: int, y1: int, x2: int, y2: int, map: Any, /) -> float: + """float PathCostArrayFloat32(int x1, int y1, int x2, int y2, const struct PathCostArray *map)""" + + @staticmethod + def PathCostArrayInt16(x1: int, y1: int, x2: int, y2: int, map: Any, /) -> float: + """float PathCostArrayInt16(int x1, int y1, int x2, int y2, const struct PathCostArray *map)""" + + @staticmethod + def PathCostArrayInt32(x1: int, y1: int, x2: int, y2: int, map: Any, /) -> float: + """float PathCostArrayInt32(int x1, int y1, int x2, int y2, const struct PathCostArray *map)""" + + @staticmethod + def PathCostArrayInt8(x1: int, y1: int, x2: int, y2: int, map: Any, /) -> float: + """float PathCostArrayInt8(int x1, int y1, int x2, int y2, const struct PathCostArray *map)""" + + @staticmethod + def PathCostArrayUInt16(x1: int, y1: int, x2: int, y2: int, map: Any, /) -> float: + """float PathCostArrayUInt16(int x1, int y1, int x2, int y2, const struct PathCostArray *map)""" + + @staticmethod + def PathCostArrayUInt32(x1: int, y1: int, x2: int, y2: int, map: Any, /) -> float: + """float PathCostArrayUInt32(int x1, int y1, int x2, int y2, const struct PathCostArray *map)""" + + @staticmethod + def PathCostArrayUInt8(x1: int, y1: int, x2: int, y2: int, map: Any, /) -> float: + """float PathCostArrayUInt8(int x1, int y1, int x2, int y2, const struct PathCostArray *map)""" + + @staticmethod + def SDL_AcquireCameraFrame(camera: Any, timestampNS: Any, /) -> Any: + """SDL_Surface *SDL_AcquireCameraFrame(SDL_Camera *camera, Uint64 *timestampNS)""" + + @staticmethod + def SDL_AcquireGPUCommandBuffer(device: Any, /) -> Any: + """SDL_GPUCommandBuffer *SDL_AcquireGPUCommandBuffer(SDL_GPUDevice *device)""" + + @staticmethod + def SDL_AcquireGPUSwapchainTexture( + command_buffer: Any, + window: Any, + swapchain_texture: Any, + swapchain_texture_width: Any, + swapchain_texture_height: Any, + /, + ) -> bool: + """bool SDL_AcquireGPUSwapchainTexture(SDL_GPUCommandBuffer *command_buffer, SDL_Window *window, SDL_GPUTexture **swapchain_texture, Uint32 *swapchain_texture_width, Uint32 *swapchain_texture_height)""" + + @staticmethod + def SDL_AddAtomicInt(a: Any, v: int, /) -> int: + """int SDL_AddAtomicInt(SDL_AtomicInt *a, int v)""" + + @staticmethod + def SDL_AddEventWatch(filter: Any, userdata: Any, /) -> bool: + """bool SDL_AddEventWatch(SDL_EventFilter filter, void *userdata)""" + + @staticmethod + def SDL_AddGamepadMapping(mapping: Any, /) -> int: + """int SDL_AddGamepadMapping(const char *mapping)""" + + @staticmethod + def SDL_AddGamepadMappingsFromFile(file: Any, /) -> int: + """int SDL_AddGamepadMappingsFromFile(const char *file)""" + + @staticmethod + def SDL_AddGamepadMappingsFromIO(src: Any, closeio: bool, /) -> int: + """int SDL_AddGamepadMappingsFromIO(SDL_IOStream *src, bool closeio)""" + + @staticmethod + def SDL_AddHintCallback(name: Any, callback: Any, userdata: Any, /) -> bool: + """bool SDL_AddHintCallback(const char *name, SDL_HintCallback callback, void *userdata)""" + + @staticmethod + def SDL_AddSurfaceAlternateImage(surface: Any, image: Any, /) -> bool: + """bool SDL_AddSurfaceAlternateImage(SDL_Surface *surface, SDL_Surface *image)""" + + @staticmethod + def SDL_AddTimer(interval: Any, callback: Any, userdata: Any, /) -> Any: + """SDL_TimerID SDL_AddTimer(Uint32 interval, SDL_TimerCallback callback, void *userdata)""" + + @staticmethod + def SDL_AddTimerNS(interval: Any, callback: Any, userdata: Any, /) -> Any: + """SDL_TimerID SDL_AddTimerNS(Uint64 interval, SDL_NSTimerCallback callback, void *userdata)""" + + @staticmethod + def SDL_AddVulkanRenderSemaphores( + renderer: Any, wait_stage_mask: Any, wait_semaphore: Any, signal_semaphore: Any, / + ) -> bool: + """bool SDL_AddVulkanRenderSemaphores(SDL_Renderer *renderer, Uint32 wait_stage_mask, Sint64 wait_semaphore, Sint64 signal_semaphore)""" + + @staticmethod + def SDL_AsyncIOFromFile(file: Any, mode: Any, /) -> Any: + """SDL_AsyncIO *SDL_AsyncIOFromFile(const char *file, const char *mode)""" + + @staticmethod + def SDL_AttachVirtualJoystick(desc: Any, /) -> Any: + """SDL_JoystickID SDL_AttachVirtualJoystick(const SDL_VirtualJoystickDesc *desc)""" + + @staticmethod + def SDL_AudioDevicePaused(devid: Any, /) -> bool: + """bool SDL_AudioDevicePaused(SDL_AudioDeviceID devid)""" + + @staticmethod + def SDL_AudioStreamDevicePaused(stream: Any, /) -> bool: + """bool SDL_AudioStreamDevicePaused(SDL_AudioStream *stream)""" + + @staticmethod + def SDL_BeginGPUComputePass( + command_buffer: Any, + storage_texture_bindings: Any, + num_storage_texture_bindings: Any, + storage_buffer_bindings: Any, + num_storage_buffer_bindings: Any, + /, + ) -> Any: + """SDL_GPUComputePass *SDL_BeginGPUComputePass(SDL_GPUCommandBuffer *command_buffer, const SDL_GPUStorageTextureReadWriteBinding *storage_texture_bindings, Uint32 num_storage_texture_bindings, const SDL_GPUStorageBufferReadWriteBinding *storage_buffer_bindings, Uint32 num_storage_buffer_bindings)""" + + @staticmethod + def SDL_BeginGPUCopyPass(command_buffer: Any, /) -> Any: + """SDL_GPUCopyPass *SDL_BeginGPUCopyPass(SDL_GPUCommandBuffer *command_buffer)""" + + @staticmethod + def SDL_BeginGPURenderPass( + command_buffer: Any, color_target_infos: Any, num_color_targets: Any, depth_stencil_target_info: Any, / + ) -> Any: + """SDL_GPURenderPass *SDL_BeginGPURenderPass(SDL_GPUCommandBuffer *command_buffer, const SDL_GPUColorTargetInfo *color_target_infos, Uint32 num_color_targets, const SDL_GPUDepthStencilTargetInfo *depth_stencil_target_info)""" + + @staticmethod + def SDL_BindAudioStream(devid: Any, stream: Any, /) -> bool: + """bool SDL_BindAudioStream(SDL_AudioDeviceID devid, SDL_AudioStream *stream)""" + + @staticmethod + def SDL_BindAudioStreams(devid: Any, streams: Any, num_streams: int, /) -> bool: + """bool SDL_BindAudioStreams(SDL_AudioDeviceID devid, SDL_AudioStream * const *streams, int num_streams)""" + + @staticmethod + def SDL_BindGPUComputePipeline(compute_pass: Any, compute_pipeline: Any, /) -> None: + """void SDL_BindGPUComputePipeline(SDL_GPUComputePass *compute_pass, SDL_GPUComputePipeline *compute_pipeline)""" + + @staticmethod + def SDL_BindGPUComputeSamplers( + compute_pass: Any, first_slot: Any, texture_sampler_bindings: Any, num_bindings: Any, / + ) -> None: + """void SDL_BindGPUComputeSamplers(SDL_GPUComputePass *compute_pass, Uint32 first_slot, const SDL_GPUTextureSamplerBinding *texture_sampler_bindings, Uint32 num_bindings)""" + + @staticmethod + def SDL_BindGPUComputeStorageBuffers( + compute_pass: Any, first_slot: Any, storage_buffers: Any, num_bindings: Any, / + ) -> None: + """void SDL_BindGPUComputeStorageBuffers(SDL_GPUComputePass *compute_pass, Uint32 first_slot, SDL_GPUBuffer * const *storage_buffers, Uint32 num_bindings)""" + + @staticmethod + def SDL_BindGPUComputeStorageTextures( + compute_pass: Any, first_slot: Any, storage_textures: Any, num_bindings: Any, / + ) -> None: + """void SDL_BindGPUComputeStorageTextures(SDL_GPUComputePass *compute_pass, Uint32 first_slot, SDL_GPUTexture * const *storage_textures, Uint32 num_bindings)""" + + @staticmethod + def SDL_BindGPUFragmentSamplers( + render_pass: Any, first_slot: Any, texture_sampler_bindings: Any, num_bindings: Any, / + ) -> None: + """void SDL_BindGPUFragmentSamplers(SDL_GPURenderPass *render_pass, Uint32 first_slot, const SDL_GPUTextureSamplerBinding *texture_sampler_bindings, Uint32 num_bindings)""" + + @staticmethod + def SDL_BindGPUFragmentStorageBuffers( + render_pass: Any, first_slot: Any, storage_buffers: Any, num_bindings: Any, / + ) -> None: + """void SDL_BindGPUFragmentStorageBuffers(SDL_GPURenderPass *render_pass, Uint32 first_slot, SDL_GPUBuffer * const *storage_buffers, Uint32 num_bindings)""" + + @staticmethod + def SDL_BindGPUFragmentStorageTextures( + render_pass: Any, first_slot: Any, storage_textures: Any, num_bindings: Any, / + ) -> None: + """void SDL_BindGPUFragmentStorageTextures(SDL_GPURenderPass *render_pass, Uint32 first_slot, SDL_GPUTexture * const *storage_textures, Uint32 num_bindings)""" + + @staticmethod + def SDL_BindGPUGraphicsPipeline(render_pass: Any, graphics_pipeline: Any, /) -> None: + """void SDL_BindGPUGraphicsPipeline(SDL_GPURenderPass *render_pass, SDL_GPUGraphicsPipeline *graphics_pipeline)""" + + @staticmethod + def SDL_BindGPUIndexBuffer(render_pass: Any, binding: Any, index_element_size: Any, /) -> None: + """void SDL_BindGPUIndexBuffer(SDL_GPURenderPass *render_pass, const SDL_GPUBufferBinding *binding, SDL_GPUIndexElementSize index_element_size)""" + + @staticmethod + def SDL_BindGPUVertexBuffers(render_pass: Any, first_slot: Any, bindings: Any, num_bindings: Any, /) -> None: + """void SDL_BindGPUVertexBuffers(SDL_GPURenderPass *render_pass, Uint32 first_slot, const SDL_GPUBufferBinding *bindings, Uint32 num_bindings)""" + + @staticmethod + def SDL_BindGPUVertexSamplers( + render_pass: Any, first_slot: Any, texture_sampler_bindings: Any, num_bindings: Any, / + ) -> None: + """void SDL_BindGPUVertexSamplers(SDL_GPURenderPass *render_pass, Uint32 first_slot, const SDL_GPUTextureSamplerBinding *texture_sampler_bindings, Uint32 num_bindings)""" + + @staticmethod + def SDL_BindGPUVertexStorageBuffers( + render_pass: Any, first_slot: Any, storage_buffers: Any, num_bindings: Any, / + ) -> None: + """void SDL_BindGPUVertexStorageBuffers(SDL_GPURenderPass *render_pass, Uint32 first_slot, SDL_GPUBuffer * const *storage_buffers, Uint32 num_bindings)""" + + @staticmethod + def SDL_BindGPUVertexStorageTextures( + render_pass: Any, first_slot: Any, storage_textures: Any, num_bindings: Any, / + ) -> None: + """void SDL_BindGPUVertexStorageTextures(SDL_GPURenderPass *render_pass, Uint32 first_slot, SDL_GPUTexture * const *storage_textures, Uint32 num_bindings)""" + + @staticmethod + def SDL_BlitGPUTexture(command_buffer: Any, info: Any, /) -> None: + """void SDL_BlitGPUTexture(SDL_GPUCommandBuffer *command_buffer, const SDL_GPUBlitInfo *info)""" + + @staticmethod + def SDL_BlitSurface(src: Any, srcrect: Any, dst: Any, dstrect: Any, /) -> bool: + """bool SDL_BlitSurface(SDL_Surface *src, const SDL_Rect *srcrect, SDL_Surface *dst, const SDL_Rect *dstrect)""" + + @staticmethod + def SDL_BlitSurface9Grid( + src: Any, + srcrect: Any, + left_width: int, + right_width: int, + top_height: int, + bottom_height: int, + scale: float, + scaleMode: Any, + dst: Any, + dstrect: Any, + /, + ) -> bool: + """bool SDL_BlitSurface9Grid(SDL_Surface *src, const SDL_Rect *srcrect, int left_width, int right_width, int top_height, int bottom_height, float scale, SDL_ScaleMode scaleMode, SDL_Surface *dst, const SDL_Rect *dstrect)""" + + @staticmethod + def SDL_BlitSurfaceScaled(src: Any, srcrect: Any, dst: Any, dstrect: Any, scaleMode: Any, /) -> bool: + """bool SDL_BlitSurfaceScaled(SDL_Surface *src, const SDL_Rect *srcrect, SDL_Surface *dst, const SDL_Rect *dstrect, SDL_ScaleMode scaleMode)""" + + @staticmethod + def SDL_BlitSurfaceTiled(src: Any, srcrect: Any, dst: Any, dstrect: Any, /) -> bool: + """bool SDL_BlitSurfaceTiled(SDL_Surface *src, const SDL_Rect *srcrect, SDL_Surface *dst, const SDL_Rect *dstrect)""" + + @staticmethod + def SDL_BlitSurfaceTiledWithScale( + src: Any, srcrect: Any, scale: float, scaleMode: Any, dst: Any, dstrect: Any, / + ) -> bool: + """bool SDL_BlitSurfaceTiledWithScale(SDL_Surface *src, const SDL_Rect *srcrect, float scale, SDL_ScaleMode scaleMode, SDL_Surface *dst, const SDL_Rect *dstrect)""" + + @staticmethod + def SDL_BlitSurfaceUnchecked(src: Any, srcrect: Any, dst: Any, dstrect: Any, /) -> bool: + """bool SDL_BlitSurfaceUnchecked(SDL_Surface *src, const SDL_Rect *srcrect, SDL_Surface *dst, const SDL_Rect *dstrect)""" + + @staticmethod + def SDL_BlitSurfaceUncheckedScaled(src: Any, srcrect: Any, dst: Any, dstrect: Any, scaleMode: Any, /) -> bool: + """bool SDL_BlitSurfaceUncheckedScaled(SDL_Surface *src, const SDL_Rect *srcrect, SDL_Surface *dst, const SDL_Rect *dstrect, SDL_ScaleMode scaleMode)""" + + @staticmethod + def SDL_BroadcastCondition(cond: Any, /) -> None: + """void SDL_BroadcastCondition(SDL_Condition *cond)""" + + @staticmethod + def SDL_CalculateGPUTextureFormatSize(format: Any, width: Any, height: Any, depth_or_layer_count: Any, /) -> Any: + """Uint32 SDL_CalculateGPUTextureFormatSize(SDL_GPUTextureFormat format, Uint32 width, Uint32 height, Uint32 depth_or_layer_count)""" + + @staticmethod + def SDL_CancelGPUCommandBuffer(command_buffer: Any, /) -> bool: + """bool SDL_CancelGPUCommandBuffer(SDL_GPUCommandBuffer *command_buffer)""" + + @staticmethod + def SDL_CaptureMouse(enabled: bool, /) -> bool: + """bool SDL_CaptureMouse(bool enabled)""" + + @staticmethod + def SDL_ClaimWindowForGPUDevice(device: Any, window: Any, /) -> bool: + """bool SDL_ClaimWindowForGPUDevice(SDL_GPUDevice *device, SDL_Window *window)""" + + @staticmethod + def SDL_CleanupTLS() -> None: + """void SDL_CleanupTLS(void)""" + + @staticmethod + def SDL_ClearAudioStream(stream: Any, /) -> bool: + """bool SDL_ClearAudioStream(SDL_AudioStream *stream)""" + + @staticmethod + def SDL_ClearClipboardData() -> bool: + """bool SDL_ClearClipboardData(void)""" + + @staticmethod + def SDL_ClearComposition(window: Any, /) -> bool: + """bool SDL_ClearComposition(SDL_Window *window)""" + + @staticmethod + def SDL_ClearError() -> bool: + """bool SDL_ClearError(void)""" + + @staticmethod + def SDL_ClearProperty(props: Any, name: Any, /) -> bool: + """bool SDL_ClearProperty(SDL_PropertiesID props, const char *name)""" + + @staticmethod + def SDL_ClearSurface(surface: Any, r: float, g: float, b: float, a: float, /) -> bool: + """bool SDL_ClearSurface(SDL_Surface *surface, float r, float g, float b, float a)""" + + @staticmethod + def SDL_ClickTrayEntry(entry: Any, /) -> None: + """void SDL_ClickTrayEntry(SDL_TrayEntry *entry)""" + + @staticmethod + def SDL_CloseAsyncIO(asyncio: Any, flush: bool, queue: Any, userdata: Any, /) -> bool: + """bool SDL_CloseAsyncIO(SDL_AsyncIO *asyncio, bool flush, SDL_AsyncIOQueue *queue, void *userdata)""" + + @staticmethod + def SDL_CloseAudioDevice(devid: Any, /) -> None: + """void SDL_CloseAudioDevice(SDL_AudioDeviceID devid)""" + + @staticmethod + def SDL_CloseCamera(camera: Any, /) -> None: + """void SDL_CloseCamera(SDL_Camera *camera)""" + + @staticmethod + def SDL_CloseGamepad(gamepad: Any, /) -> None: + """void SDL_CloseGamepad(SDL_Gamepad *gamepad)""" + + @staticmethod + def SDL_CloseHaptic(haptic: Any, /) -> None: + """void SDL_CloseHaptic(SDL_Haptic *haptic)""" + + @staticmethod + def SDL_CloseIO(context: Any, /) -> bool: + """bool SDL_CloseIO(SDL_IOStream *context)""" + + @staticmethod + def SDL_CloseJoystick(joystick: Any, /) -> None: + """void SDL_CloseJoystick(SDL_Joystick *joystick)""" + + @staticmethod + def SDL_CloseSensor(sensor: Any, /) -> None: + """void SDL_CloseSensor(SDL_Sensor *sensor)""" + + @staticmethod + def SDL_CloseStorage(storage: Any, /) -> bool: + """bool SDL_CloseStorage(SDL_Storage *storage)""" + + @staticmethod + def SDL_CompareAndSwapAtomicInt(a: Any, oldval: int, newval: int, /) -> bool: + """bool SDL_CompareAndSwapAtomicInt(SDL_AtomicInt *a, int oldval, int newval)""" + + @staticmethod + def SDL_CompareAndSwapAtomicPointer(a: Any, oldval: Any, newval: Any, /) -> bool: + """bool SDL_CompareAndSwapAtomicPointer(void **a, void *oldval, void *newval)""" + + @staticmethod + def SDL_CompareAndSwapAtomicU32(a: Any, oldval: Any, newval: Any, /) -> bool: + """bool SDL_CompareAndSwapAtomicU32(SDL_AtomicU32 *a, Uint32 oldval, Uint32 newval)""" + + @staticmethod + def SDL_ComposeCustomBlendMode( + srcColorFactor: Any, + dstColorFactor: Any, + colorOperation: Any, + srcAlphaFactor: Any, + dstAlphaFactor: Any, + alphaOperation: Any, + /, + ) -> Any: + """SDL_BlendMode SDL_ComposeCustomBlendMode(SDL_BlendFactor srcColorFactor, SDL_BlendFactor dstColorFactor, SDL_BlendOperation colorOperation, SDL_BlendFactor srcAlphaFactor, SDL_BlendFactor dstAlphaFactor, SDL_BlendOperation alphaOperation)""" + + @staticmethod + def SDL_ConvertAudioSamples( + src_spec: Any, src_data: Any, src_len: int, dst_spec: Any, dst_data: Any, dst_len: Any, / + ) -> bool: + """bool SDL_ConvertAudioSamples(const SDL_AudioSpec *src_spec, const Uint8 *src_data, int src_len, const SDL_AudioSpec *dst_spec, Uint8 **dst_data, int *dst_len)""" + + @staticmethod + def SDL_ConvertEventToRenderCoordinates(renderer: Any, event: Any, /) -> bool: + """bool SDL_ConvertEventToRenderCoordinates(SDL_Renderer *renderer, SDL_Event *event)""" + + @staticmethod + def SDL_ConvertPixels( + width: int, height: int, src_format: Any, src: Any, src_pitch: int, dst_format: Any, dst: Any, dst_pitch: int, / + ) -> bool: + """bool SDL_ConvertPixels(int width, int height, SDL_PixelFormat src_format, const void *src, int src_pitch, SDL_PixelFormat dst_format, void *dst, int dst_pitch)""" + + @staticmethod + def SDL_ConvertPixelsAndColorspace( + width: int, + height: int, + src_format: Any, + src_colorspace: Any, + src_properties: Any, + src: Any, + src_pitch: int, + dst_format: Any, + dst_colorspace: Any, + dst_properties: Any, + dst: Any, + dst_pitch: int, + /, + ) -> bool: + """bool SDL_ConvertPixelsAndColorspace(int width, int height, SDL_PixelFormat src_format, SDL_Colorspace src_colorspace, SDL_PropertiesID src_properties, const void *src, int src_pitch, SDL_PixelFormat dst_format, SDL_Colorspace dst_colorspace, SDL_PropertiesID dst_properties, void *dst, int dst_pitch)""" + + @staticmethod + def SDL_ConvertSurface(surface: Any, format: Any, /) -> Any: + """SDL_Surface *SDL_ConvertSurface(SDL_Surface *surface, SDL_PixelFormat format)""" + + @staticmethod + def SDL_ConvertSurfaceAndColorspace(surface: Any, format: Any, palette: Any, colorspace: Any, props: Any, /) -> Any: + """SDL_Surface *SDL_ConvertSurfaceAndColorspace(SDL_Surface *surface, SDL_PixelFormat format, SDL_Palette *palette, SDL_Colorspace colorspace, SDL_PropertiesID props)""" + + @staticmethod + def SDL_CopyFile(oldpath: Any, newpath: Any, /) -> bool: + """bool SDL_CopyFile(const char *oldpath, const char *newpath)""" + + @staticmethod + def SDL_CopyGPUBufferToBuffer(copy_pass: Any, source: Any, destination: Any, size: Any, cycle: bool, /) -> None: + """void SDL_CopyGPUBufferToBuffer(SDL_GPUCopyPass *copy_pass, const SDL_GPUBufferLocation *source, const SDL_GPUBufferLocation *destination, Uint32 size, bool cycle)""" + + @staticmethod + def SDL_CopyGPUTextureToTexture( + copy_pass: Any, source: Any, destination: Any, w: Any, h: Any, d: Any, cycle: bool, / + ) -> None: + """void SDL_CopyGPUTextureToTexture(SDL_GPUCopyPass *copy_pass, const SDL_GPUTextureLocation *source, const SDL_GPUTextureLocation *destination, Uint32 w, Uint32 h, Uint32 d, bool cycle)""" + + @staticmethod + def SDL_CopyProperties(src: Any, dst: Any, /) -> bool: + """bool SDL_CopyProperties(SDL_PropertiesID src, SDL_PropertiesID dst)""" + + @staticmethod + def SDL_CopyStorageFile(storage: Any, oldpath: Any, newpath: Any, /) -> bool: + """bool SDL_CopyStorageFile(SDL_Storage *storage, const char *oldpath, const char *newpath)""" + + @staticmethod + def SDL_CreateAsyncIOQueue() -> Any: + """SDL_AsyncIOQueue *SDL_CreateAsyncIOQueue(void)""" + + @staticmethod + def SDL_CreateAudioStream(src_spec: Any, dst_spec: Any, /) -> Any: + """SDL_AudioStream *SDL_CreateAudioStream(const SDL_AudioSpec *src_spec, const SDL_AudioSpec *dst_spec)""" + + @staticmethod + def SDL_CreateColorCursor(surface: Any, hot_x: int, hot_y: int, /) -> Any: + """SDL_Cursor *SDL_CreateColorCursor(SDL_Surface *surface, int hot_x, int hot_y)""" + + @staticmethod + def SDL_CreateCondition() -> Any: + """SDL_Condition *SDL_CreateCondition(void)""" + + @staticmethod + def SDL_CreateCursor(data: Any, mask: Any, w: int, h: int, hot_x: int, hot_y: int, /) -> Any: + """SDL_Cursor *SDL_CreateCursor(const Uint8 *data, const Uint8 *mask, int w, int h, int hot_x, int hot_y)""" + + @staticmethod + def SDL_CreateDirectory(path: Any, /) -> bool: + """bool SDL_CreateDirectory(const char *path)""" + + @staticmethod + def SDL_CreateEnvironment(populated: bool, /) -> Any: + """SDL_Environment *SDL_CreateEnvironment(bool populated)""" + + @staticmethod + def SDL_CreateGPUBuffer(device: Any, createinfo: Any, /) -> Any: + """SDL_GPUBuffer *SDL_CreateGPUBuffer(SDL_GPUDevice *device, const SDL_GPUBufferCreateInfo *createinfo)""" + + @staticmethod + def SDL_CreateGPUComputePipeline(device: Any, createinfo: Any, /) -> Any: + """SDL_GPUComputePipeline *SDL_CreateGPUComputePipeline(SDL_GPUDevice *device, const SDL_GPUComputePipelineCreateInfo *createinfo)""" + + @staticmethod + def SDL_CreateGPUDevice(format_flags: Any, debug_mode: bool, name: Any, /) -> Any: + """SDL_GPUDevice *SDL_CreateGPUDevice(SDL_GPUShaderFormat format_flags, bool debug_mode, const char *name)""" + + @staticmethod + def SDL_CreateGPUDeviceWithProperties(props: Any, /) -> Any: + """SDL_GPUDevice *SDL_CreateGPUDeviceWithProperties(SDL_PropertiesID props)""" + + @staticmethod + def SDL_CreateGPUGraphicsPipeline(device: Any, createinfo: Any, /) -> Any: + """SDL_GPUGraphicsPipeline *SDL_CreateGPUGraphicsPipeline(SDL_GPUDevice *device, const SDL_GPUGraphicsPipelineCreateInfo *createinfo)""" + + @staticmethod + def SDL_CreateGPUSampler(device: Any, createinfo: Any, /) -> Any: + """SDL_GPUSampler *SDL_CreateGPUSampler(SDL_GPUDevice *device, const SDL_GPUSamplerCreateInfo *createinfo)""" + + @staticmethod + def SDL_CreateGPUShader(device: Any, createinfo: Any, /) -> Any: + """SDL_GPUShader *SDL_CreateGPUShader(SDL_GPUDevice *device, const SDL_GPUShaderCreateInfo *createinfo)""" + + @staticmethod + def SDL_CreateGPUTexture(device: Any, createinfo: Any, /) -> Any: + """SDL_GPUTexture *SDL_CreateGPUTexture(SDL_GPUDevice *device, const SDL_GPUTextureCreateInfo *createinfo)""" + + @staticmethod + def SDL_CreateGPUTransferBuffer(device: Any, createinfo: Any, /) -> Any: + """SDL_GPUTransferBuffer *SDL_CreateGPUTransferBuffer(SDL_GPUDevice *device, const SDL_GPUTransferBufferCreateInfo *createinfo)""" + + @staticmethod + def SDL_CreateHapticEffect(haptic: Any, effect: Any, /) -> int: + """int SDL_CreateHapticEffect(SDL_Haptic *haptic, const SDL_HapticEffect *effect)""" + + @staticmethod + def SDL_CreateMutex() -> Any: + """SDL_Mutex *SDL_CreateMutex(void)""" + + @staticmethod + def SDL_CreatePalette(ncolors: int, /) -> Any: + """SDL_Palette *SDL_CreatePalette(int ncolors)""" + + @staticmethod + def SDL_CreatePopupWindow(parent: Any, offset_x: int, offset_y: int, w: int, h: int, flags: Any, /) -> Any: + """SDL_Window *SDL_CreatePopupWindow(SDL_Window *parent, int offset_x, int offset_y, int w, int h, SDL_WindowFlags flags)""" + + @staticmethod + def SDL_CreateProcess(args: Any, pipe_stdio: bool, /) -> Any: + """SDL_Process *SDL_CreateProcess(const char * const *args, bool pipe_stdio)""" + + @staticmethod + def SDL_CreateProcessWithProperties(props: Any, /) -> Any: + """SDL_Process *SDL_CreateProcessWithProperties(SDL_PropertiesID props)""" + + @staticmethod + def SDL_CreateProperties() -> Any: + """SDL_PropertiesID SDL_CreateProperties(void)""" + + @staticmethod + def SDL_CreateRWLock() -> Any: + """SDL_RWLock *SDL_CreateRWLock(void)""" + + @staticmethod + def SDL_CreateRenderer(window: Any, name: Any, /) -> Any: + """SDL_Renderer *SDL_CreateRenderer(SDL_Window *window, const char *name)""" + + @staticmethod + def SDL_CreateRendererWithProperties(props: Any, /) -> Any: + """SDL_Renderer *SDL_CreateRendererWithProperties(SDL_PropertiesID props)""" + + @staticmethod + def SDL_CreateSemaphore(initial_value: Any, /) -> Any: + """SDL_Semaphore *SDL_CreateSemaphore(Uint32 initial_value)""" + + @staticmethod + def SDL_CreateSoftwareRenderer(surface: Any, /) -> Any: + """SDL_Renderer *SDL_CreateSoftwareRenderer(SDL_Surface *surface)""" + + @staticmethod + def SDL_CreateStorageDirectory(storage: Any, path: Any, /) -> bool: + """bool SDL_CreateStorageDirectory(SDL_Storage *storage, const char *path)""" + + @staticmethod + def SDL_CreateSurface(width: int, height: int, format: Any, /) -> Any: + """SDL_Surface *SDL_CreateSurface(int width, int height, SDL_PixelFormat format)""" + + @staticmethod + def SDL_CreateSurfaceFrom(width: int, height: int, format: Any, pixels: Any, pitch: int, /) -> Any: + """SDL_Surface *SDL_CreateSurfaceFrom(int width, int height, SDL_PixelFormat format, void *pixels, int pitch)""" + + @staticmethod + def SDL_CreateSurfacePalette(surface: Any, /) -> Any: + """SDL_Palette *SDL_CreateSurfacePalette(SDL_Surface *surface)""" + + @staticmethod + def SDL_CreateSystemCursor(id: Any, /) -> Any: + """SDL_Cursor *SDL_CreateSystemCursor(SDL_SystemCursor id)""" + + @staticmethod + def SDL_CreateTexture(renderer: Any, format: Any, access: Any, w: int, h: int, /) -> Any: + """SDL_Texture *SDL_CreateTexture(SDL_Renderer *renderer, SDL_PixelFormat format, SDL_TextureAccess access, int w, int h)""" + + @staticmethod + def SDL_CreateTextureFromSurface(renderer: Any, surface: Any, /) -> Any: + """SDL_Texture *SDL_CreateTextureFromSurface(SDL_Renderer *renderer, SDL_Surface *surface)""" + + @staticmethod + def SDL_CreateTextureWithProperties(renderer: Any, props: Any, /) -> Any: + """SDL_Texture *SDL_CreateTextureWithProperties(SDL_Renderer *renderer, SDL_PropertiesID props)""" + + @staticmethod + def SDL_CreateThreadRuntime(fn: Any, name: Any, data: Any, pfnBeginThread: Any, pfnEndThread: Any, /) -> Any: + """SDL_Thread *SDL_CreateThreadRuntime(SDL_ThreadFunction fn, const char *name, void *data, SDL_FunctionPointer pfnBeginThread, SDL_FunctionPointer pfnEndThread)""" + + @staticmethod + def SDL_CreateThreadWithPropertiesRuntime(props: Any, pfnBeginThread: Any, pfnEndThread: Any, /) -> Any: + """SDL_Thread *SDL_CreateThreadWithPropertiesRuntime(SDL_PropertiesID props, SDL_FunctionPointer pfnBeginThread, SDL_FunctionPointer pfnEndThread)""" + + @staticmethod + def SDL_CreateTray(icon: Any, tooltip: Any, /) -> Any: + """SDL_Tray *SDL_CreateTray(SDL_Surface *icon, const char *tooltip)""" + + @staticmethod + def SDL_CreateTrayMenu(tray: Any, /) -> Any: + """SDL_TrayMenu *SDL_CreateTrayMenu(SDL_Tray *tray)""" + + @staticmethod + def SDL_CreateTraySubmenu(entry: Any, /) -> Any: + """SDL_TrayMenu *SDL_CreateTraySubmenu(SDL_TrayEntry *entry)""" + + @staticmethod + def SDL_CreateWindow(title: Any, w: int, h: int, flags: Any, /) -> Any: + """SDL_Window *SDL_CreateWindow(const char *title, int w, int h, SDL_WindowFlags flags)""" + + @staticmethod + def SDL_CreateWindowAndRenderer( + title: Any, width: int, height: int, window_flags: Any, window: Any, renderer: Any, / + ) -> bool: + """bool SDL_CreateWindowAndRenderer(const char *title, int width, int height, SDL_WindowFlags window_flags, SDL_Window **window, SDL_Renderer **renderer)""" + + @staticmethod + def SDL_CreateWindowWithProperties(props: Any, /) -> Any: + """SDL_Window *SDL_CreateWindowWithProperties(SDL_PropertiesID props)""" + + @staticmethod + def SDL_CursorVisible() -> bool: + """bool SDL_CursorVisible(void)""" + + @staticmethod + def SDL_DateTimeToTime(dt: Any, ticks: Any, /) -> bool: + """bool SDL_DateTimeToTime(const SDL_DateTime *dt, SDL_Time *ticks)""" + + @staticmethod + def SDL_Delay(ms: Any, /) -> None: + """void SDL_Delay(Uint32 ms)""" + + @staticmethod + def SDL_DelayNS(ns: Any, /) -> None: + """void SDL_DelayNS(Uint64 ns)""" + + @staticmethod + def SDL_DelayPrecise(ns: Any, /) -> None: + """void SDL_DelayPrecise(Uint64 ns)""" + + @staticmethod + def SDL_DestroyAsyncIOQueue(queue: Any, /) -> None: + """void SDL_DestroyAsyncIOQueue(SDL_AsyncIOQueue *queue)""" + + @staticmethod + def SDL_DestroyAudioStream(stream: Any, /) -> None: + """void SDL_DestroyAudioStream(SDL_AudioStream *stream)""" + + @staticmethod + def SDL_DestroyCondition(cond: Any, /) -> None: + """void SDL_DestroyCondition(SDL_Condition *cond)""" + + @staticmethod + def SDL_DestroyCursor(cursor: Any, /) -> None: + """void SDL_DestroyCursor(SDL_Cursor *cursor)""" + + @staticmethod + def SDL_DestroyEnvironment(env: Any, /) -> None: + """void SDL_DestroyEnvironment(SDL_Environment *env)""" + + @staticmethod + def SDL_DestroyGPUDevice(device: Any, /) -> None: + """void SDL_DestroyGPUDevice(SDL_GPUDevice *device)""" + + @staticmethod + def SDL_DestroyHapticEffect(haptic: Any, effect: int, /) -> None: + """void SDL_DestroyHapticEffect(SDL_Haptic *haptic, int effect)""" + + @staticmethod + def SDL_DestroyMutex(mutex: Any, /) -> None: + """void SDL_DestroyMutex(SDL_Mutex *mutex)""" + + @staticmethod + def SDL_DestroyPalette(palette: Any, /) -> None: + """void SDL_DestroyPalette(SDL_Palette *palette)""" + + @staticmethod + def SDL_DestroyProcess(process: Any, /) -> None: + """void SDL_DestroyProcess(SDL_Process *process)""" + + @staticmethod + def SDL_DestroyProperties(props: Any, /) -> None: + """void SDL_DestroyProperties(SDL_PropertiesID props)""" + + @staticmethod + def SDL_DestroyRWLock(rwlock: Any, /) -> None: + """void SDL_DestroyRWLock(SDL_RWLock *rwlock)""" + + @staticmethod + def SDL_DestroyRenderer(renderer: Any, /) -> None: + """void SDL_DestroyRenderer(SDL_Renderer *renderer)""" + + @staticmethod + def SDL_DestroySemaphore(sem: Any, /) -> None: + """void SDL_DestroySemaphore(SDL_Semaphore *sem)""" + + @staticmethod + def SDL_DestroySurface(surface: Any, /) -> None: + """void SDL_DestroySurface(SDL_Surface *surface)""" + + @staticmethod + def SDL_DestroyTexture(texture: Any, /) -> None: + """void SDL_DestroyTexture(SDL_Texture *texture)""" + + @staticmethod + def SDL_DestroyTray(tray: Any, /) -> None: + """void SDL_DestroyTray(SDL_Tray *tray)""" + + @staticmethod + def SDL_DestroyWindow(window: Any, /) -> None: + """void SDL_DestroyWindow(SDL_Window *window)""" + + @staticmethod + def SDL_DestroyWindowSurface(window: Any, /) -> bool: + """bool SDL_DestroyWindowSurface(SDL_Window *window)""" + + @staticmethod + def SDL_DetachThread(thread: Any, /) -> None: + """void SDL_DetachThread(SDL_Thread *thread)""" + + @staticmethod + def SDL_DetachVirtualJoystick(instance_id: Any, /) -> bool: + """bool SDL_DetachVirtualJoystick(SDL_JoystickID instance_id)""" + + @staticmethod + def SDL_DisableScreenSaver() -> bool: + """bool SDL_DisableScreenSaver(void)""" + + @staticmethod + def SDL_DispatchGPUCompute(compute_pass: Any, groupcount_x: Any, groupcount_y: Any, groupcount_z: Any, /) -> None: + """void SDL_DispatchGPUCompute(SDL_GPUComputePass *compute_pass, Uint32 groupcount_x, Uint32 groupcount_y, Uint32 groupcount_z)""" + + @staticmethod + def SDL_DispatchGPUComputeIndirect(compute_pass: Any, buffer: Any, offset: Any, /) -> None: + """void SDL_DispatchGPUComputeIndirect(SDL_GPUComputePass *compute_pass, SDL_GPUBuffer *buffer, Uint32 offset)""" + + @staticmethod + def SDL_DownloadFromGPUBuffer(copy_pass: Any, source: Any, destination: Any, /) -> None: + """void SDL_DownloadFromGPUBuffer(SDL_GPUCopyPass *copy_pass, const SDL_GPUBufferRegion *source, const SDL_GPUTransferBufferLocation *destination)""" + + @staticmethod + def SDL_DownloadFromGPUTexture(copy_pass: Any, source: Any, destination: Any, /) -> None: + """void SDL_DownloadFromGPUTexture(SDL_GPUCopyPass *copy_pass, const SDL_GPUTextureRegion *source, const SDL_GPUTextureTransferInfo *destination)""" + + @staticmethod + def SDL_DrawGPUIndexedPrimitives( + render_pass: Any, + num_indices: Any, + num_instances: Any, + first_index: Any, + vertex_offset: Any, + first_instance: Any, + /, + ) -> None: + """void SDL_DrawGPUIndexedPrimitives(SDL_GPURenderPass *render_pass, Uint32 num_indices, Uint32 num_instances, Uint32 first_index, Sint32 vertex_offset, Uint32 first_instance)""" + + @staticmethod + def SDL_DrawGPUIndexedPrimitivesIndirect(render_pass: Any, buffer: Any, offset: Any, draw_count: Any, /) -> None: + """void SDL_DrawGPUIndexedPrimitivesIndirect(SDL_GPURenderPass *render_pass, SDL_GPUBuffer *buffer, Uint32 offset, Uint32 draw_count)""" + + @staticmethod + def SDL_DrawGPUPrimitives( + render_pass: Any, num_vertices: Any, num_instances: Any, first_vertex: Any, first_instance: Any, / + ) -> None: + """void SDL_DrawGPUPrimitives(SDL_GPURenderPass *render_pass, Uint32 num_vertices, Uint32 num_instances, Uint32 first_vertex, Uint32 first_instance)""" + + @staticmethod + def SDL_DrawGPUPrimitivesIndirect(render_pass: Any, buffer: Any, offset: Any, draw_count: Any, /) -> None: + """void SDL_DrawGPUPrimitivesIndirect(SDL_GPURenderPass *render_pass, SDL_GPUBuffer *buffer, Uint32 offset, Uint32 draw_count)""" + + @staticmethod + def SDL_DuplicateSurface(surface: Any, /) -> Any: + """SDL_Surface *SDL_DuplicateSurface(SDL_Surface *surface)""" + + @staticmethod + def SDL_EGL_GetCurrentConfig() -> Any: + """SDL_EGLConfig SDL_EGL_GetCurrentConfig(void)""" + + @staticmethod + def SDL_EGL_GetCurrentDisplay() -> Any: + """SDL_EGLDisplay SDL_EGL_GetCurrentDisplay(void)""" + + @staticmethod + def SDL_EGL_GetProcAddress(proc: Any, /) -> Any: + """SDL_FunctionPointer SDL_EGL_GetProcAddress(const char *proc)""" + + @staticmethod + def SDL_EGL_GetWindowSurface(window: Any, /) -> Any: + """SDL_EGLSurface SDL_EGL_GetWindowSurface(SDL_Window *window)""" + + @staticmethod + def SDL_EGL_SetAttributeCallbacks( + platformAttribCallback: Any, surfaceAttribCallback: Any, contextAttribCallback: Any, userdata: Any, / + ) -> None: + """void SDL_EGL_SetAttributeCallbacks(SDL_EGLAttribArrayCallback platformAttribCallback, SDL_EGLIntArrayCallback surfaceAttribCallback, SDL_EGLIntArrayCallback contextAttribCallback, void *userdata)""" + + @staticmethod + def SDL_EnableScreenSaver() -> bool: + """bool SDL_EnableScreenSaver(void)""" + + @staticmethod + def SDL_EndGPUComputePass(compute_pass: Any, /) -> None: + """void SDL_EndGPUComputePass(SDL_GPUComputePass *compute_pass)""" + + @staticmethod + def SDL_EndGPUCopyPass(copy_pass: Any, /) -> None: + """void SDL_EndGPUCopyPass(SDL_GPUCopyPass *copy_pass)""" + + @staticmethod + def SDL_EndGPURenderPass(render_pass: Any, /) -> None: + """void SDL_EndGPURenderPass(SDL_GPURenderPass *render_pass)""" + + @staticmethod + def SDL_EnumerateDirectory(path: Any, callback: Any, userdata: Any, /) -> bool: + """bool SDL_EnumerateDirectory(const char *path, SDL_EnumerateDirectoryCallback callback, void *userdata)""" + + @staticmethod + def SDL_EnumerateProperties(props: Any, callback: Any, userdata: Any, /) -> bool: + """bool SDL_EnumerateProperties(SDL_PropertiesID props, SDL_EnumeratePropertiesCallback callback, void *userdata)""" + + @staticmethod + def SDL_EnumerateStorageDirectory(storage: Any, path: Any, callback: Any, userdata: Any, /) -> bool: + """bool SDL_EnumerateStorageDirectory(SDL_Storage *storage, const char *path, SDL_EnumerateDirectoryCallback callback, void *userdata)""" + + @staticmethod + def SDL_EventEnabled(type: Any, /) -> bool: + """bool SDL_EventEnabled(Uint32 type)""" + + @staticmethod + def SDL_FillSurfaceRect(dst: Any, rect: Any, color: Any, /) -> bool: + """bool SDL_FillSurfaceRect(SDL_Surface *dst, const SDL_Rect *rect, Uint32 color)""" + + @staticmethod + def SDL_FillSurfaceRects(dst: Any, rects: Any, count: int, color: Any, /) -> bool: + """bool SDL_FillSurfaceRects(SDL_Surface *dst, const SDL_Rect *rects, int count, Uint32 color)""" + + @staticmethod + def SDL_FilterEvents(filter: Any, userdata: Any, /) -> None: + """void SDL_FilterEvents(SDL_EventFilter filter, void *userdata)""" + + @staticmethod + def SDL_FlashWindow(window: Any, operation: Any, /) -> bool: + """bool SDL_FlashWindow(SDL_Window *window, SDL_FlashOperation operation)""" + + @staticmethod + def SDL_FlipSurface(surface: Any, flip: Any, /) -> bool: + """bool SDL_FlipSurface(SDL_Surface *surface, SDL_FlipMode flip)""" + + @staticmethod + def SDL_FlushAudioStream(stream: Any, /) -> bool: + """bool SDL_FlushAudioStream(SDL_AudioStream *stream)""" + + @staticmethod + def SDL_FlushEvent(type: Any, /) -> None: + """void SDL_FlushEvent(Uint32 type)""" + + @staticmethod + def SDL_FlushEvents(minType: Any, maxType: Any, /) -> None: + """void SDL_FlushEvents(Uint32 minType, Uint32 maxType)""" + + @staticmethod + def SDL_FlushIO(context: Any, /) -> bool: + """bool SDL_FlushIO(SDL_IOStream *context)""" + + @staticmethod + def SDL_FlushRenderer(renderer: Any, /) -> bool: + """bool SDL_FlushRenderer(SDL_Renderer *renderer)""" + + @staticmethod + def SDL_GL_CreateContext(window: Any, /) -> Any: + """SDL_GLContext SDL_GL_CreateContext(SDL_Window *window)""" + + @staticmethod + def SDL_GL_DestroyContext(context: Any, /) -> bool: + """bool SDL_GL_DestroyContext(SDL_GLContext context)""" + + @staticmethod + def SDL_GL_ExtensionSupported(extension: Any, /) -> bool: + """bool SDL_GL_ExtensionSupported(const char *extension)""" + + @staticmethod + def SDL_GL_GetAttribute(attr: Any, value: Any, /) -> bool: + """bool SDL_GL_GetAttribute(SDL_GLAttr attr, int *value)""" + + @staticmethod + def SDL_GL_GetCurrentContext() -> Any: + """SDL_GLContext SDL_GL_GetCurrentContext(void)""" + + @staticmethod + def SDL_GL_GetCurrentWindow() -> Any: + """SDL_Window *SDL_GL_GetCurrentWindow(void)""" + + @staticmethod + def SDL_GL_GetProcAddress(proc: Any, /) -> Any: + """SDL_FunctionPointer SDL_GL_GetProcAddress(const char *proc)""" + + @staticmethod + def SDL_GL_GetSwapInterval(interval: Any, /) -> bool: + """bool SDL_GL_GetSwapInterval(int *interval)""" + + @staticmethod + def SDL_GL_LoadLibrary(path: Any, /) -> bool: + """bool SDL_GL_LoadLibrary(const char *path)""" + + @staticmethod + def SDL_GL_MakeCurrent(window: Any, context: Any, /) -> bool: + """bool SDL_GL_MakeCurrent(SDL_Window *window, SDL_GLContext context)""" + + @staticmethod + def SDL_GL_ResetAttributes() -> None: + """void SDL_GL_ResetAttributes(void)""" + + @staticmethod + def SDL_GL_SetAttribute(attr: Any, value: int, /) -> bool: + """bool SDL_GL_SetAttribute(SDL_GLAttr attr, int value)""" + + @staticmethod + def SDL_GL_SetSwapInterval(interval: int, /) -> bool: + """bool SDL_GL_SetSwapInterval(int interval)""" + + @staticmethod + def SDL_GL_SwapWindow(window: Any, /) -> bool: + """bool SDL_GL_SwapWindow(SDL_Window *window)""" + + @staticmethod + def SDL_GL_UnloadLibrary() -> None: + """void SDL_GL_UnloadLibrary(void)""" + + @staticmethod + def SDL_GPUSupportsProperties(props: Any, /) -> bool: + """bool SDL_GPUSupportsProperties(SDL_PropertiesID props)""" + + @staticmethod + def SDL_GPUSupportsShaderFormats(format_flags: Any, name: Any, /) -> bool: + """bool SDL_GPUSupportsShaderFormats(SDL_GPUShaderFormat format_flags, const char *name)""" + + @staticmethod + def SDL_GPUTextureFormatTexelBlockSize(format: Any, /) -> Any: + """Uint32 SDL_GPUTextureFormatTexelBlockSize(SDL_GPUTextureFormat format)""" + + @staticmethod + def SDL_GPUTextureSupportsFormat(device: Any, format: Any, type: Any, usage: Any, /) -> bool: + """bool SDL_GPUTextureSupportsFormat(SDL_GPUDevice *device, SDL_GPUTextureFormat format, SDL_GPUTextureType type, SDL_GPUTextureUsageFlags usage)""" + + @staticmethod + def SDL_GPUTextureSupportsSampleCount(device: Any, format: Any, sample_count: Any, /) -> bool: + """bool SDL_GPUTextureSupportsSampleCount(SDL_GPUDevice *device, SDL_GPUTextureFormat format, SDL_GPUSampleCount sample_count)""" + + @staticmethod + def SDL_GUIDToString(guid: Any, pszGUID: Any, cbGUID: int, /) -> None: + """void SDL_GUIDToString(SDL_GUID guid, char *pszGUID, int cbGUID)""" + + @staticmethod + def SDL_GamepadConnected(gamepad: Any, /) -> bool: + """bool SDL_GamepadConnected(SDL_Gamepad *gamepad)""" + + @staticmethod + def SDL_GamepadEventsEnabled() -> bool: + """bool SDL_GamepadEventsEnabled(void)""" + + @staticmethod + def SDL_GamepadHasAxis(gamepad: Any, axis: Any, /) -> bool: + """bool SDL_GamepadHasAxis(SDL_Gamepad *gamepad, SDL_GamepadAxis axis)""" + + @staticmethod + def SDL_GamepadHasButton(gamepad: Any, button: Any, /) -> bool: + """bool SDL_GamepadHasButton(SDL_Gamepad *gamepad, SDL_GamepadButton button)""" + + @staticmethod + def SDL_GamepadHasSensor(gamepad: Any, type: Any, /) -> bool: + """bool SDL_GamepadHasSensor(SDL_Gamepad *gamepad, SDL_SensorType type)""" + + @staticmethod + def SDL_GamepadSensorEnabled(gamepad: Any, type: Any, /) -> bool: + """bool SDL_GamepadSensorEnabled(SDL_Gamepad *gamepad, SDL_SensorType type)""" + + @staticmethod + def SDL_GenerateMipmapsForGPUTexture(command_buffer: Any, texture: Any, /) -> None: + """void SDL_GenerateMipmapsForGPUTexture(SDL_GPUCommandBuffer *command_buffer, SDL_GPUTexture *texture)""" + + @staticmethod + def SDL_GetAppMetadataProperty(name: Any, /) -> Any: + """const char *SDL_GetAppMetadataProperty(const char *name)""" + + @staticmethod + def SDL_GetAssertionHandler(puserdata: Any, /) -> Any: + """SDL_AssertionHandler SDL_GetAssertionHandler(void **puserdata)""" + + @staticmethod + def SDL_GetAssertionReport() -> Any: + """const SDL_AssertData *SDL_GetAssertionReport(void)""" + + @staticmethod + def SDL_GetAsyncIOResult(queue: Any, outcome: Any, /) -> bool: + """bool SDL_GetAsyncIOResult(SDL_AsyncIOQueue *queue, SDL_AsyncIOOutcome *outcome)""" + + @staticmethod + def SDL_GetAsyncIOSize(asyncio: Any, /) -> Any: + """Sint64 SDL_GetAsyncIOSize(SDL_AsyncIO *asyncio)""" + + @staticmethod + def SDL_GetAtomicInt(a: Any, /) -> int: + """int SDL_GetAtomicInt(SDL_AtomicInt *a)""" + + @staticmethod + def SDL_GetAtomicPointer(a: Any, /) -> Any: + """void *SDL_GetAtomicPointer(void **a)""" + + @staticmethod + def SDL_GetAtomicU32(a: Any, /) -> Any: + """Uint32 SDL_GetAtomicU32(SDL_AtomicU32 *a)""" + + @staticmethod + def SDL_GetAudioDeviceChannelMap(devid: Any, count: Any, /) -> Any: + """int *SDL_GetAudioDeviceChannelMap(SDL_AudioDeviceID devid, int *count)""" + + @staticmethod + def SDL_GetAudioDeviceFormat(devid: Any, spec: Any, sample_frames: Any, /) -> bool: + """bool SDL_GetAudioDeviceFormat(SDL_AudioDeviceID devid, SDL_AudioSpec *spec, int *sample_frames)""" + + @staticmethod + def SDL_GetAudioDeviceGain(devid: Any, /) -> float: + """float SDL_GetAudioDeviceGain(SDL_AudioDeviceID devid)""" + + @staticmethod + def SDL_GetAudioDeviceName(devid: Any, /) -> Any: + """const char *SDL_GetAudioDeviceName(SDL_AudioDeviceID devid)""" + + @staticmethod + def SDL_GetAudioDriver(index: int, /) -> Any: + """const char *SDL_GetAudioDriver(int index)""" + + @staticmethod + def SDL_GetAudioFormatName(format: Any, /) -> Any: + """const char *SDL_GetAudioFormatName(SDL_AudioFormat format)""" + + @staticmethod + def SDL_GetAudioPlaybackDevices(count: Any, /) -> Any: + """SDL_AudioDeviceID *SDL_GetAudioPlaybackDevices(int *count)""" + + @staticmethod + def SDL_GetAudioRecordingDevices(count: Any, /) -> Any: + """SDL_AudioDeviceID *SDL_GetAudioRecordingDevices(int *count)""" + + @staticmethod + def SDL_GetAudioStreamAvailable(stream: Any, /) -> int: + """int SDL_GetAudioStreamAvailable(SDL_AudioStream *stream)""" + + @staticmethod + def SDL_GetAudioStreamData(stream: Any, buf: Any, len: int, /) -> int: + """int SDL_GetAudioStreamData(SDL_AudioStream *stream, void *buf, int len)""" + + @staticmethod + def SDL_GetAudioStreamDevice(stream: Any, /) -> Any: + """SDL_AudioDeviceID SDL_GetAudioStreamDevice(SDL_AudioStream *stream)""" + + @staticmethod + def SDL_GetAudioStreamFormat(stream: Any, src_spec: Any, dst_spec: Any, /) -> bool: + """bool SDL_GetAudioStreamFormat(SDL_AudioStream *stream, SDL_AudioSpec *src_spec, SDL_AudioSpec *dst_spec)""" + + @staticmethod + def SDL_GetAudioStreamFrequencyRatio(stream: Any, /) -> float: + """float SDL_GetAudioStreamFrequencyRatio(SDL_AudioStream *stream)""" + + @staticmethod + def SDL_GetAudioStreamGain(stream: Any, /) -> float: + """float SDL_GetAudioStreamGain(SDL_AudioStream *stream)""" + + @staticmethod + def SDL_GetAudioStreamInputChannelMap(stream: Any, count: Any, /) -> Any: + """int *SDL_GetAudioStreamInputChannelMap(SDL_AudioStream *stream, int *count)""" + + @staticmethod + def SDL_GetAudioStreamOutputChannelMap(stream: Any, count: Any, /) -> Any: + """int *SDL_GetAudioStreamOutputChannelMap(SDL_AudioStream *stream, int *count)""" + + @staticmethod + def SDL_GetAudioStreamProperties(stream: Any, /) -> Any: + """SDL_PropertiesID SDL_GetAudioStreamProperties(SDL_AudioStream *stream)""" + + @staticmethod + def SDL_GetAudioStreamQueued(stream: Any, /) -> int: + """int SDL_GetAudioStreamQueued(SDL_AudioStream *stream)""" + + @staticmethod + def SDL_GetBasePath() -> Any: + """const char *SDL_GetBasePath(void)""" + + @staticmethod + def SDL_GetBooleanProperty(props: Any, name: Any, default_value: bool, /) -> bool: + """bool SDL_GetBooleanProperty(SDL_PropertiesID props, const char *name, bool default_value)""" + + @staticmethod + def SDL_GetCPUCacheLineSize() -> int: + """int SDL_GetCPUCacheLineSize(void)""" + + @staticmethod + def SDL_GetCameraDriver(index: int, /) -> Any: + """const char *SDL_GetCameraDriver(int index)""" + + @staticmethod + def SDL_GetCameraFormat(camera: Any, spec: Any, /) -> bool: + """bool SDL_GetCameraFormat(SDL_Camera *camera, SDL_CameraSpec *spec)""" + + @staticmethod + def SDL_GetCameraID(camera: Any, /) -> Any: + """SDL_CameraID SDL_GetCameraID(SDL_Camera *camera)""" + + @staticmethod + def SDL_GetCameraName(instance_id: Any, /) -> Any: + """const char *SDL_GetCameraName(SDL_CameraID instance_id)""" + + @staticmethod + def SDL_GetCameraPermissionState(camera: Any, /) -> int: + """int SDL_GetCameraPermissionState(SDL_Camera *camera)""" + + @staticmethod + def SDL_GetCameraPosition(instance_id: Any, /) -> Any: + """SDL_CameraPosition SDL_GetCameraPosition(SDL_CameraID instance_id)""" + + @staticmethod + def SDL_GetCameraProperties(camera: Any, /) -> Any: + """SDL_PropertiesID SDL_GetCameraProperties(SDL_Camera *camera)""" + + @staticmethod + def SDL_GetCameraSupportedFormats(instance_id: Any, count: Any, /) -> Any: + """SDL_CameraSpec **SDL_GetCameraSupportedFormats(SDL_CameraID instance_id, int *count)""" + + @staticmethod + def SDL_GetCameras(count: Any, /) -> Any: + """SDL_CameraID *SDL_GetCameras(int *count)""" + + @staticmethod + def SDL_GetClipboardData(mime_type: Any, size: Any, /) -> Any: + """void *SDL_GetClipboardData(const char *mime_type, size_t *size)""" + + @staticmethod + def SDL_GetClipboardMimeTypes(num_mime_types: Any, /) -> Any: + """char **SDL_GetClipboardMimeTypes(size_t *num_mime_types)""" + + @staticmethod + def SDL_GetClipboardText() -> Any: + """char *SDL_GetClipboardText(void)""" + + @staticmethod + def SDL_GetClosestFullscreenDisplayMode( + displayID: Any, w: int, h: int, refresh_rate: float, include_high_density_modes: bool, closest: Any, / + ) -> bool: + """bool SDL_GetClosestFullscreenDisplayMode(SDL_DisplayID displayID, int w, int h, float refresh_rate, bool include_high_density_modes, SDL_DisplayMode *closest)""" + + @staticmethod + def SDL_GetCurrentAudioDriver() -> Any: + """const char *SDL_GetCurrentAudioDriver(void)""" + + @staticmethod + def SDL_GetCurrentCameraDriver() -> Any: + """const char *SDL_GetCurrentCameraDriver(void)""" + + @staticmethod + def SDL_GetCurrentDirectory() -> Any: + """char *SDL_GetCurrentDirectory(void)""" + + @staticmethod + def SDL_GetCurrentDisplayMode(displayID: Any, /) -> Any: + """const SDL_DisplayMode *SDL_GetCurrentDisplayMode(SDL_DisplayID displayID)""" + + @staticmethod + def SDL_GetCurrentDisplayOrientation(displayID: Any, /) -> Any: + """SDL_DisplayOrientation SDL_GetCurrentDisplayOrientation(SDL_DisplayID displayID)""" + + @staticmethod + def SDL_GetCurrentRenderOutputSize(renderer: Any, w: Any, h: Any, /) -> bool: + """bool SDL_GetCurrentRenderOutputSize(SDL_Renderer *renderer, int *w, int *h)""" + + @staticmethod + def SDL_GetCurrentThreadID() -> Any: + """SDL_ThreadID SDL_GetCurrentThreadID(void)""" + + @staticmethod + def SDL_GetCurrentTime(ticks: Any, /) -> bool: + """bool SDL_GetCurrentTime(SDL_Time *ticks)""" + + @staticmethod + def SDL_GetCurrentVideoDriver() -> Any: + """const char *SDL_GetCurrentVideoDriver(void)""" + + @staticmethod + def SDL_GetCursor() -> Any: + """SDL_Cursor *SDL_GetCursor(void)""" + + @staticmethod + def SDL_GetDateTimeLocalePreferences(dateFormat: Any, timeFormat: Any, /) -> bool: + """bool SDL_GetDateTimeLocalePreferences(SDL_DateFormat *dateFormat, SDL_TimeFormat *timeFormat)""" + + @staticmethod + def SDL_GetDayOfWeek(year: int, month: int, day: int, /) -> int: + """int SDL_GetDayOfWeek(int year, int month, int day)""" + + @staticmethod + def SDL_GetDayOfYear(year: int, month: int, day: int, /) -> int: + """int SDL_GetDayOfYear(int year, int month, int day)""" + + @staticmethod + def SDL_GetDaysInMonth(year: int, month: int, /) -> int: + """int SDL_GetDaysInMonth(int year, int month)""" + + @staticmethod + def SDL_GetDefaultAssertionHandler() -> Any: + """SDL_AssertionHandler SDL_GetDefaultAssertionHandler(void)""" + + @staticmethod + def SDL_GetDefaultCursor() -> Any: + """SDL_Cursor *SDL_GetDefaultCursor(void)""" + + @staticmethod + def SDL_GetDefaultLogOutputFunction() -> Any: + """SDL_LogOutputFunction SDL_GetDefaultLogOutputFunction(void)""" + + @staticmethod + def SDL_GetDesktopDisplayMode(displayID: Any, /) -> Any: + """const SDL_DisplayMode *SDL_GetDesktopDisplayMode(SDL_DisplayID displayID)""" + + @staticmethod + def SDL_GetDisplayBounds(displayID: Any, rect: Any, /) -> bool: + """bool SDL_GetDisplayBounds(SDL_DisplayID displayID, SDL_Rect *rect)""" + + @staticmethod + def SDL_GetDisplayContentScale(displayID: Any, /) -> float: + """float SDL_GetDisplayContentScale(SDL_DisplayID displayID)""" + + @staticmethod + def SDL_GetDisplayForPoint(point: Any, /) -> Any: + """SDL_DisplayID SDL_GetDisplayForPoint(const SDL_Point *point)""" + + @staticmethod + def SDL_GetDisplayForRect(rect: Any, /) -> Any: + """SDL_DisplayID SDL_GetDisplayForRect(const SDL_Rect *rect)""" + + @staticmethod + def SDL_GetDisplayForWindow(window: Any, /) -> Any: + """SDL_DisplayID SDL_GetDisplayForWindow(SDL_Window *window)""" + + @staticmethod + def SDL_GetDisplayName(displayID: Any, /) -> Any: + """const char *SDL_GetDisplayName(SDL_DisplayID displayID)""" + + @staticmethod + def SDL_GetDisplayProperties(displayID: Any, /) -> Any: + """SDL_PropertiesID SDL_GetDisplayProperties(SDL_DisplayID displayID)""" + + @staticmethod + def SDL_GetDisplayUsableBounds(displayID: Any, rect: Any, /) -> bool: + """bool SDL_GetDisplayUsableBounds(SDL_DisplayID displayID, SDL_Rect *rect)""" + + @staticmethod + def SDL_GetDisplays(count: Any, /) -> Any: + """SDL_DisplayID *SDL_GetDisplays(int *count)""" + + @staticmethod + def SDL_GetEnvironment() -> Any: + """SDL_Environment *SDL_GetEnvironment(void)""" + + @staticmethod + def SDL_GetEnvironmentVariable(env: Any, name: Any, /) -> Any: + """const char *SDL_GetEnvironmentVariable(SDL_Environment *env, const char *name)""" + + @staticmethod + def SDL_GetEnvironmentVariables(env: Any, /) -> Any: + """char **SDL_GetEnvironmentVariables(SDL_Environment *env)""" + + @staticmethod + def SDL_GetError() -> Any: + """const char *SDL_GetError(void)""" + + @staticmethod + def SDL_GetEventFilter(filter: Any, userdata: Any, /) -> bool: + """bool SDL_GetEventFilter(SDL_EventFilter *filter, void **userdata)""" + + @staticmethod + def SDL_GetFloatProperty(props: Any, name: Any, default_value: float, /) -> float: + """float SDL_GetFloatProperty(SDL_PropertiesID props, const char *name, float default_value)""" + + @staticmethod + def SDL_GetFullscreenDisplayModes(displayID: Any, count: Any, /) -> Any: + """SDL_DisplayMode **SDL_GetFullscreenDisplayModes(SDL_DisplayID displayID, int *count)""" + + @staticmethod + def SDL_GetGPUDeviceDriver(device: Any, /) -> Any: + """const char *SDL_GetGPUDeviceDriver(SDL_GPUDevice *device)""" + + @staticmethod + def SDL_GetGPUDriver(index: int, /) -> Any: + """const char *SDL_GetGPUDriver(int index)""" + + @staticmethod + def SDL_GetGPUShaderFormats(device: Any, /) -> Any: + """SDL_GPUShaderFormat SDL_GetGPUShaderFormats(SDL_GPUDevice *device)""" + + @staticmethod + def SDL_GetGPUSwapchainTextureFormat(device: Any, window: Any, /) -> Any: + """SDL_GPUTextureFormat SDL_GetGPUSwapchainTextureFormat(SDL_GPUDevice *device, SDL_Window *window)""" + + @staticmethod + def SDL_GetGamepadAppleSFSymbolsNameForAxis(gamepad: Any, axis: Any, /) -> Any: + """const char *SDL_GetGamepadAppleSFSymbolsNameForAxis(SDL_Gamepad *gamepad, SDL_GamepadAxis axis)""" + + @staticmethod + def SDL_GetGamepadAppleSFSymbolsNameForButton(gamepad: Any, button: Any, /) -> Any: + """const char *SDL_GetGamepadAppleSFSymbolsNameForButton(SDL_Gamepad *gamepad, SDL_GamepadButton button)""" + + @staticmethod + def SDL_GetGamepadAxis(gamepad: Any, axis: Any, /) -> Any: + """Sint16 SDL_GetGamepadAxis(SDL_Gamepad *gamepad, SDL_GamepadAxis axis)""" + + @staticmethod + def SDL_GetGamepadAxisFromString(str: Any, /) -> Any: + """SDL_GamepadAxis SDL_GetGamepadAxisFromString(const char *str)""" + + @staticmethod + def SDL_GetGamepadBindings(gamepad: Any, count: Any, /) -> Any: + """SDL_GamepadBinding **SDL_GetGamepadBindings(SDL_Gamepad *gamepad, int *count)""" + + @staticmethod + def SDL_GetGamepadButton(gamepad: Any, button: Any, /) -> bool: + """bool SDL_GetGamepadButton(SDL_Gamepad *gamepad, SDL_GamepadButton button)""" + + @staticmethod + def SDL_GetGamepadButtonFromString(str: Any, /) -> Any: + """SDL_GamepadButton SDL_GetGamepadButtonFromString(const char *str)""" + + @staticmethod + def SDL_GetGamepadButtonLabel(gamepad: Any, button: Any, /) -> Any: + """SDL_GamepadButtonLabel SDL_GetGamepadButtonLabel(SDL_Gamepad *gamepad, SDL_GamepadButton button)""" + + @staticmethod + def SDL_GetGamepadButtonLabelForType(type: Any, button: Any, /) -> Any: + """SDL_GamepadButtonLabel SDL_GetGamepadButtonLabelForType(SDL_GamepadType type, SDL_GamepadButton button)""" + + @staticmethod + def SDL_GetGamepadConnectionState(gamepad: Any, /) -> Any: + """SDL_JoystickConnectionState SDL_GetGamepadConnectionState(SDL_Gamepad *gamepad)""" + + @staticmethod + def SDL_GetGamepadFirmwareVersion(gamepad: Any, /) -> Any: + """Uint16 SDL_GetGamepadFirmwareVersion(SDL_Gamepad *gamepad)""" + + @staticmethod + def SDL_GetGamepadFromID(instance_id: Any, /) -> Any: + """SDL_Gamepad *SDL_GetGamepadFromID(SDL_JoystickID instance_id)""" + + @staticmethod + def SDL_GetGamepadFromPlayerIndex(player_index: int, /) -> Any: + """SDL_Gamepad *SDL_GetGamepadFromPlayerIndex(int player_index)""" + + @staticmethod + def SDL_GetGamepadGUIDForID(instance_id: Any, /) -> Any: + """SDL_GUID SDL_GetGamepadGUIDForID(SDL_JoystickID instance_id)""" + + @staticmethod + def SDL_GetGamepadID(gamepad: Any, /) -> Any: + """SDL_JoystickID SDL_GetGamepadID(SDL_Gamepad *gamepad)""" + + @staticmethod + def SDL_GetGamepadJoystick(gamepad: Any, /) -> Any: + """SDL_Joystick *SDL_GetGamepadJoystick(SDL_Gamepad *gamepad)""" + + @staticmethod + def SDL_GetGamepadMapping(gamepad: Any, /) -> Any: + """char *SDL_GetGamepadMapping(SDL_Gamepad *gamepad)""" + + @staticmethod + def SDL_GetGamepadMappingForGUID(guid: Any, /) -> Any: + """char *SDL_GetGamepadMappingForGUID(SDL_GUID guid)""" + + @staticmethod + def SDL_GetGamepadMappingForID(instance_id: Any, /) -> Any: + """char *SDL_GetGamepadMappingForID(SDL_JoystickID instance_id)""" + + @staticmethod + def SDL_GetGamepadMappings(count: Any, /) -> Any: + """char **SDL_GetGamepadMappings(int *count)""" + + @staticmethod + def SDL_GetGamepadName(gamepad: Any, /) -> Any: + """const char *SDL_GetGamepadName(SDL_Gamepad *gamepad)""" + + @staticmethod + def SDL_GetGamepadNameForID(instance_id: Any, /) -> Any: + """const char *SDL_GetGamepadNameForID(SDL_JoystickID instance_id)""" + + @staticmethod + def SDL_GetGamepadPath(gamepad: Any, /) -> Any: + """const char *SDL_GetGamepadPath(SDL_Gamepad *gamepad)""" + + @staticmethod + def SDL_GetGamepadPathForID(instance_id: Any, /) -> Any: + """const char *SDL_GetGamepadPathForID(SDL_JoystickID instance_id)""" + + @staticmethod + def SDL_GetGamepadPlayerIndex(gamepad: Any, /) -> int: + """int SDL_GetGamepadPlayerIndex(SDL_Gamepad *gamepad)""" + + @staticmethod + def SDL_GetGamepadPlayerIndexForID(instance_id: Any, /) -> int: + """int SDL_GetGamepadPlayerIndexForID(SDL_JoystickID instance_id)""" + + @staticmethod + def SDL_GetGamepadPowerInfo(gamepad: Any, percent: Any, /) -> Any: + """SDL_PowerState SDL_GetGamepadPowerInfo(SDL_Gamepad *gamepad, int *percent)""" + + @staticmethod + def SDL_GetGamepadProduct(gamepad: Any, /) -> Any: + """Uint16 SDL_GetGamepadProduct(SDL_Gamepad *gamepad)""" + + @staticmethod + def SDL_GetGamepadProductForID(instance_id: Any, /) -> Any: + """Uint16 SDL_GetGamepadProductForID(SDL_JoystickID instance_id)""" + + @staticmethod + def SDL_GetGamepadProductVersion(gamepad: Any, /) -> Any: + """Uint16 SDL_GetGamepadProductVersion(SDL_Gamepad *gamepad)""" + + @staticmethod + def SDL_GetGamepadProductVersionForID(instance_id: Any, /) -> Any: + """Uint16 SDL_GetGamepadProductVersionForID(SDL_JoystickID instance_id)""" + + @staticmethod + def SDL_GetGamepadProperties(gamepad: Any, /) -> Any: + """SDL_PropertiesID SDL_GetGamepadProperties(SDL_Gamepad *gamepad)""" + + @staticmethod + def SDL_GetGamepadSensorData(gamepad: Any, type: Any, data: Any, num_values: int, /) -> bool: + """bool SDL_GetGamepadSensorData(SDL_Gamepad *gamepad, SDL_SensorType type, float *data, int num_values)""" + + @staticmethod + def SDL_GetGamepadSensorDataRate(gamepad: Any, type: Any, /) -> float: + """float SDL_GetGamepadSensorDataRate(SDL_Gamepad *gamepad, SDL_SensorType type)""" + + @staticmethod + def SDL_GetGamepadSerial(gamepad: Any, /) -> Any: + """const char *SDL_GetGamepadSerial(SDL_Gamepad *gamepad)""" + + @staticmethod + def SDL_GetGamepadSteamHandle(gamepad: Any, /) -> Any: + """Uint64 SDL_GetGamepadSteamHandle(SDL_Gamepad *gamepad)""" + + @staticmethod + def SDL_GetGamepadStringForAxis(axis: Any, /) -> Any: + """const char *SDL_GetGamepadStringForAxis(SDL_GamepadAxis axis)""" + + @staticmethod + def SDL_GetGamepadStringForButton(button: Any, /) -> Any: + """const char *SDL_GetGamepadStringForButton(SDL_GamepadButton button)""" + + @staticmethod + def SDL_GetGamepadStringForType(type: Any, /) -> Any: + """const char *SDL_GetGamepadStringForType(SDL_GamepadType type)""" + + @staticmethod + def SDL_GetGamepadTouchpadFinger( + gamepad: Any, touchpad: int, finger: int, down: Any, x: Any, y: Any, pressure: Any, / + ) -> bool: + """bool SDL_GetGamepadTouchpadFinger(SDL_Gamepad *gamepad, int touchpad, int finger, bool *down, float *x, float *y, float *pressure)""" + + @staticmethod + def SDL_GetGamepadType(gamepad: Any, /) -> Any: + """SDL_GamepadType SDL_GetGamepadType(SDL_Gamepad *gamepad)""" + + @staticmethod + def SDL_GetGamepadTypeForID(instance_id: Any, /) -> Any: + """SDL_GamepadType SDL_GetGamepadTypeForID(SDL_JoystickID instance_id)""" + + @staticmethod + def SDL_GetGamepadTypeFromString(str: Any, /) -> Any: + """SDL_GamepadType SDL_GetGamepadTypeFromString(const char *str)""" + + @staticmethod + def SDL_GetGamepadVendor(gamepad: Any, /) -> Any: + """Uint16 SDL_GetGamepadVendor(SDL_Gamepad *gamepad)""" + + @staticmethod + def SDL_GetGamepadVendorForID(instance_id: Any, /) -> Any: + """Uint16 SDL_GetGamepadVendorForID(SDL_JoystickID instance_id)""" + + @staticmethod + def SDL_GetGamepads(count: Any, /) -> Any: + """SDL_JoystickID *SDL_GetGamepads(int *count)""" + + @staticmethod + def SDL_GetGlobalMouseState(x: Any, y: Any, /) -> Any: + """SDL_MouseButtonFlags SDL_GetGlobalMouseState(float *x, float *y)""" + + @staticmethod + def SDL_GetGlobalProperties() -> Any: + """SDL_PropertiesID SDL_GetGlobalProperties(void)""" + + @staticmethod + def SDL_GetGrabbedWindow() -> Any: + """SDL_Window *SDL_GetGrabbedWindow(void)""" + + @staticmethod + def SDL_GetHapticEffectStatus(haptic: Any, effect: int, /) -> bool: + """bool SDL_GetHapticEffectStatus(SDL_Haptic *haptic, int effect)""" + + @staticmethod + def SDL_GetHapticFeatures(haptic: Any, /) -> Any: + """Uint32 SDL_GetHapticFeatures(SDL_Haptic *haptic)""" + + @staticmethod + def SDL_GetHapticFromID(instance_id: Any, /) -> Any: + """SDL_Haptic *SDL_GetHapticFromID(SDL_HapticID instance_id)""" + + @staticmethod + def SDL_GetHapticID(haptic: Any, /) -> Any: + """SDL_HapticID SDL_GetHapticID(SDL_Haptic *haptic)""" + + @staticmethod + def SDL_GetHapticName(haptic: Any, /) -> Any: + """const char *SDL_GetHapticName(SDL_Haptic *haptic)""" + + @staticmethod + def SDL_GetHapticNameForID(instance_id: Any, /) -> Any: + """const char *SDL_GetHapticNameForID(SDL_HapticID instance_id)""" + + @staticmethod + def SDL_GetHaptics(count: Any, /) -> Any: + """SDL_HapticID *SDL_GetHaptics(int *count)""" + + @staticmethod + def SDL_GetHint(name: Any, /) -> Any: + """const char *SDL_GetHint(const char *name)""" + + @staticmethod + def SDL_GetHintBoolean(name: Any, default_value: bool, /) -> bool: + """bool SDL_GetHintBoolean(const char *name, bool default_value)""" + + @staticmethod + def SDL_GetIOProperties(context: Any, /) -> Any: + """SDL_PropertiesID SDL_GetIOProperties(SDL_IOStream *context)""" + + @staticmethod + def SDL_GetIOSize(context: Any, /) -> Any: + """Sint64 SDL_GetIOSize(SDL_IOStream *context)""" + + @staticmethod + def SDL_GetIOStatus(context: Any, /) -> Any: + """SDL_IOStatus SDL_GetIOStatus(SDL_IOStream *context)""" + + @staticmethod + def SDL_GetJoystickAxis(joystick: Any, axis: int, /) -> Any: + """Sint16 SDL_GetJoystickAxis(SDL_Joystick *joystick, int axis)""" + + @staticmethod + def SDL_GetJoystickAxisInitialState(joystick: Any, axis: int, state: Any, /) -> bool: + """bool SDL_GetJoystickAxisInitialState(SDL_Joystick *joystick, int axis, Sint16 *state)""" + + @staticmethod + def SDL_GetJoystickBall(joystick: Any, ball: int, dx: Any, dy: Any, /) -> bool: + """bool SDL_GetJoystickBall(SDL_Joystick *joystick, int ball, int *dx, int *dy)""" + + @staticmethod + def SDL_GetJoystickButton(joystick: Any, button: int, /) -> bool: + """bool SDL_GetJoystickButton(SDL_Joystick *joystick, int button)""" + + @staticmethod + def SDL_GetJoystickConnectionState(joystick: Any, /) -> Any: + """SDL_JoystickConnectionState SDL_GetJoystickConnectionState(SDL_Joystick *joystick)""" + + @staticmethod + def SDL_GetJoystickFirmwareVersion(joystick: Any, /) -> Any: + """Uint16 SDL_GetJoystickFirmwareVersion(SDL_Joystick *joystick)""" + + @staticmethod + def SDL_GetJoystickFromID(instance_id: Any, /) -> Any: + """SDL_Joystick *SDL_GetJoystickFromID(SDL_JoystickID instance_id)""" + + @staticmethod + def SDL_GetJoystickFromPlayerIndex(player_index: int, /) -> Any: + """SDL_Joystick *SDL_GetJoystickFromPlayerIndex(int player_index)""" + + @staticmethod + def SDL_GetJoystickGUID(joystick: Any, /) -> Any: + """SDL_GUID SDL_GetJoystickGUID(SDL_Joystick *joystick)""" + + @staticmethod + def SDL_GetJoystickGUIDForID(instance_id: Any, /) -> Any: + """SDL_GUID SDL_GetJoystickGUIDForID(SDL_JoystickID instance_id)""" + + @staticmethod + def SDL_GetJoystickGUIDInfo(guid: Any, vendor: Any, product: Any, version: Any, crc16: Any, /) -> None: + """void SDL_GetJoystickGUIDInfo(SDL_GUID guid, Uint16 *vendor, Uint16 *product, Uint16 *version, Uint16 *crc16)""" + + @staticmethod + def SDL_GetJoystickHat(joystick: Any, hat: int, /) -> Any: + """Uint8 SDL_GetJoystickHat(SDL_Joystick *joystick, int hat)""" + + @staticmethod + def SDL_GetJoystickID(joystick: Any, /) -> Any: + """SDL_JoystickID SDL_GetJoystickID(SDL_Joystick *joystick)""" + + @staticmethod + def SDL_GetJoystickName(joystick: Any, /) -> Any: + """const char *SDL_GetJoystickName(SDL_Joystick *joystick)""" + + @staticmethod + def SDL_GetJoystickNameForID(instance_id: Any, /) -> Any: + """const char *SDL_GetJoystickNameForID(SDL_JoystickID instance_id)""" + + @staticmethod + def SDL_GetJoystickPath(joystick: Any, /) -> Any: + """const char *SDL_GetJoystickPath(SDL_Joystick *joystick)""" + + @staticmethod + def SDL_GetJoystickPathForID(instance_id: Any, /) -> Any: + """const char *SDL_GetJoystickPathForID(SDL_JoystickID instance_id)""" + + @staticmethod + def SDL_GetJoystickPlayerIndex(joystick: Any, /) -> int: + """int SDL_GetJoystickPlayerIndex(SDL_Joystick *joystick)""" + + @staticmethod + def SDL_GetJoystickPlayerIndexForID(instance_id: Any, /) -> int: + """int SDL_GetJoystickPlayerIndexForID(SDL_JoystickID instance_id)""" + + @staticmethod + def SDL_GetJoystickPowerInfo(joystick: Any, percent: Any, /) -> Any: + """SDL_PowerState SDL_GetJoystickPowerInfo(SDL_Joystick *joystick, int *percent)""" + + @staticmethod + def SDL_GetJoystickProduct(joystick: Any, /) -> Any: + """Uint16 SDL_GetJoystickProduct(SDL_Joystick *joystick)""" + + @staticmethod + def SDL_GetJoystickProductForID(instance_id: Any, /) -> Any: + """Uint16 SDL_GetJoystickProductForID(SDL_JoystickID instance_id)""" + + @staticmethod + def SDL_GetJoystickProductVersion(joystick: Any, /) -> Any: + """Uint16 SDL_GetJoystickProductVersion(SDL_Joystick *joystick)""" + + @staticmethod + def SDL_GetJoystickProductVersionForID(instance_id: Any, /) -> Any: + """Uint16 SDL_GetJoystickProductVersionForID(SDL_JoystickID instance_id)""" + + @staticmethod + def SDL_GetJoystickProperties(joystick: Any, /) -> Any: + """SDL_PropertiesID SDL_GetJoystickProperties(SDL_Joystick *joystick)""" + + @staticmethod + def SDL_GetJoystickSerial(joystick: Any, /) -> Any: + """const char *SDL_GetJoystickSerial(SDL_Joystick *joystick)""" + + @staticmethod + def SDL_GetJoystickType(joystick: Any, /) -> Any: + """SDL_JoystickType SDL_GetJoystickType(SDL_Joystick *joystick)""" + + @staticmethod + def SDL_GetJoystickTypeForID(instance_id: Any, /) -> Any: + """SDL_JoystickType SDL_GetJoystickTypeForID(SDL_JoystickID instance_id)""" + + @staticmethod + def SDL_GetJoystickVendor(joystick: Any, /) -> Any: + """Uint16 SDL_GetJoystickVendor(SDL_Joystick *joystick)""" + + @staticmethod + def SDL_GetJoystickVendorForID(instance_id: Any, /) -> Any: + """Uint16 SDL_GetJoystickVendorForID(SDL_JoystickID instance_id)""" + + @staticmethod + def SDL_GetJoysticks(count: Any, /) -> Any: + """SDL_JoystickID *SDL_GetJoysticks(int *count)""" + + @staticmethod + def SDL_GetKeyFromName(name: Any, /) -> Any: + """SDL_Keycode SDL_GetKeyFromName(const char *name)""" + + @staticmethod + def SDL_GetKeyFromScancode(scancode: Any, modstate: Any, key_event: bool, /) -> Any: + """SDL_Keycode SDL_GetKeyFromScancode(SDL_Scancode scancode, SDL_Keymod modstate, bool key_event)""" + + @staticmethod + def SDL_GetKeyName(key: Any, /) -> Any: + """const char *SDL_GetKeyName(SDL_Keycode key)""" + + @staticmethod + def SDL_GetKeyboardFocus() -> Any: + """SDL_Window *SDL_GetKeyboardFocus(void)""" + + @staticmethod + def SDL_GetKeyboardNameForID(instance_id: Any, /) -> Any: + """const char *SDL_GetKeyboardNameForID(SDL_KeyboardID instance_id)""" + + @staticmethod + def SDL_GetKeyboardState(numkeys: Any, /) -> Any: + """const bool *SDL_GetKeyboardState(int *numkeys)""" + + @staticmethod + def SDL_GetKeyboards(count: Any, /) -> Any: + """SDL_KeyboardID *SDL_GetKeyboards(int *count)""" + + @staticmethod + def SDL_GetLogOutputFunction(callback: Any, userdata: Any, /) -> None: + """void SDL_GetLogOutputFunction(SDL_LogOutputFunction *callback, void **userdata)""" + + @staticmethod + def SDL_GetLogPriority(category: int, /) -> Any: + """SDL_LogPriority SDL_GetLogPriority(int category)""" + + @staticmethod + def SDL_GetMasksForPixelFormat(format: Any, bpp: Any, Rmask: Any, Gmask: Any, Bmask: Any, Amask: Any, /) -> bool: + """bool SDL_GetMasksForPixelFormat(SDL_PixelFormat format, int *bpp, Uint32 *Rmask, Uint32 *Gmask, Uint32 *Bmask, Uint32 *Amask)""" + + @staticmethod + def SDL_GetMaxHapticEffects(haptic: Any, /) -> int: + """int SDL_GetMaxHapticEffects(SDL_Haptic *haptic)""" + + @staticmethod + def SDL_GetMaxHapticEffectsPlaying(haptic: Any, /) -> int: + """int SDL_GetMaxHapticEffectsPlaying(SDL_Haptic *haptic)""" + + @staticmethod + def SDL_GetMemoryFunctions(malloc_func: Any, calloc_func: Any, realloc_func: Any, free_func: Any, /) -> None: + """void SDL_GetMemoryFunctions(SDL_malloc_func *malloc_func, SDL_calloc_func *calloc_func, SDL_realloc_func *realloc_func, SDL_free_func *free_func)""" + + @staticmethod + def SDL_GetMice(count: Any, /) -> Any: + """SDL_MouseID *SDL_GetMice(int *count)""" + + @staticmethod + def SDL_GetModState() -> Any: + """SDL_Keymod SDL_GetModState(void)""" + + @staticmethod + def SDL_GetMouseFocus() -> Any: + """SDL_Window *SDL_GetMouseFocus(void)""" + + @staticmethod + def SDL_GetMouseNameForID(instance_id: Any, /) -> Any: + """const char *SDL_GetMouseNameForID(SDL_MouseID instance_id)""" + + @staticmethod + def SDL_GetMouseState(x: Any, y: Any, /) -> Any: + """SDL_MouseButtonFlags SDL_GetMouseState(float *x, float *y)""" + + @staticmethod + def SDL_GetNaturalDisplayOrientation(displayID: Any, /) -> Any: + """SDL_DisplayOrientation SDL_GetNaturalDisplayOrientation(SDL_DisplayID displayID)""" + + @staticmethod + def SDL_GetNumAllocations() -> int: + """int SDL_GetNumAllocations(void)""" + + @staticmethod + def SDL_GetNumAudioDrivers() -> int: + """int SDL_GetNumAudioDrivers(void)""" + + @staticmethod + def SDL_GetNumCameraDrivers() -> int: + """int SDL_GetNumCameraDrivers(void)""" + + @staticmethod + def SDL_GetNumGPUDrivers() -> int: + """int SDL_GetNumGPUDrivers(void)""" + + @staticmethod + def SDL_GetNumGamepadTouchpadFingers(gamepad: Any, touchpad: int, /) -> int: + """int SDL_GetNumGamepadTouchpadFingers(SDL_Gamepad *gamepad, int touchpad)""" + + @staticmethod + def SDL_GetNumGamepadTouchpads(gamepad: Any, /) -> int: + """int SDL_GetNumGamepadTouchpads(SDL_Gamepad *gamepad)""" + + @staticmethod + def SDL_GetNumHapticAxes(haptic: Any, /) -> int: + """int SDL_GetNumHapticAxes(SDL_Haptic *haptic)""" + + @staticmethod + def SDL_GetNumJoystickAxes(joystick: Any, /) -> int: + """int SDL_GetNumJoystickAxes(SDL_Joystick *joystick)""" + + @staticmethod + def SDL_GetNumJoystickBalls(joystick: Any, /) -> int: + """int SDL_GetNumJoystickBalls(SDL_Joystick *joystick)""" + + @staticmethod + def SDL_GetNumJoystickButtons(joystick: Any, /) -> int: + """int SDL_GetNumJoystickButtons(SDL_Joystick *joystick)""" + + @staticmethod + def SDL_GetNumJoystickHats(joystick: Any, /) -> int: + """int SDL_GetNumJoystickHats(SDL_Joystick *joystick)""" + + @staticmethod + def SDL_GetNumLogicalCPUCores() -> int: + """int SDL_GetNumLogicalCPUCores(void)""" + + @staticmethod + def SDL_GetNumRenderDrivers() -> int: + """int SDL_GetNumRenderDrivers(void)""" + + @staticmethod + def SDL_GetNumVideoDrivers() -> int: + """int SDL_GetNumVideoDrivers(void)""" + + @staticmethod + def SDL_GetNumberProperty(props: Any, name: Any, default_value: Any, /) -> Any: + """Sint64 SDL_GetNumberProperty(SDL_PropertiesID props, const char *name, Sint64 default_value)""" + + @staticmethod + def SDL_GetOriginalMemoryFunctions( + malloc_func: Any, calloc_func: Any, realloc_func: Any, free_func: Any, / + ) -> None: + """void SDL_GetOriginalMemoryFunctions(SDL_malloc_func *malloc_func, SDL_calloc_func *calloc_func, SDL_realloc_func *realloc_func, SDL_free_func *free_func)""" + + @staticmethod + def SDL_GetPathInfo(path: Any, info: Any, /) -> bool: + """bool SDL_GetPathInfo(const char *path, SDL_PathInfo *info)""" + + @staticmethod + def SDL_GetPerformanceCounter() -> Any: + """Uint64 SDL_GetPerformanceCounter(void)""" + + @staticmethod + def SDL_GetPerformanceFrequency() -> Any: + """Uint64 SDL_GetPerformanceFrequency(void)""" + + @staticmethod + def SDL_GetPixelFormatDetails(format: Any, /) -> Any: + """const SDL_PixelFormatDetails *SDL_GetPixelFormatDetails(SDL_PixelFormat format)""" + + @staticmethod + def SDL_GetPixelFormatForMasks(bpp: int, Rmask: Any, Gmask: Any, Bmask: Any, Amask: Any, /) -> Any: + """SDL_PixelFormat SDL_GetPixelFormatForMasks(int bpp, Uint32 Rmask, Uint32 Gmask, Uint32 Bmask, Uint32 Amask)""" + + @staticmethod + def SDL_GetPixelFormatName(format: Any, /) -> Any: + """const char *SDL_GetPixelFormatName(SDL_PixelFormat format)""" + + @staticmethod + def SDL_GetPlatform() -> Any: + """const char *SDL_GetPlatform(void)""" + + @staticmethod + def SDL_GetPointerProperty(props: Any, name: Any, default_value: Any, /) -> Any: + """void *SDL_GetPointerProperty(SDL_PropertiesID props, const char *name, void *default_value)""" + + @staticmethod + def SDL_GetPowerInfo(seconds: Any, percent: Any, /) -> Any: + """SDL_PowerState SDL_GetPowerInfo(int *seconds, int *percent)""" + + @staticmethod + def SDL_GetPrefPath(org: Any, app: Any, /) -> Any: + """char *SDL_GetPrefPath(const char *org, const char *app)""" + + @staticmethod + def SDL_GetPreferredLocales(count: Any, /) -> Any: + """SDL_Locale **SDL_GetPreferredLocales(int *count)""" + + @staticmethod + def SDL_GetPrimaryDisplay() -> Any: + """SDL_DisplayID SDL_GetPrimaryDisplay(void)""" + + @staticmethod + def SDL_GetPrimarySelectionText() -> Any: + """char *SDL_GetPrimarySelectionText(void)""" + + @staticmethod + def SDL_GetProcessInput(process: Any, /) -> Any: + """SDL_IOStream *SDL_GetProcessInput(SDL_Process *process)""" + + @staticmethod + def SDL_GetProcessOutput(process: Any, /) -> Any: + """SDL_IOStream *SDL_GetProcessOutput(SDL_Process *process)""" + + @staticmethod + def SDL_GetProcessProperties(process: Any, /) -> Any: + """SDL_PropertiesID SDL_GetProcessProperties(SDL_Process *process)""" + + @staticmethod + def SDL_GetPropertyType(props: Any, name: Any, /) -> Any: + """SDL_PropertyType SDL_GetPropertyType(SDL_PropertiesID props, const char *name)""" + + @staticmethod + def SDL_GetRGB(pixel: Any, format: Any, palette: Any, r: Any, g: Any, b: Any, /) -> None: + """void SDL_GetRGB(Uint32 pixel, const SDL_PixelFormatDetails *format, const SDL_Palette *palette, Uint8 *r, Uint8 *g, Uint8 *b)""" + + @staticmethod + def SDL_GetRGBA(pixel: Any, format: Any, palette: Any, r: Any, g: Any, b: Any, a: Any, /) -> None: + """void SDL_GetRGBA(Uint32 pixel, const SDL_PixelFormatDetails *format, const SDL_Palette *palette, Uint8 *r, Uint8 *g, Uint8 *b, Uint8 *a)""" + + @staticmethod + def SDL_GetRealGamepadType(gamepad: Any, /) -> Any: + """SDL_GamepadType SDL_GetRealGamepadType(SDL_Gamepad *gamepad)""" + + @staticmethod + def SDL_GetRealGamepadTypeForID(instance_id: Any, /) -> Any: + """SDL_GamepadType SDL_GetRealGamepadTypeForID(SDL_JoystickID instance_id)""" + + @staticmethod + def SDL_GetRectAndLineIntersection(rect: Any, X1: Any, Y1: Any, X2: Any, Y2: Any, /) -> bool: + """bool SDL_GetRectAndLineIntersection(const SDL_Rect *rect, int *X1, int *Y1, int *X2, int *Y2)""" + + @staticmethod + def SDL_GetRectAndLineIntersectionFloat(rect: Any, X1: Any, Y1: Any, X2: Any, Y2: Any, /) -> bool: + """bool SDL_GetRectAndLineIntersectionFloat(const SDL_FRect *rect, float *X1, float *Y1, float *X2, float *Y2)""" + + @staticmethod + def SDL_GetRectEnclosingPoints(points: Any, count: int, clip: Any, result: Any, /) -> bool: + """bool SDL_GetRectEnclosingPoints(const SDL_Point *points, int count, const SDL_Rect *clip, SDL_Rect *result)""" + + @staticmethod + def SDL_GetRectEnclosingPointsFloat(points: Any, count: int, clip: Any, result: Any, /) -> bool: + """bool SDL_GetRectEnclosingPointsFloat(const SDL_FPoint *points, int count, const SDL_FRect *clip, SDL_FRect *result)""" + + @staticmethod + def SDL_GetRectIntersection(A: Any, B: Any, result: Any, /) -> bool: + """bool SDL_GetRectIntersection(const SDL_Rect *A, const SDL_Rect *B, SDL_Rect *result)""" + + @staticmethod + def SDL_GetRectIntersectionFloat(A: Any, B: Any, result: Any, /) -> bool: + """bool SDL_GetRectIntersectionFloat(const SDL_FRect *A, const SDL_FRect *B, SDL_FRect *result)""" + + @staticmethod + def SDL_GetRectUnion(A: Any, B: Any, result: Any, /) -> bool: + """bool SDL_GetRectUnion(const SDL_Rect *A, const SDL_Rect *B, SDL_Rect *result)""" + + @staticmethod + def SDL_GetRectUnionFloat(A: Any, B: Any, result: Any, /) -> bool: + """bool SDL_GetRectUnionFloat(const SDL_FRect *A, const SDL_FRect *B, SDL_FRect *result)""" + + @staticmethod + def SDL_GetRelativeMouseState(x: Any, y: Any, /) -> Any: + """SDL_MouseButtonFlags SDL_GetRelativeMouseState(float *x, float *y)""" + + @staticmethod + def SDL_GetRenderClipRect(renderer: Any, rect: Any, /) -> bool: + """bool SDL_GetRenderClipRect(SDL_Renderer *renderer, SDL_Rect *rect)""" + + @staticmethod + def SDL_GetRenderColorScale(renderer: Any, scale: Any, /) -> bool: + """bool SDL_GetRenderColorScale(SDL_Renderer *renderer, float *scale)""" + + @staticmethod + def SDL_GetRenderDrawBlendMode(renderer: Any, blendMode: Any, /) -> bool: + """bool SDL_GetRenderDrawBlendMode(SDL_Renderer *renderer, SDL_BlendMode *blendMode)""" + + @staticmethod + def SDL_GetRenderDrawColor(renderer: Any, r: Any, g: Any, b: Any, a: Any, /) -> bool: + """bool SDL_GetRenderDrawColor(SDL_Renderer *renderer, Uint8 *r, Uint8 *g, Uint8 *b, Uint8 *a)""" + + @staticmethod + def SDL_GetRenderDrawColorFloat(renderer: Any, r: Any, g: Any, b: Any, a: Any, /) -> bool: + """bool SDL_GetRenderDrawColorFloat(SDL_Renderer *renderer, float *r, float *g, float *b, float *a)""" + + @staticmethod + def SDL_GetRenderDriver(index: int, /) -> Any: + """const char *SDL_GetRenderDriver(int index)""" + + @staticmethod + def SDL_GetRenderLogicalPresentation(renderer: Any, w: Any, h: Any, mode: Any, /) -> bool: + """bool SDL_GetRenderLogicalPresentation(SDL_Renderer *renderer, int *w, int *h, SDL_RendererLogicalPresentation *mode)""" + + @staticmethod + def SDL_GetRenderLogicalPresentationRect(renderer: Any, rect: Any, /) -> bool: + """bool SDL_GetRenderLogicalPresentationRect(SDL_Renderer *renderer, SDL_FRect *rect)""" + + @staticmethod + def SDL_GetRenderMetalCommandEncoder(renderer: Any, /) -> Any: + """void *SDL_GetRenderMetalCommandEncoder(SDL_Renderer *renderer)""" + + @staticmethod + def SDL_GetRenderMetalLayer(renderer: Any, /) -> Any: + """void *SDL_GetRenderMetalLayer(SDL_Renderer *renderer)""" + + @staticmethod + def SDL_GetRenderOutputSize(renderer: Any, w: Any, h: Any, /) -> bool: + """bool SDL_GetRenderOutputSize(SDL_Renderer *renderer, int *w, int *h)""" + + @staticmethod + def SDL_GetRenderSafeArea(renderer: Any, rect: Any, /) -> bool: + """bool SDL_GetRenderSafeArea(SDL_Renderer *renderer, SDL_Rect *rect)""" + + @staticmethod + def SDL_GetRenderScale(renderer: Any, scaleX: Any, scaleY: Any, /) -> bool: + """bool SDL_GetRenderScale(SDL_Renderer *renderer, float *scaleX, float *scaleY)""" + + @staticmethod + def SDL_GetRenderTarget(renderer: Any, /) -> Any: + """SDL_Texture *SDL_GetRenderTarget(SDL_Renderer *renderer)""" + + @staticmethod + def SDL_GetRenderVSync(renderer: Any, vsync: Any, /) -> bool: + """bool SDL_GetRenderVSync(SDL_Renderer *renderer, int *vsync)""" + + @staticmethod + def SDL_GetRenderViewport(renderer: Any, rect: Any, /) -> bool: + """bool SDL_GetRenderViewport(SDL_Renderer *renderer, SDL_Rect *rect)""" + + @staticmethod + def SDL_GetRenderWindow(renderer: Any, /) -> Any: + """SDL_Window *SDL_GetRenderWindow(SDL_Renderer *renderer)""" + + @staticmethod + def SDL_GetRenderer(window: Any, /) -> Any: + """SDL_Renderer *SDL_GetRenderer(SDL_Window *window)""" + + @staticmethod + def SDL_GetRendererFromTexture(texture: Any, /) -> Any: + """SDL_Renderer *SDL_GetRendererFromTexture(SDL_Texture *texture)""" + + @staticmethod + def SDL_GetRendererName(renderer: Any, /) -> Any: + """const char *SDL_GetRendererName(SDL_Renderer *renderer)""" + + @staticmethod + def SDL_GetRendererProperties(renderer: Any, /) -> Any: + """SDL_PropertiesID SDL_GetRendererProperties(SDL_Renderer *renderer)""" + + @staticmethod + def SDL_GetRevision() -> Any: + """const char *SDL_GetRevision(void)""" + + @staticmethod + def SDL_GetSIMDAlignment() -> int: + """size_t SDL_GetSIMDAlignment(void)""" + + @staticmethod + def SDL_GetSandbox() -> Any: + """SDL_Sandbox SDL_GetSandbox(void)""" + + @staticmethod + def SDL_GetScancodeFromKey(key: Any, modstate: Any, /) -> Any: + """SDL_Scancode SDL_GetScancodeFromKey(SDL_Keycode key, SDL_Keymod *modstate)""" + + @staticmethod + def SDL_GetScancodeFromName(name: Any, /) -> Any: + """SDL_Scancode SDL_GetScancodeFromName(const char *name)""" + + @staticmethod + def SDL_GetScancodeName(scancode: Any, /) -> Any: + """const char *SDL_GetScancodeName(SDL_Scancode scancode)""" + + @staticmethod + def SDL_GetSemaphoreValue(sem: Any, /) -> Any: + """Uint32 SDL_GetSemaphoreValue(SDL_Semaphore *sem)""" + + @staticmethod + def SDL_GetSensorData(sensor: Any, data: Any, num_values: int, /) -> bool: + """bool SDL_GetSensorData(SDL_Sensor *sensor, float *data, int num_values)""" + + @staticmethod + def SDL_GetSensorFromID(instance_id: Any, /) -> Any: + """SDL_Sensor *SDL_GetSensorFromID(SDL_SensorID instance_id)""" + + @staticmethod + def SDL_GetSensorID(sensor: Any, /) -> Any: + """SDL_SensorID SDL_GetSensorID(SDL_Sensor *sensor)""" + + @staticmethod + def SDL_GetSensorName(sensor: Any, /) -> Any: + """const char *SDL_GetSensorName(SDL_Sensor *sensor)""" + + @staticmethod + def SDL_GetSensorNameForID(instance_id: Any, /) -> Any: + """const char *SDL_GetSensorNameForID(SDL_SensorID instance_id)""" + + @staticmethod + def SDL_GetSensorNonPortableType(sensor: Any, /) -> int: + """int SDL_GetSensorNonPortableType(SDL_Sensor *sensor)""" + + @staticmethod + def SDL_GetSensorNonPortableTypeForID(instance_id: Any, /) -> int: + """int SDL_GetSensorNonPortableTypeForID(SDL_SensorID instance_id)""" + + @staticmethod + def SDL_GetSensorProperties(sensor: Any, /) -> Any: + """SDL_PropertiesID SDL_GetSensorProperties(SDL_Sensor *sensor)""" + + @staticmethod + def SDL_GetSensorType(sensor: Any, /) -> Any: + """SDL_SensorType SDL_GetSensorType(SDL_Sensor *sensor)""" + + @staticmethod + def SDL_GetSensorTypeForID(instance_id: Any, /) -> Any: + """SDL_SensorType SDL_GetSensorTypeForID(SDL_SensorID instance_id)""" + + @staticmethod + def SDL_GetSensors(count: Any, /) -> Any: + """SDL_SensorID *SDL_GetSensors(int *count)""" + + @staticmethod + def SDL_GetSilenceValueForFormat(format: Any, /) -> int: + """int SDL_GetSilenceValueForFormat(SDL_AudioFormat format)""" + + @staticmethod + def SDL_GetStorageFileSize(storage: Any, path: Any, length: Any, /) -> bool: + """bool SDL_GetStorageFileSize(SDL_Storage *storage, const char *path, Uint64 *length)""" + + @staticmethod + def SDL_GetStoragePathInfo(storage: Any, path: Any, info: Any, /) -> bool: + """bool SDL_GetStoragePathInfo(SDL_Storage *storage, const char *path, SDL_PathInfo *info)""" + + @staticmethod + def SDL_GetStorageSpaceRemaining(storage: Any, /) -> Any: + """Uint64 SDL_GetStorageSpaceRemaining(SDL_Storage *storage)""" + + @staticmethod + def SDL_GetStringProperty(props: Any, name: Any, default_value: Any, /) -> Any: + """const char *SDL_GetStringProperty(SDL_PropertiesID props, const char *name, const char *default_value)""" + + @staticmethod + def SDL_GetSurfaceAlphaMod(surface: Any, alpha: Any, /) -> bool: + """bool SDL_GetSurfaceAlphaMod(SDL_Surface *surface, Uint8 *alpha)""" + + @staticmethod + def SDL_GetSurfaceBlendMode(surface: Any, blendMode: Any, /) -> bool: + """bool SDL_GetSurfaceBlendMode(SDL_Surface *surface, SDL_BlendMode *blendMode)""" + + @staticmethod + def SDL_GetSurfaceClipRect(surface: Any, rect: Any, /) -> bool: + """bool SDL_GetSurfaceClipRect(SDL_Surface *surface, SDL_Rect *rect)""" + + @staticmethod + def SDL_GetSurfaceColorKey(surface: Any, key: Any, /) -> bool: + """bool SDL_GetSurfaceColorKey(SDL_Surface *surface, Uint32 *key)""" + + @staticmethod + def SDL_GetSurfaceColorMod(surface: Any, r: Any, g: Any, b: Any, /) -> bool: + """bool SDL_GetSurfaceColorMod(SDL_Surface *surface, Uint8 *r, Uint8 *g, Uint8 *b)""" + + @staticmethod + def SDL_GetSurfaceColorspace(surface: Any, /) -> Any: + """SDL_Colorspace SDL_GetSurfaceColorspace(SDL_Surface *surface)""" + + @staticmethod + def SDL_GetSurfaceImages(surface: Any, count: Any, /) -> Any: + """SDL_Surface **SDL_GetSurfaceImages(SDL_Surface *surface, int *count)""" + + @staticmethod + def SDL_GetSurfacePalette(surface: Any, /) -> Any: + """SDL_Palette *SDL_GetSurfacePalette(SDL_Surface *surface)""" + + @staticmethod + def SDL_GetSurfaceProperties(surface: Any, /) -> Any: + """SDL_PropertiesID SDL_GetSurfaceProperties(SDL_Surface *surface)""" + + @staticmethod + def SDL_GetSystemRAM() -> int: + """int SDL_GetSystemRAM(void)""" + + @staticmethod + def SDL_GetSystemTheme() -> Any: + """SDL_SystemTheme SDL_GetSystemTheme(void)""" + + @staticmethod + def SDL_GetTLS(id: Any, /) -> Any: + """void *SDL_GetTLS(SDL_TLSID *id)""" + + @staticmethod + def SDL_GetTextInputArea(window: Any, rect: Any, cursor: Any, /) -> bool: + """bool SDL_GetTextInputArea(SDL_Window *window, SDL_Rect *rect, int *cursor)""" + + @staticmethod + def SDL_GetTextureAlphaMod(texture: Any, alpha: Any, /) -> bool: + """bool SDL_GetTextureAlphaMod(SDL_Texture *texture, Uint8 *alpha)""" + + @staticmethod + def SDL_GetTextureAlphaModFloat(texture: Any, alpha: Any, /) -> bool: + """bool SDL_GetTextureAlphaModFloat(SDL_Texture *texture, float *alpha)""" + + @staticmethod + def SDL_GetTextureBlendMode(texture: Any, blendMode: Any, /) -> bool: + """bool SDL_GetTextureBlendMode(SDL_Texture *texture, SDL_BlendMode *blendMode)""" + + @staticmethod + def SDL_GetTextureColorMod(texture: Any, r: Any, g: Any, b: Any, /) -> bool: + """bool SDL_GetTextureColorMod(SDL_Texture *texture, Uint8 *r, Uint8 *g, Uint8 *b)""" + + @staticmethod + def SDL_GetTextureColorModFloat(texture: Any, r: Any, g: Any, b: Any, /) -> bool: + """bool SDL_GetTextureColorModFloat(SDL_Texture *texture, float *r, float *g, float *b)""" + + @staticmethod + def SDL_GetTextureProperties(texture: Any, /) -> Any: + """SDL_PropertiesID SDL_GetTextureProperties(SDL_Texture *texture)""" + + @staticmethod + def SDL_GetTextureScaleMode(texture: Any, scaleMode: Any, /) -> bool: + """bool SDL_GetTextureScaleMode(SDL_Texture *texture, SDL_ScaleMode *scaleMode)""" + + @staticmethod + def SDL_GetTextureSize(texture: Any, w: Any, h: Any, /) -> bool: + """bool SDL_GetTextureSize(SDL_Texture *texture, float *w, float *h)""" + + @staticmethod + def SDL_GetThreadID(thread: Any, /) -> Any: + """SDL_ThreadID SDL_GetThreadID(SDL_Thread *thread)""" + + @staticmethod + def SDL_GetThreadName(thread: Any, /) -> Any: + """const char *SDL_GetThreadName(SDL_Thread *thread)""" + + @staticmethod + def SDL_GetThreadState(thread: Any, /) -> Any: + """SDL_ThreadState SDL_GetThreadState(SDL_Thread *thread)""" + + @staticmethod + def SDL_GetTicks() -> Any: + """Uint64 SDL_GetTicks(void)""" + + @staticmethod + def SDL_GetTicksNS() -> Any: + """Uint64 SDL_GetTicksNS(void)""" + + @staticmethod + def SDL_GetTouchDeviceName(touchID: Any, /) -> Any: + """const char *SDL_GetTouchDeviceName(SDL_TouchID touchID)""" + + @staticmethod + def SDL_GetTouchDeviceType(touchID: Any, /) -> Any: + """SDL_TouchDeviceType SDL_GetTouchDeviceType(SDL_TouchID touchID)""" + + @staticmethod + def SDL_GetTouchDevices(count: Any, /) -> Any: + """SDL_TouchID *SDL_GetTouchDevices(int *count)""" + + @staticmethod + def SDL_GetTouchFingers(touchID: Any, count: Any, /) -> Any: + """SDL_Finger **SDL_GetTouchFingers(SDL_TouchID touchID, int *count)""" + + @staticmethod + def SDL_GetTrayEntries(menu: Any, count: Any, /) -> Any: + """const SDL_TrayEntry **SDL_GetTrayEntries(SDL_TrayMenu *menu, int *count)""" + + @staticmethod + def SDL_GetTrayEntryChecked(entry: Any, /) -> bool: + """bool SDL_GetTrayEntryChecked(SDL_TrayEntry *entry)""" + + @staticmethod + def SDL_GetTrayEntryEnabled(entry: Any, /) -> bool: + """bool SDL_GetTrayEntryEnabled(SDL_TrayEntry *entry)""" + + @staticmethod + def SDL_GetTrayEntryLabel(entry: Any, /) -> Any: + """const char *SDL_GetTrayEntryLabel(SDL_TrayEntry *entry)""" + + @staticmethod + def SDL_GetTrayEntryParent(entry: Any, /) -> Any: + """SDL_TrayMenu *SDL_GetTrayEntryParent(SDL_TrayEntry *entry)""" + + @staticmethod + def SDL_GetTrayMenu(tray: Any, /) -> Any: + """SDL_TrayMenu *SDL_GetTrayMenu(SDL_Tray *tray)""" + + @staticmethod + def SDL_GetTrayMenuParentEntry(menu: Any, /) -> Any: + """SDL_TrayEntry *SDL_GetTrayMenuParentEntry(SDL_TrayMenu *menu)""" + + @staticmethod + def SDL_GetTrayMenuParentTray(menu: Any, /) -> Any: + """SDL_Tray *SDL_GetTrayMenuParentTray(SDL_TrayMenu *menu)""" + + @staticmethod + def SDL_GetTraySubmenu(entry: Any, /) -> Any: + """SDL_TrayMenu *SDL_GetTraySubmenu(SDL_TrayEntry *entry)""" + + @staticmethod + def SDL_GetUserFolder(folder: Any, /) -> Any: + """const char *SDL_GetUserFolder(SDL_Folder folder)""" + + @staticmethod + def SDL_GetVersion() -> int: + """int SDL_GetVersion(void)""" + + @staticmethod + def SDL_GetVideoDriver(index: int, /) -> Any: + """const char *SDL_GetVideoDriver(int index)""" + + @staticmethod + def SDL_GetWindowAspectRatio(window: Any, min_aspect: Any, max_aspect: Any, /) -> bool: + """bool SDL_GetWindowAspectRatio(SDL_Window *window, float *min_aspect, float *max_aspect)""" + + @staticmethod + def SDL_GetWindowBordersSize(window: Any, top: Any, left: Any, bottom: Any, right: Any, /) -> bool: + """bool SDL_GetWindowBordersSize(SDL_Window *window, int *top, int *left, int *bottom, int *right)""" + + @staticmethod + def SDL_GetWindowDisplayScale(window: Any, /) -> float: + """float SDL_GetWindowDisplayScale(SDL_Window *window)""" + + @staticmethod + def SDL_GetWindowFlags(window: Any, /) -> Any: + """SDL_WindowFlags SDL_GetWindowFlags(SDL_Window *window)""" + + @staticmethod + def SDL_GetWindowFromEvent(event: Any, /) -> Any: + """SDL_Window *SDL_GetWindowFromEvent(const SDL_Event *event)""" + + @staticmethod + def SDL_GetWindowFromID(id: Any, /) -> Any: + """SDL_Window *SDL_GetWindowFromID(SDL_WindowID id)""" + + @staticmethod + def SDL_GetWindowFullscreenMode(window: Any, /) -> Any: + """const SDL_DisplayMode *SDL_GetWindowFullscreenMode(SDL_Window *window)""" + + @staticmethod + def SDL_GetWindowICCProfile(window: Any, size: Any, /) -> Any: + """void *SDL_GetWindowICCProfile(SDL_Window *window, size_t *size)""" + + @staticmethod + def SDL_GetWindowID(window: Any, /) -> Any: + """SDL_WindowID SDL_GetWindowID(SDL_Window *window)""" + + @staticmethod + def SDL_GetWindowKeyboardGrab(window: Any, /) -> bool: + """bool SDL_GetWindowKeyboardGrab(SDL_Window *window)""" + + @staticmethod + def SDL_GetWindowMaximumSize(window: Any, w: Any, h: Any, /) -> bool: + """bool SDL_GetWindowMaximumSize(SDL_Window *window, int *w, int *h)""" + + @staticmethod + def SDL_GetWindowMinimumSize(window: Any, w: Any, h: Any, /) -> bool: + """bool SDL_GetWindowMinimumSize(SDL_Window *window, int *w, int *h)""" + + @staticmethod + def SDL_GetWindowMouseGrab(window: Any, /) -> bool: + """bool SDL_GetWindowMouseGrab(SDL_Window *window)""" + + @staticmethod + def SDL_GetWindowMouseRect(window: Any, /) -> Any: + """const SDL_Rect *SDL_GetWindowMouseRect(SDL_Window *window)""" + + @staticmethod + def SDL_GetWindowOpacity(window: Any, /) -> float: + """float SDL_GetWindowOpacity(SDL_Window *window)""" + + @staticmethod + def SDL_GetWindowParent(window: Any, /) -> Any: + """SDL_Window *SDL_GetWindowParent(SDL_Window *window)""" + + @staticmethod + def SDL_GetWindowPixelDensity(window: Any, /) -> float: + """float SDL_GetWindowPixelDensity(SDL_Window *window)""" + + @staticmethod + def SDL_GetWindowPixelFormat(window: Any, /) -> Any: + """SDL_PixelFormat SDL_GetWindowPixelFormat(SDL_Window *window)""" + + @staticmethod + def SDL_GetWindowPosition(window: Any, x: Any, y: Any, /) -> bool: + """bool SDL_GetWindowPosition(SDL_Window *window, int *x, int *y)""" + + @staticmethod + def SDL_GetWindowProperties(window: Any, /) -> Any: + """SDL_PropertiesID SDL_GetWindowProperties(SDL_Window *window)""" + + @staticmethod + def SDL_GetWindowRelativeMouseMode(window: Any, /) -> bool: + """bool SDL_GetWindowRelativeMouseMode(SDL_Window *window)""" + + @staticmethod + def SDL_GetWindowSafeArea(window: Any, rect: Any, /) -> bool: + """bool SDL_GetWindowSafeArea(SDL_Window *window, SDL_Rect *rect)""" + + @staticmethod + def SDL_GetWindowSize(window: Any, w: Any, h: Any, /) -> bool: + """bool SDL_GetWindowSize(SDL_Window *window, int *w, int *h)""" + + @staticmethod + def SDL_GetWindowSizeInPixels(window: Any, w: Any, h: Any, /) -> bool: + """bool SDL_GetWindowSizeInPixels(SDL_Window *window, int *w, int *h)""" + + @staticmethod + def SDL_GetWindowSurface(window: Any, /) -> Any: + """SDL_Surface *SDL_GetWindowSurface(SDL_Window *window)""" + + @staticmethod + def SDL_GetWindowSurfaceVSync(window: Any, vsync: Any, /) -> bool: + """bool SDL_GetWindowSurfaceVSync(SDL_Window *window, int *vsync)""" + + @staticmethod + def SDL_GetWindowTitle(window: Any, /) -> Any: + """const char *SDL_GetWindowTitle(SDL_Window *window)""" + + @staticmethod + def SDL_GetWindows(count: Any, /) -> Any: + """SDL_Window **SDL_GetWindows(int *count)""" + + @staticmethod + def SDL_GlobDirectory(path: Any, pattern: Any, flags: Any, count: Any, /) -> Any: + """char **SDL_GlobDirectory(const char *path, const char *pattern, SDL_GlobFlags flags, int *count)""" + + @staticmethod + def SDL_GlobStorageDirectory(storage: Any, path: Any, pattern: Any, flags: Any, count: Any, /) -> Any: + """char **SDL_GlobStorageDirectory(SDL_Storage *storage, const char *path, const char *pattern, SDL_GlobFlags flags, int *count)""" + + @staticmethod + def SDL_HapticEffectSupported(haptic: Any, effect: Any, /) -> bool: + """bool SDL_HapticEffectSupported(SDL_Haptic *haptic, const SDL_HapticEffect *effect)""" + + @staticmethod + def SDL_HapticRumbleSupported(haptic: Any, /) -> bool: + """bool SDL_HapticRumbleSupported(SDL_Haptic *haptic)""" + + @staticmethod + def SDL_HasARMSIMD() -> bool: + """bool SDL_HasARMSIMD(void)""" + + @staticmethod + def SDL_HasAVX() -> bool: + """bool SDL_HasAVX(void)""" + + @staticmethod + def SDL_HasAVX2() -> bool: + """bool SDL_HasAVX2(void)""" + + @staticmethod + def SDL_HasAVX512F() -> bool: + """bool SDL_HasAVX512F(void)""" + + @staticmethod + def SDL_HasAltiVec() -> bool: + """bool SDL_HasAltiVec(void)""" + + @staticmethod + def SDL_HasClipboardData(mime_type: Any, /) -> bool: + """bool SDL_HasClipboardData(const char *mime_type)""" + + @staticmethod + def SDL_HasClipboardText() -> bool: + """bool SDL_HasClipboardText(void)""" + + @staticmethod + def SDL_HasEvent(type: Any, /) -> bool: + """bool SDL_HasEvent(Uint32 type)""" + + @staticmethod + def SDL_HasEvents(minType: Any, maxType: Any, /) -> bool: + """bool SDL_HasEvents(Uint32 minType, Uint32 maxType)""" + + @staticmethod + def SDL_HasGamepad() -> bool: + """bool SDL_HasGamepad(void)""" + + @staticmethod + def SDL_HasJoystick() -> bool: + """bool SDL_HasJoystick(void)""" + + @staticmethod + def SDL_HasKeyboard() -> bool: + """bool SDL_HasKeyboard(void)""" + + @staticmethod + def SDL_HasLASX() -> bool: + """bool SDL_HasLASX(void)""" + + @staticmethod + def SDL_HasLSX() -> bool: + """bool SDL_HasLSX(void)""" + + @staticmethod + def SDL_HasMMX() -> bool: + """bool SDL_HasMMX(void)""" + + @staticmethod + def SDL_HasMouse() -> bool: + """bool SDL_HasMouse(void)""" + + @staticmethod + def SDL_HasNEON() -> bool: + """bool SDL_HasNEON(void)""" + + @staticmethod + def SDL_HasPrimarySelectionText() -> bool: + """bool SDL_HasPrimarySelectionText(void)""" + + @staticmethod + def SDL_HasProperty(props: Any, name: Any, /) -> bool: + """bool SDL_HasProperty(SDL_PropertiesID props, const char *name)""" + + @staticmethod + def SDL_HasRectIntersection(A: Any, B: Any, /) -> bool: + """bool SDL_HasRectIntersection(const SDL_Rect *A, const SDL_Rect *B)""" + + @staticmethod + def SDL_HasRectIntersectionFloat(A: Any, B: Any, /) -> bool: + """bool SDL_HasRectIntersectionFloat(const SDL_FRect *A, const SDL_FRect *B)""" + + @staticmethod + def SDL_HasSSE() -> bool: + """bool SDL_HasSSE(void)""" + + @staticmethod + def SDL_HasSSE2() -> bool: + """bool SDL_HasSSE2(void)""" + + @staticmethod + def SDL_HasSSE3() -> bool: + """bool SDL_HasSSE3(void)""" + + @staticmethod + def SDL_HasSSE41() -> bool: + """bool SDL_HasSSE41(void)""" + + @staticmethod + def SDL_HasSSE42() -> bool: + """bool SDL_HasSSE42(void)""" + + @staticmethod + def SDL_HasScreenKeyboardSupport() -> bool: + """bool SDL_HasScreenKeyboardSupport(void)""" + + @staticmethod + def SDL_HideCursor() -> bool: + """bool SDL_HideCursor(void)""" + + @staticmethod + def SDL_HideWindow(window: Any, /) -> bool: + """bool SDL_HideWindow(SDL_Window *window)""" + + @staticmethod + def SDL_IOFromConstMem(mem: Any, size: int, /) -> Any: + """SDL_IOStream *SDL_IOFromConstMem(const void *mem, size_t size)""" + + @staticmethod + def SDL_IOFromDynamicMem() -> Any: + """SDL_IOStream *SDL_IOFromDynamicMem(void)""" + + @staticmethod + def SDL_IOFromFile(file: Any, mode: Any, /) -> Any: + """SDL_IOStream *SDL_IOFromFile(const char *file, const char *mode)""" + + @staticmethod + def SDL_IOFromMem(mem: Any, size: int, /) -> Any: + """SDL_IOStream *SDL_IOFromMem(void *mem, size_t size)""" + + @staticmethod + def SDL_IOprintf(context: Any, fmt: Any, /, *__args: Any) -> int: + """size_t SDL_IOprintf(SDL_IOStream *context, const char *fmt, ...)""" + + @staticmethod + def SDL_Init(flags: Any, /) -> bool: + """bool SDL_Init(SDL_InitFlags flags)""" + + @staticmethod + def SDL_InitHapticRumble(haptic: Any, /) -> bool: + """bool SDL_InitHapticRumble(SDL_Haptic *haptic)""" + + @staticmethod + def SDL_InitSubSystem(flags: Any, /) -> bool: + """bool SDL_InitSubSystem(SDL_InitFlags flags)""" + + @staticmethod + def SDL_InsertGPUDebugLabel(command_buffer: Any, text: Any, /) -> None: + """void SDL_InsertGPUDebugLabel(SDL_GPUCommandBuffer *command_buffer, const char *text)""" + + @staticmethod + def SDL_InsertTrayEntryAt(menu: Any, pos: int, label: Any, flags: Any, /) -> Any: + """SDL_TrayEntry *SDL_InsertTrayEntryAt(SDL_TrayMenu *menu, int pos, const char *label, SDL_TrayEntryFlags flags)""" + + @staticmethod + def SDL_IsAudioDevicePhysical(devid: Any, /) -> bool: + """bool SDL_IsAudioDevicePhysical(SDL_AudioDeviceID devid)""" + + @staticmethod + def SDL_IsAudioDevicePlayback(devid: Any, /) -> bool: + """bool SDL_IsAudioDevicePlayback(SDL_AudioDeviceID devid)""" + + @staticmethod + def SDL_IsGamepad(instance_id: Any, /) -> bool: + """bool SDL_IsGamepad(SDL_JoystickID instance_id)""" + + @staticmethod + def SDL_IsJoystickHaptic(joystick: Any, /) -> bool: + """bool SDL_IsJoystickHaptic(SDL_Joystick *joystick)""" + + @staticmethod + def SDL_IsJoystickVirtual(instance_id: Any, /) -> bool: + """bool SDL_IsJoystickVirtual(SDL_JoystickID instance_id)""" + + @staticmethod + def SDL_IsMainThread() -> bool: + """bool SDL_IsMainThread(void)""" + + @staticmethod + def SDL_IsMouseHaptic() -> bool: + """bool SDL_IsMouseHaptic(void)""" + + @staticmethod + def SDL_IsTV() -> bool: + """bool SDL_IsTV(void)""" + + @staticmethod + def SDL_IsTablet() -> bool: + """bool SDL_IsTablet(void)""" + + @staticmethod + def SDL_JoystickConnected(joystick: Any, /) -> bool: + """bool SDL_JoystickConnected(SDL_Joystick *joystick)""" + + @staticmethod + def SDL_JoystickEventsEnabled() -> bool: + """bool SDL_JoystickEventsEnabled(void)""" + + @staticmethod + def SDL_KillProcess(process: Any, force: bool, /) -> bool: + """bool SDL_KillProcess(SDL_Process *process, bool force)""" + + @staticmethod + def SDL_LoadBMP(file: Any, /) -> Any: + """SDL_Surface *SDL_LoadBMP(const char *file)""" + + @staticmethod + def SDL_LoadBMP_IO(src: Any, closeio: bool, /) -> Any: + """SDL_Surface *SDL_LoadBMP_IO(SDL_IOStream *src, bool closeio)""" + + @staticmethod + def SDL_LoadFile(file: Any, datasize: Any, /) -> Any: + """void *SDL_LoadFile(const char *file, size_t *datasize)""" + + @staticmethod + def SDL_LoadFileAsync(file: Any, queue: Any, userdata: Any, /) -> bool: + """bool SDL_LoadFileAsync(const char *file, SDL_AsyncIOQueue *queue, void *userdata)""" + + @staticmethod + def SDL_LoadFile_IO(src: Any, datasize: Any, closeio: bool, /) -> Any: + """void *SDL_LoadFile_IO(SDL_IOStream *src, size_t *datasize, bool closeio)""" + + @staticmethod + def SDL_LoadFunction(handle: Any, name: Any, /) -> Any: + """SDL_FunctionPointer SDL_LoadFunction(SDL_SharedObject *handle, const char *name)""" + + @staticmethod + def SDL_LoadObject(sofile: Any, /) -> Any: + """SDL_SharedObject *SDL_LoadObject(const char *sofile)""" + + @staticmethod + def SDL_LoadWAV(path: Any, spec: Any, audio_buf: Any, audio_len: Any, /) -> bool: + """bool SDL_LoadWAV(const char *path, SDL_AudioSpec *spec, Uint8 **audio_buf, Uint32 *audio_len)""" + + @staticmethod + def SDL_LoadWAV_IO(src: Any, closeio: bool, spec: Any, audio_buf: Any, audio_len: Any, /) -> bool: + """bool SDL_LoadWAV_IO(SDL_IOStream *src, bool closeio, SDL_AudioSpec *spec, Uint8 **audio_buf, Uint32 *audio_len)""" + + @staticmethod + def SDL_LockAudioStream(stream: Any, /) -> bool: + """bool SDL_LockAudioStream(SDL_AudioStream *stream)""" + + @staticmethod + def SDL_LockJoysticks() -> None: + """void SDL_LockJoysticks(void)""" + + @staticmethod + def SDL_LockMutex(mutex: Any, /) -> None: + """void SDL_LockMutex(SDL_Mutex *mutex)""" + + @staticmethod + def SDL_LockProperties(props: Any, /) -> bool: + """bool SDL_LockProperties(SDL_PropertiesID props)""" + + @staticmethod + def SDL_LockRWLockForReading(rwlock: Any, /) -> None: + """void SDL_LockRWLockForReading(SDL_RWLock *rwlock)""" + + @staticmethod + def SDL_LockRWLockForWriting(rwlock: Any, /) -> None: + """void SDL_LockRWLockForWriting(SDL_RWLock *rwlock)""" + + @staticmethod + def SDL_LockSpinlock(lock: Any, /) -> None: + """void SDL_LockSpinlock(SDL_SpinLock *lock)""" + + @staticmethod + def SDL_LockSurface(surface: Any, /) -> bool: + """bool SDL_LockSurface(SDL_Surface *surface)""" + + @staticmethod + def SDL_LockTexture(texture: Any, rect: Any, pixels: Any, pitch: Any, /) -> bool: + """bool SDL_LockTexture(SDL_Texture *texture, const SDL_Rect *rect, void **pixels, int *pitch)""" + + @staticmethod + def SDL_LockTextureToSurface(texture: Any, rect: Any, surface: Any, /) -> bool: + """bool SDL_LockTextureToSurface(SDL_Texture *texture, const SDL_Rect *rect, SDL_Surface **surface)""" + + @staticmethod + def SDL_Log(fmt: Any, /, *__args: Any) -> None: + """void SDL_Log(const char *fmt, ...)""" + + @staticmethod + def SDL_LogCritical(category: int, fmt: Any, /, *__args: Any) -> None: + """void SDL_LogCritical(int category, const char *fmt, ...)""" + + @staticmethod + def SDL_LogDebug(category: int, fmt: Any, /, *__args: Any) -> None: + """void SDL_LogDebug(int category, const char *fmt, ...)""" + + @staticmethod + def SDL_LogError(category: int, fmt: Any, /, *__args: Any) -> None: + """void SDL_LogError(int category, const char *fmt, ...)""" + + @staticmethod + def SDL_LogInfo(category: int, fmt: Any, /, *__args: Any) -> None: + """void SDL_LogInfo(int category, const char *fmt, ...)""" + + @staticmethod + def SDL_LogMessage(category: int, priority: Any, fmt: Any, /, *__args: Any) -> None: + """void SDL_LogMessage(int category, SDL_LogPriority priority, const char *fmt, ...)""" + + @staticmethod + def SDL_LogTrace(category: int, fmt: Any, /, *__args: Any) -> None: + """void SDL_LogTrace(int category, const char *fmt, ...)""" + + @staticmethod + def SDL_LogVerbose(category: int, fmt: Any, /, *__args: Any) -> None: + """void SDL_LogVerbose(int category, const char *fmt, ...)""" + + @staticmethod + def SDL_LogWarn(category: int, fmt: Any, /, *__args: Any) -> None: + """void SDL_LogWarn(int category, const char *fmt, ...)""" + + @staticmethod + def SDL_MapGPUTransferBuffer(device: Any, transfer_buffer: Any, cycle: bool, /) -> Any: + """void *SDL_MapGPUTransferBuffer(SDL_GPUDevice *device, SDL_GPUTransferBuffer *transfer_buffer, bool cycle)""" + + @staticmethod + def SDL_MapRGB(format: Any, palette: Any, r: Any, g: Any, b: Any, /) -> Any: + """Uint32 SDL_MapRGB(const SDL_PixelFormatDetails *format, const SDL_Palette *palette, Uint8 r, Uint8 g, Uint8 b)""" + + @staticmethod + def SDL_MapRGBA(format: Any, palette: Any, r: Any, g: Any, b: Any, a: Any, /) -> Any: + """Uint32 SDL_MapRGBA(const SDL_PixelFormatDetails *format, const SDL_Palette *palette, Uint8 r, Uint8 g, Uint8 b, Uint8 a)""" + + @staticmethod + def SDL_MapSurfaceRGB(surface: Any, r: Any, g: Any, b: Any, /) -> Any: + """Uint32 SDL_MapSurfaceRGB(SDL_Surface *surface, Uint8 r, Uint8 g, Uint8 b)""" + + @staticmethod + def SDL_MapSurfaceRGBA(surface: Any, r: Any, g: Any, b: Any, a: Any, /) -> Any: + """Uint32 SDL_MapSurfaceRGBA(SDL_Surface *surface, Uint8 r, Uint8 g, Uint8 b, Uint8 a)""" + + @staticmethod + def SDL_MaximizeWindow(window: Any, /) -> bool: + """bool SDL_MaximizeWindow(SDL_Window *window)""" + + @staticmethod + def SDL_MemoryBarrierAcquireFunction() -> None: + """void SDL_MemoryBarrierAcquireFunction(void)""" + + @staticmethod + def SDL_MemoryBarrierReleaseFunction() -> None: + """void SDL_MemoryBarrierReleaseFunction(void)""" + + @staticmethod + def SDL_Metal_CreateView(window: Any, /) -> Any: + """SDL_MetalView SDL_Metal_CreateView(SDL_Window *window)""" + + @staticmethod + def SDL_Metal_DestroyView(view: Any, /) -> None: + """void SDL_Metal_DestroyView(SDL_MetalView view)""" + + @staticmethod + def SDL_Metal_GetLayer(view: Any, /) -> Any: + """void *SDL_Metal_GetLayer(SDL_MetalView view)""" + + @staticmethod + def SDL_MinimizeWindow(window: Any, /) -> bool: + """bool SDL_MinimizeWindow(SDL_Window *window)""" + + @staticmethod + def SDL_MixAudio(dst: Any, src: Any, format: Any, len: Any, volume: float, /) -> bool: + """bool SDL_MixAudio(Uint8 *dst, const Uint8 *src, SDL_AudioFormat format, Uint32 len, float volume)""" + + @staticmethod + def SDL_OnApplicationDidEnterBackground() -> None: + """void SDL_OnApplicationDidEnterBackground(void)""" + + @staticmethod + def SDL_OnApplicationDidEnterForeground() -> None: + """void SDL_OnApplicationDidEnterForeground(void)""" + + @staticmethod + def SDL_OnApplicationDidReceiveMemoryWarning() -> None: + """void SDL_OnApplicationDidReceiveMemoryWarning(void)""" + + @staticmethod + def SDL_OnApplicationWillEnterBackground() -> None: + """void SDL_OnApplicationWillEnterBackground(void)""" + + @staticmethod + def SDL_OnApplicationWillEnterForeground() -> None: + """void SDL_OnApplicationWillEnterForeground(void)""" + + @staticmethod + def SDL_OnApplicationWillTerminate() -> None: + """void SDL_OnApplicationWillTerminate(void)""" + + @staticmethod + def SDL_OpenAudioDevice(devid: Any, spec: Any, /) -> Any: + """SDL_AudioDeviceID SDL_OpenAudioDevice(SDL_AudioDeviceID devid, const SDL_AudioSpec *spec)""" + + @staticmethod + def SDL_OpenAudioDeviceStream(devid: Any, spec: Any, callback: Any, userdata: Any, /) -> Any: + """SDL_AudioStream *SDL_OpenAudioDeviceStream(SDL_AudioDeviceID devid, const SDL_AudioSpec *spec, SDL_AudioStreamCallback callback, void *userdata)""" + + @staticmethod + def SDL_OpenCamera(instance_id: Any, spec: Any, /) -> Any: + """SDL_Camera *SDL_OpenCamera(SDL_CameraID instance_id, const SDL_CameraSpec *spec)""" + + @staticmethod + def SDL_OpenFileStorage(path: Any, /) -> Any: + """SDL_Storage *SDL_OpenFileStorage(const char *path)""" + + @staticmethod + def SDL_OpenGamepad(instance_id: Any, /) -> Any: + """SDL_Gamepad *SDL_OpenGamepad(SDL_JoystickID instance_id)""" + + @staticmethod + def SDL_OpenHaptic(instance_id: Any, /) -> Any: + """SDL_Haptic *SDL_OpenHaptic(SDL_HapticID instance_id)""" + + @staticmethod + def SDL_OpenHapticFromJoystick(joystick: Any, /) -> Any: + """SDL_Haptic *SDL_OpenHapticFromJoystick(SDL_Joystick *joystick)""" + + @staticmethod + def SDL_OpenHapticFromMouse() -> Any: + """SDL_Haptic *SDL_OpenHapticFromMouse(void)""" + + @staticmethod + def SDL_OpenIO(iface: Any, userdata: Any, /) -> Any: + """SDL_IOStream *SDL_OpenIO(const SDL_IOStreamInterface *iface, void *userdata)""" + + @staticmethod + def SDL_OpenJoystick(instance_id: Any, /) -> Any: + """SDL_Joystick *SDL_OpenJoystick(SDL_JoystickID instance_id)""" + + @staticmethod + def SDL_OpenSensor(instance_id: Any, /) -> Any: + """SDL_Sensor *SDL_OpenSensor(SDL_SensorID instance_id)""" + + @staticmethod + def SDL_OpenStorage(iface: Any, userdata: Any, /) -> Any: + """SDL_Storage *SDL_OpenStorage(const SDL_StorageInterface *iface, void *userdata)""" + + @staticmethod + def SDL_OpenTitleStorage(override: Any, props: Any, /) -> Any: + """SDL_Storage *SDL_OpenTitleStorage(const char *override, SDL_PropertiesID props)""" + + @staticmethod + def SDL_OpenURL(url: Any, /) -> bool: + """bool SDL_OpenURL(const char *url)""" + + @staticmethod + def SDL_OpenUserStorage(org: Any, app: Any, props: Any, /) -> Any: + """SDL_Storage *SDL_OpenUserStorage(const char *org, const char *app, SDL_PropertiesID props)""" + + @staticmethod + def SDL_OutOfMemory() -> bool: + """bool SDL_OutOfMemory(void)""" + + @staticmethod + def SDL_PauseAudioDevice(devid: Any, /) -> bool: + """bool SDL_PauseAudioDevice(SDL_AudioDeviceID devid)""" + + @staticmethod + def SDL_PauseAudioStreamDevice(stream: Any, /) -> bool: + """bool SDL_PauseAudioStreamDevice(SDL_AudioStream *stream)""" + + @staticmethod + def SDL_PauseHaptic(haptic: Any, /) -> bool: + """bool SDL_PauseHaptic(SDL_Haptic *haptic)""" + + @staticmethod + def SDL_PeepEvents(events: Any, numevents: int, action: Any, minType: Any, maxType: Any, /) -> int: + """int SDL_PeepEvents(SDL_Event *events, int numevents, SDL_EventAction action, Uint32 minType, Uint32 maxType)""" + + @staticmethod + def SDL_PlayHapticRumble(haptic: Any, strength: float, length: Any, /) -> bool: + """bool SDL_PlayHapticRumble(SDL_Haptic *haptic, float strength, Uint32 length)""" + + @staticmethod + def SDL_PollEvent(event: Any, /) -> bool: + """bool SDL_PollEvent(SDL_Event *event)""" + + @staticmethod + def SDL_PopGPUDebugGroup(command_buffer: Any, /) -> None: + """void SDL_PopGPUDebugGroup(SDL_GPUCommandBuffer *command_buffer)""" + + @staticmethod + def SDL_PremultiplyAlpha( + width: int, + height: int, + src_format: Any, + src: Any, + src_pitch: int, + dst_format: Any, + dst: Any, + dst_pitch: int, + linear: bool, + /, + ) -> bool: + """bool SDL_PremultiplyAlpha(int width, int height, SDL_PixelFormat src_format, const void *src, int src_pitch, SDL_PixelFormat dst_format, void *dst, int dst_pitch, bool linear)""" + + @staticmethod + def SDL_PremultiplySurfaceAlpha(surface: Any, linear: bool, /) -> bool: + """bool SDL_PremultiplySurfaceAlpha(SDL_Surface *surface, bool linear)""" + + @staticmethod + def SDL_PumpEvents() -> None: + """void SDL_PumpEvents(void)""" + + @staticmethod + def SDL_PushEvent(event: Any, /) -> bool: + """bool SDL_PushEvent(SDL_Event *event)""" + + @staticmethod + def SDL_PushGPUComputeUniformData(command_buffer: Any, slot_index: Any, data: Any, length: Any, /) -> None: + """void SDL_PushGPUComputeUniformData(SDL_GPUCommandBuffer *command_buffer, Uint32 slot_index, const void *data, Uint32 length)""" + + @staticmethod + def SDL_PushGPUDebugGroup(command_buffer: Any, name: Any, /) -> None: + """void SDL_PushGPUDebugGroup(SDL_GPUCommandBuffer *command_buffer, const char *name)""" + + @staticmethod + def SDL_PushGPUFragmentUniformData(command_buffer: Any, slot_index: Any, data: Any, length: Any, /) -> None: + """void SDL_PushGPUFragmentUniformData(SDL_GPUCommandBuffer *command_buffer, Uint32 slot_index, const void *data, Uint32 length)""" + + @staticmethod + def SDL_PushGPUVertexUniformData(command_buffer: Any, slot_index: Any, data: Any, length: Any, /) -> None: + """void SDL_PushGPUVertexUniformData(SDL_GPUCommandBuffer *command_buffer, Uint32 slot_index, const void *data, Uint32 length)""" + + @staticmethod + def SDL_PutAudioStreamData(stream: Any, buf: Any, len: int, /) -> bool: + """bool SDL_PutAudioStreamData(SDL_AudioStream *stream, const void *buf, int len)""" + + @staticmethod + def SDL_QueryGPUFence(device: Any, fence: Any, /) -> bool: + """bool SDL_QueryGPUFence(SDL_GPUDevice *device, SDL_GPUFence *fence)""" + + @staticmethod + def SDL_Quit() -> None: + """void SDL_Quit(void)""" + + @staticmethod + def SDL_QuitSubSystem(flags: Any, /) -> None: + """void SDL_QuitSubSystem(SDL_InitFlags flags)""" + + @staticmethod + def SDL_RaiseWindow(window: Any, /) -> bool: + """bool SDL_RaiseWindow(SDL_Window *window)""" + + @staticmethod + def SDL_ReadAsyncIO(asyncio: Any, ptr: Any, offset: Any, size: Any, queue: Any, userdata: Any, /) -> bool: + """bool SDL_ReadAsyncIO(SDL_AsyncIO *asyncio, void *ptr, Uint64 offset, Uint64 size, SDL_AsyncIOQueue *queue, void *userdata)""" + + @staticmethod + def SDL_ReadIO(context: Any, ptr: Any, size: int, /) -> int: + """size_t SDL_ReadIO(SDL_IOStream *context, void *ptr, size_t size)""" + + @staticmethod + def SDL_ReadProcess(process: Any, datasize: Any, exitcode: Any, /) -> Any: + """void *SDL_ReadProcess(SDL_Process *process, size_t *datasize, int *exitcode)""" + + @staticmethod + def SDL_ReadS16BE(src: Any, value: Any, /) -> bool: + """bool SDL_ReadS16BE(SDL_IOStream *src, Sint16 *value)""" + + @staticmethod + def SDL_ReadS16LE(src: Any, value: Any, /) -> bool: + """bool SDL_ReadS16LE(SDL_IOStream *src, Sint16 *value)""" + + @staticmethod + def SDL_ReadS32BE(src: Any, value: Any, /) -> bool: + """bool SDL_ReadS32BE(SDL_IOStream *src, Sint32 *value)""" + + @staticmethod + def SDL_ReadS32LE(src: Any, value: Any, /) -> bool: + """bool SDL_ReadS32LE(SDL_IOStream *src, Sint32 *value)""" + + @staticmethod + def SDL_ReadS64BE(src: Any, value: Any, /) -> bool: + """bool SDL_ReadS64BE(SDL_IOStream *src, Sint64 *value)""" + + @staticmethod + def SDL_ReadS64LE(src: Any, value: Any, /) -> bool: + """bool SDL_ReadS64LE(SDL_IOStream *src, Sint64 *value)""" + + @staticmethod + def SDL_ReadS8(src: Any, value: Any, /) -> bool: + """bool SDL_ReadS8(SDL_IOStream *src, Sint8 *value)""" + + @staticmethod + def SDL_ReadStorageFile(storage: Any, path: Any, destination: Any, length: Any, /) -> bool: + """bool SDL_ReadStorageFile(SDL_Storage *storage, const char *path, void *destination, Uint64 length)""" + + @staticmethod + def SDL_ReadSurfacePixel(surface: Any, x: int, y: int, r: Any, g: Any, b: Any, a: Any, /) -> bool: + """bool SDL_ReadSurfacePixel(SDL_Surface *surface, int x, int y, Uint8 *r, Uint8 *g, Uint8 *b, Uint8 *a)""" + + @staticmethod + def SDL_ReadSurfacePixelFloat(surface: Any, x: int, y: int, r: Any, g: Any, b: Any, a: Any, /) -> bool: + """bool SDL_ReadSurfacePixelFloat(SDL_Surface *surface, int x, int y, float *r, float *g, float *b, float *a)""" + + @staticmethod + def SDL_ReadU16BE(src: Any, value: Any, /) -> bool: + """bool SDL_ReadU16BE(SDL_IOStream *src, Uint16 *value)""" + + @staticmethod + def SDL_ReadU16LE(src: Any, value: Any, /) -> bool: + """bool SDL_ReadU16LE(SDL_IOStream *src, Uint16 *value)""" + + @staticmethod + def SDL_ReadU32BE(src: Any, value: Any, /) -> bool: + """bool SDL_ReadU32BE(SDL_IOStream *src, Uint32 *value)""" + + @staticmethod + def SDL_ReadU32LE(src: Any, value: Any, /) -> bool: + """bool SDL_ReadU32LE(SDL_IOStream *src, Uint32 *value)""" + + @staticmethod + def SDL_ReadU64BE(src: Any, value: Any, /) -> bool: + """bool SDL_ReadU64BE(SDL_IOStream *src, Uint64 *value)""" + + @staticmethod + def SDL_ReadU64LE(src: Any, value: Any, /) -> bool: + """bool SDL_ReadU64LE(SDL_IOStream *src, Uint64 *value)""" + + @staticmethod + def SDL_ReadU8(src: Any, value: Any, /) -> bool: + """bool SDL_ReadU8(SDL_IOStream *src, Uint8 *value)""" + + @staticmethod + def SDL_RegisterEvents(numevents: int, /) -> Any: + """Uint32 SDL_RegisterEvents(int numevents)""" + + @staticmethod + def SDL_ReleaseCameraFrame(camera: Any, frame: Any, /) -> None: + """void SDL_ReleaseCameraFrame(SDL_Camera *camera, SDL_Surface *frame)""" + + @staticmethod + def SDL_ReleaseGPUBuffer(device: Any, buffer: Any, /) -> None: + """void SDL_ReleaseGPUBuffer(SDL_GPUDevice *device, SDL_GPUBuffer *buffer)""" + + @staticmethod + def SDL_ReleaseGPUComputePipeline(device: Any, compute_pipeline: Any, /) -> None: + """void SDL_ReleaseGPUComputePipeline(SDL_GPUDevice *device, SDL_GPUComputePipeline *compute_pipeline)""" + + @staticmethod + def SDL_ReleaseGPUFence(device: Any, fence: Any, /) -> None: + """void SDL_ReleaseGPUFence(SDL_GPUDevice *device, SDL_GPUFence *fence)""" + + @staticmethod + def SDL_ReleaseGPUGraphicsPipeline(device: Any, graphics_pipeline: Any, /) -> None: + """void SDL_ReleaseGPUGraphicsPipeline(SDL_GPUDevice *device, SDL_GPUGraphicsPipeline *graphics_pipeline)""" + + @staticmethod + def SDL_ReleaseGPUSampler(device: Any, sampler: Any, /) -> None: + """void SDL_ReleaseGPUSampler(SDL_GPUDevice *device, SDL_GPUSampler *sampler)""" + + @staticmethod + def SDL_ReleaseGPUShader(device: Any, shader: Any, /) -> None: + """void SDL_ReleaseGPUShader(SDL_GPUDevice *device, SDL_GPUShader *shader)""" + + @staticmethod + def SDL_ReleaseGPUTexture(device: Any, texture: Any, /) -> None: + """void SDL_ReleaseGPUTexture(SDL_GPUDevice *device, SDL_GPUTexture *texture)""" + + @staticmethod + def SDL_ReleaseGPUTransferBuffer(device: Any, transfer_buffer: Any, /) -> None: + """void SDL_ReleaseGPUTransferBuffer(SDL_GPUDevice *device, SDL_GPUTransferBuffer *transfer_buffer)""" + + @staticmethod + def SDL_ReleaseWindowFromGPUDevice(device: Any, window: Any, /) -> None: + """void SDL_ReleaseWindowFromGPUDevice(SDL_GPUDevice *device, SDL_Window *window)""" + + @staticmethod + def SDL_ReloadGamepadMappings() -> bool: + """bool SDL_ReloadGamepadMappings(void)""" + + @staticmethod + def SDL_RemoveEventWatch(filter: Any, userdata: Any, /) -> None: + """void SDL_RemoveEventWatch(SDL_EventFilter filter, void *userdata)""" + + @staticmethod + def SDL_RemoveHintCallback(name: Any, callback: Any, userdata: Any, /) -> None: + """void SDL_RemoveHintCallback(const char *name, SDL_HintCallback callback, void *userdata)""" + + @staticmethod + def SDL_RemovePath(path: Any, /) -> bool: + """bool SDL_RemovePath(const char *path)""" + + @staticmethod + def SDL_RemoveStoragePath(storage: Any, path: Any, /) -> bool: + """bool SDL_RemoveStoragePath(SDL_Storage *storage, const char *path)""" + + @staticmethod + def SDL_RemoveSurfaceAlternateImages(surface: Any, /) -> None: + """void SDL_RemoveSurfaceAlternateImages(SDL_Surface *surface)""" + + @staticmethod + def SDL_RemoveTimer(id: Any, /) -> bool: + """bool SDL_RemoveTimer(SDL_TimerID id)""" + + @staticmethod + def SDL_RemoveTrayEntry(entry: Any, /) -> None: + """void SDL_RemoveTrayEntry(SDL_TrayEntry *entry)""" + + @staticmethod + def SDL_RenamePath(oldpath: Any, newpath: Any, /) -> bool: + """bool SDL_RenamePath(const char *oldpath, const char *newpath)""" + + @staticmethod + def SDL_RenameStoragePath(storage: Any, oldpath: Any, newpath: Any, /) -> bool: + """bool SDL_RenameStoragePath(SDL_Storage *storage, const char *oldpath, const char *newpath)""" + + @staticmethod + def SDL_RenderClear(renderer: Any, /) -> bool: + """bool SDL_RenderClear(SDL_Renderer *renderer)""" + + @staticmethod + def SDL_RenderClipEnabled(renderer: Any, /) -> bool: + """bool SDL_RenderClipEnabled(SDL_Renderer *renderer)""" + + @staticmethod + def SDL_RenderCoordinatesFromWindow(renderer: Any, window_x: float, window_y: float, x: Any, y: Any, /) -> bool: + """bool SDL_RenderCoordinatesFromWindow(SDL_Renderer *renderer, float window_x, float window_y, float *x, float *y)""" + + @staticmethod + def SDL_RenderCoordinatesToWindow(renderer: Any, x: float, y: float, window_x: Any, window_y: Any, /) -> bool: + """bool SDL_RenderCoordinatesToWindow(SDL_Renderer *renderer, float x, float y, float *window_x, float *window_y)""" + + @staticmethod + def SDL_RenderDebugText(renderer: Any, x: float, y: float, str: Any, /) -> bool: + """bool SDL_RenderDebugText(SDL_Renderer *renderer, float x, float y, const char *str)""" + + @staticmethod + def SDL_RenderDebugTextFormat(renderer: Any, x: float, y: float, fmt: Any, /, *__args: Any) -> bool: + """bool SDL_RenderDebugTextFormat(SDL_Renderer *renderer, float x, float y, const char *fmt, ...)""" + + @staticmethod + def SDL_RenderFillRect(renderer: Any, rect: Any, /) -> bool: + """bool SDL_RenderFillRect(SDL_Renderer *renderer, const SDL_FRect *rect)""" + + @staticmethod + def SDL_RenderFillRects(renderer: Any, rects: Any, count: int, /) -> bool: + """bool SDL_RenderFillRects(SDL_Renderer *renderer, const SDL_FRect *rects, int count)""" + + @staticmethod + def SDL_RenderGeometry( + renderer: Any, texture: Any, vertices: Any, num_vertices: int, indices: Any, num_indices: int, / + ) -> bool: + """bool SDL_RenderGeometry(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_Vertex *vertices, int num_vertices, const int *indices, int num_indices)""" + + @staticmethod + def SDL_RenderGeometryRaw( + renderer: Any, + texture: Any, + xy: Any, + xy_stride: int, + color: Any, + color_stride: int, + uv: Any, + uv_stride: int, + num_vertices: int, + indices: Any, + num_indices: int, + size_indices: int, + /, + ) -> bool: + """bool SDL_RenderGeometryRaw(SDL_Renderer *renderer, SDL_Texture *texture, const float *xy, int xy_stride, const SDL_FColor *color, int color_stride, const float *uv, int uv_stride, int num_vertices, const void *indices, int num_indices, int size_indices)""" + + @staticmethod + def SDL_RenderLine(renderer: Any, x1: float, y1: float, x2: float, y2: float, /) -> bool: + """bool SDL_RenderLine(SDL_Renderer *renderer, float x1, float y1, float x2, float y2)""" + + @staticmethod + def SDL_RenderLines(renderer: Any, points: Any, count: int, /) -> bool: + """bool SDL_RenderLines(SDL_Renderer *renderer, const SDL_FPoint *points, int count)""" + + @staticmethod + def SDL_RenderPoint(renderer: Any, x: float, y: float, /) -> bool: + """bool SDL_RenderPoint(SDL_Renderer *renderer, float x, float y)""" + + @staticmethod + def SDL_RenderPoints(renderer: Any, points: Any, count: int, /) -> bool: + """bool SDL_RenderPoints(SDL_Renderer *renderer, const SDL_FPoint *points, int count)""" + + @staticmethod + def SDL_RenderPresent(renderer: Any, /) -> bool: + """bool SDL_RenderPresent(SDL_Renderer *renderer)""" + + @staticmethod + def SDL_RenderReadPixels(renderer: Any, rect: Any, /) -> Any: + """SDL_Surface *SDL_RenderReadPixels(SDL_Renderer *renderer, const SDL_Rect *rect)""" + + @staticmethod + def SDL_RenderRect(renderer: Any, rect: Any, /) -> bool: + """bool SDL_RenderRect(SDL_Renderer *renderer, const SDL_FRect *rect)""" + + @staticmethod + def SDL_RenderRects(renderer: Any, rects: Any, count: int, /) -> bool: + """bool SDL_RenderRects(SDL_Renderer *renderer, const SDL_FRect *rects, int count)""" + + @staticmethod + def SDL_RenderTexture(renderer: Any, texture: Any, srcrect: Any, dstrect: Any, /) -> bool: + """bool SDL_RenderTexture(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_FRect *srcrect, const SDL_FRect *dstrect)""" + + @staticmethod + def SDL_RenderTexture9Grid( + renderer: Any, + texture: Any, + srcrect: Any, + left_width: float, + right_width: float, + top_height: float, + bottom_height: float, + scale: float, + dstrect: Any, + /, + ) -> bool: + """bool SDL_RenderTexture9Grid(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_FRect *srcrect, float left_width, float right_width, float top_height, float bottom_height, float scale, const SDL_FRect *dstrect)""" + + @staticmethod + def SDL_RenderTextureAffine( + renderer: Any, texture: Any, srcrect: Any, origin: Any, right: Any, down: Any, / + ) -> bool: + """bool SDL_RenderTextureAffine(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_FRect *srcrect, const SDL_FPoint *origin, const SDL_FPoint *right, const SDL_FPoint *down)""" + + @staticmethod + def SDL_RenderTextureRotated( + renderer: Any, texture: Any, srcrect: Any, dstrect: Any, angle: float, center: Any, flip: Any, / + ) -> bool: + """bool SDL_RenderTextureRotated(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_FRect *srcrect, const SDL_FRect *dstrect, double angle, const SDL_FPoint *center, SDL_FlipMode flip)""" + + @staticmethod + def SDL_RenderTextureTiled(renderer: Any, texture: Any, srcrect: Any, scale: float, dstrect: Any, /) -> bool: + """bool SDL_RenderTextureTiled(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_FRect *srcrect, float scale, const SDL_FRect *dstrect)""" + + @staticmethod + def SDL_RenderViewportSet(renderer: Any, /) -> bool: + """bool SDL_RenderViewportSet(SDL_Renderer *renderer)""" + + @staticmethod + def SDL_ReportAssertion(data: Any, func: Any, file: Any, line: int, /) -> Any: + """SDL_AssertState SDL_ReportAssertion(SDL_AssertData *data, const char *func, const char *file, int line)""" + + @staticmethod + def SDL_ResetAssertionReport() -> None: + """void SDL_ResetAssertionReport(void)""" + + @staticmethod + def SDL_ResetHint(name: Any, /) -> bool: + """bool SDL_ResetHint(const char *name)""" + + @staticmethod + def SDL_ResetHints() -> None: + """void SDL_ResetHints(void)""" + + @staticmethod + def SDL_ResetKeyboard() -> None: + """void SDL_ResetKeyboard(void)""" + + @staticmethod + def SDL_ResetLogPriorities() -> None: + """void SDL_ResetLogPriorities(void)""" + + @staticmethod + def SDL_RestoreWindow(window: Any, /) -> bool: + """bool SDL_RestoreWindow(SDL_Window *window)""" + + @staticmethod + def SDL_ResumeAudioDevice(devid: Any, /) -> bool: + """bool SDL_ResumeAudioDevice(SDL_AudioDeviceID devid)""" + + @staticmethod + def SDL_ResumeAudioStreamDevice(stream: Any, /) -> bool: + """bool SDL_ResumeAudioStreamDevice(SDL_AudioStream *stream)""" + + @staticmethod + def SDL_ResumeHaptic(haptic: Any, /) -> bool: + """bool SDL_ResumeHaptic(SDL_Haptic *haptic)""" + + @staticmethod + def SDL_RumbleGamepad( + gamepad: Any, low_frequency_rumble: Any, high_frequency_rumble: Any, duration_ms: Any, / + ) -> bool: + """bool SDL_RumbleGamepad(SDL_Gamepad *gamepad, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms)""" + + @staticmethod + def SDL_RumbleGamepadTriggers(gamepad: Any, left_rumble: Any, right_rumble: Any, duration_ms: Any, /) -> bool: + """bool SDL_RumbleGamepadTriggers(SDL_Gamepad *gamepad, Uint16 left_rumble, Uint16 right_rumble, Uint32 duration_ms)""" + + @staticmethod + def SDL_RumbleJoystick( + joystick: Any, low_frequency_rumble: Any, high_frequency_rumble: Any, duration_ms: Any, / + ) -> bool: + """bool SDL_RumbleJoystick(SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms)""" + + @staticmethod + def SDL_RumbleJoystickTriggers(joystick: Any, left_rumble: Any, right_rumble: Any, duration_ms: Any, /) -> bool: + """bool SDL_RumbleJoystickTriggers(SDL_Joystick *joystick, Uint16 left_rumble, Uint16 right_rumble, Uint32 duration_ms)""" + + @staticmethod + def SDL_RunHapticEffect(haptic: Any, effect: int, iterations: Any, /) -> bool: + """bool SDL_RunHapticEffect(SDL_Haptic *haptic, int effect, Uint32 iterations)""" + + @staticmethod + def SDL_RunOnMainThread(callback: Any, userdata: Any, wait_complete: bool, /) -> bool: + """bool SDL_RunOnMainThread(SDL_MainThreadCallback callback, void *userdata, bool wait_complete)""" + + @staticmethod + def SDL_SaveBMP(surface: Any, file: Any, /) -> bool: + """bool SDL_SaveBMP(SDL_Surface *surface, const char *file)""" + + @staticmethod + def SDL_SaveBMP_IO(surface: Any, dst: Any, closeio: bool, /) -> bool: + """bool SDL_SaveBMP_IO(SDL_Surface *surface, SDL_IOStream *dst, bool closeio)""" + + @staticmethod + def SDL_SaveFile(file: Any, data: Any, datasize: int, /) -> bool: + """bool SDL_SaveFile(const char *file, const void *data, size_t datasize)""" + + @staticmethod + def SDL_SaveFile_IO(src: Any, data: Any, datasize: int, closeio: bool, /) -> bool: + """bool SDL_SaveFile_IO(SDL_IOStream *src, const void *data, size_t datasize, bool closeio)""" + + @staticmethod + def SDL_ScaleSurface(surface: Any, width: int, height: int, scaleMode: Any, /) -> Any: + """SDL_Surface *SDL_ScaleSurface(SDL_Surface *surface, int width, int height, SDL_ScaleMode scaleMode)""" + + @staticmethod + def SDL_ScreenKeyboardShown(window: Any, /) -> bool: + """bool SDL_ScreenKeyboardShown(SDL_Window *window)""" + + @staticmethod + def SDL_ScreenSaverEnabled() -> bool: + """bool SDL_ScreenSaverEnabled(void)""" + + @staticmethod + def SDL_SeekIO(context: Any, offset: Any, whence: Any, /) -> Any: + """Sint64 SDL_SeekIO(SDL_IOStream *context, Sint64 offset, SDL_IOWhence whence)""" + + @staticmethod + def SDL_SendGamepadEffect(gamepad: Any, data: Any, size: int, /) -> bool: + """bool SDL_SendGamepadEffect(SDL_Gamepad *gamepad, const void *data, int size)""" + + @staticmethod + def SDL_SendJoystickEffect(joystick: Any, data: Any, size: int, /) -> bool: + """bool SDL_SendJoystickEffect(SDL_Joystick *joystick, const void *data, int size)""" + + @staticmethod + def SDL_SendJoystickVirtualSensorData( + joystick: Any, type: Any, sensor_timestamp: Any, data: Any, num_values: int, / + ) -> bool: + """bool SDL_SendJoystickVirtualSensorData(SDL_Joystick *joystick, SDL_SensorType type, Uint64 sensor_timestamp, const float *data, int num_values)""" + + @staticmethod + def SDL_SetAppMetadata(appname: Any, appversion: Any, appidentifier: Any, /) -> bool: + """bool SDL_SetAppMetadata(const char *appname, const char *appversion, const char *appidentifier)""" + + @staticmethod + def SDL_SetAppMetadataProperty(name: Any, value: Any, /) -> bool: + """bool SDL_SetAppMetadataProperty(const char *name, const char *value)""" + + @staticmethod + def SDL_SetAssertionHandler(handler: Any, userdata: Any, /) -> None: + """void SDL_SetAssertionHandler(SDL_AssertionHandler handler, void *userdata)""" + + @staticmethod + def SDL_SetAtomicInt(a: Any, v: int, /) -> int: + """int SDL_SetAtomicInt(SDL_AtomicInt *a, int v)""" + + @staticmethod + def SDL_SetAtomicPointer(a: Any, v: Any, /) -> Any: + """void *SDL_SetAtomicPointer(void **a, void *v)""" + + @staticmethod + def SDL_SetAtomicU32(a: Any, v: Any, /) -> Any: + """Uint32 SDL_SetAtomicU32(SDL_AtomicU32 *a, Uint32 v)""" + + @staticmethod + def SDL_SetAudioDeviceGain(devid: Any, gain: float, /) -> bool: + """bool SDL_SetAudioDeviceGain(SDL_AudioDeviceID devid, float gain)""" + + @staticmethod + def SDL_SetAudioPostmixCallback(devid: Any, callback: Any, userdata: Any, /) -> bool: + """bool SDL_SetAudioPostmixCallback(SDL_AudioDeviceID devid, SDL_AudioPostmixCallback callback, void *userdata)""" + + @staticmethod + def SDL_SetAudioStreamFormat(stream: Any, src_spec: Any, dst_spec: Any, /) -> bool: + """bool SDL_SetAudioStreamFormat(SDL_AudioStream *stream, const SDL_AudioSpec *src_spec, const SDL_AudioSpec *dst_spec)""" + + @staticmethod + def SDL_SetAudioStreamFrequencyRatio(stream: Any, ratio: float, /) -> bool: + """bool SDL_SetAudioStreamFrequencyRatio(SDL_AudioStream *stream, float ratio)""" + + @staticmethod + def SDL_SetAudioStreamGain(stream: Any, gain: float, /) -> bool: + """bool SDL_SetAudioStreamGain(SDL_AudioStream *stream, float gain)""" + + @staticmethod + def SDL_SetAudioStreamGetCallback(stream: Any, callback: Any, userdata: Any, /) -> bool: + """bool SDL_SetAudioStreamGetCallback(SDL_AudioStream *stream, SDL_AudioStreamCallback callback, void *userdata)""" + + @staticmethod + def SDL_SetAudioStreamInputChannelMap(stream: Any, chmap: Any, count: int, /) -> bool: + """bool SDL_SetAudioStreamInputChannelMap(SDL_AudioStream *stream, const int *chmap, int count)""" + + @staticmethod + def SDL_SetAudioStreamOutputChannelMap(stream: Any, chmap: Any, count: int, /) -> bool: + """bool SDL_SetAudioStreamOutputChannelMap(SDL_AudioStream *stream, const int *chmap, int count)""" + + @staticmethod + def SDL_SetAudioStreamPutCallback(stream: Any, callback: Any, userdata: Any, /) -> bool: + """bool SDL_SetAudioStreamPutCallback(SDL_AudioStream *stream, SDL_AudioStreamCallback callback, void *userdata)""" + + @staticmethod + def SDL_SetBooleanProperty(props: Any, name: Any, value: bool, /) -> bool: + """bool SDL_SetBooleanProperty(SDL_PropertiesID props, const char *name, bool value)""" + + @staticmethod + def SDL_SetClipboardData( + callback: Any, cleanup: Any, userdata: Any, mime_types: Any, num_mime_types: int, / + ) -> bool: + """bool SDL_SetClipboardData(SDL_ClipboardDataCallback callback, SDL_ClipboardCleanupCallback cleanup, void *userdata, const char **mime_types, size_t num_mime_types)""" + + @staticmethod + def SDL_SetClipboardText(text: Any, /) -> bool: + """bool SDL_SetClipboardText(const char *text)""" + + @staticmethod + def SDL_SetCurrentThreadPriority(priority: Any, /) -> bool: + """bool SDL_SetCurrentThreadPriority(SDL_ThreadPriority priority)""" + + @staticmethod + def SDL_SetCursor(cursor: Any, /) -> bool: + """bool SDL_SetCursor(SDL_Cursor *cursor)""" + + @staticmethod + def SDL_SetEnvironmentVariable(env: Any, name: Any, value: Any, overwrite: bool, /) -> bool: + """bool SDL_SetEnvironmentVariable(SDL_Environment *env, const char *name, const char *value, bool overwrite)""" + + @staticmethod + def SDL_SetError(fmt: Any, /, *__args: Any) -> bool: + """bool SDL_SetError(const char *fmt, ...)""" + + @staticmethod + def SDL_SetEventEnabled(type: Any, enabled: bool, /) -> None: + """void SDL_SetEventEnabled(Uint32 type, bool enabled)""" + + @staticmethod + def SDL_SetEventFilter(filter: Any, userdata: Any, /) -> None: + """void SDL_SetEventFilter(SDL_EventFilter filter, void *userdata)""" + + @staticmethod + def SDL_SetFloatProperty(props: Any, name: Any, value: float, /) -> bool: + """bool SDL_SetFloatProperty(SDL_PropertiesID props, const char *name, float value)""" + + @staticmethod + def SDL_SetGPUAllowedFramesInFlight(device: Any, allowed_frames_in_flight: Any, /) -> bool: + """bool SDL_SetGPUAllowedFramesInFlight(SDL_GPUDevice *device, Uint32 allowed_frames_in_flight)""" + + @staticmethod + def SDL_SetGPUBlendConstants(render_pass: Any, blend_constants: Any, /) -> None: + """void SDL_SetGPUBlendConstants(SDL_GPURenderPass *render_pass, SDL_FColor blend_constants)""" + + @staticmethod + def SDL_SetGPUBufferName(device: Any, buffer: Any, text: Any, /) -> None: + """void SDL_SetGPUBufferName(SDL_GPUDevice *device, SDL_GPUBuffer *buffer, const char *text)""" + + @staticmethod + def SDL_SetGPUScissor(render_pass: Any, scissor: Any, /) -> None: + """void SDL_SetGPUScissor(SDL_GPURenderPass *render_pass, const SDL_Rect *scissor)""" + + @staticmethod + def SDL_SetGPUStencilReference(render_pass: Any, reference: Any, /) -> None: + """void SDL_SetGPUStencilReference(SDL_GPURenderPass *render_pass, Uint8 reference)""" + + @staticmethod + def SDL_SetGPUSwapchainParameters( + device: Any, window: Any, swapchain_composition: Any, present_mode: Any, / + ) -> bool: + """bool SDL_SetGPUSwapchainParameters(SDL_GPUDevice *device, SDL_Window *window, SDL_GPUSwapchainComposition swapchain_composition, SDL_GPUPresentMode present_mode)""" + + @staticmethod + def SDL_SetGPUTextureName(device: Any, texture: Any, text: Any, /) -> None: + """void SDL_SetGPUTextureName(SDL_GPUDevice *device, SDL_GPUTexture *texture, const char *text)""" + + @staticmethod + def SDL_SetGPUViewport(render_pass: Any, viewport: Any, /) -> None: + """void SDL_SetGPUViewport(SDL_GPURenderPass *render_pass, const SDL_GPUViewport *viewport)""" + + @staticmethod + def SDL_SetGamepadEventsEnabled(enabled: bool, /) -> None: + """void SDL_SetGamepadEventsEnabled(bool enabled)""" + + @staticmethod + def SDL_SetGamepadLED(gamepad: Any, red: Any, green: Any, blue: Any, /) -> bool: + """bool SDL_SetGamepadLED(SDL_Gamepad *gamepad, Uint8 red, Uint8 green, Uint8 blue)""" + + @staticmethod + def SDL_SetGamepadMapping(instance_id: Any, mapping: Any, /) -> bool: + """bool SDL_SetGamepadMapping(SDL_JoystickID instance_id, const char *mapping)""" + + @staticmethod + def SDL_SetGamepadPlayerIndex(gamepad: Any, player_index: int, /) -> bool: + """bool SDL_SetGamepadPlayerIndex(SDL_Gamepad *gamepad, int player_index)""" + + @staticmethod + def SDL_SetGamepadSensorEnabled(gamepad: Any, type: Any, enabled: bool, /) -> bool: + """bool SDL_SetGamepadSensorEnabled(SDL_Gamepad *gamepad, SDL_SensorType type, bool enabled)""" + + @staticmethod + def SDL_SetHapticAutocenter(haptic: Any, autocenter: int, /) -> bool: + """bool SDL_SetHapticAutocenter(SDL_Haptic *haptic, int autocenter)""" + + @staticmethod + def SDL_SetHapticGain(haptic: Any, gain: int, /) -> bool: + """bool SDL_SetHapticGain(SDL_Haptic *haptic, int gain)""" + + @staticmethod + def SDL_SetHint(name: Any, value: Any, /) -> bool: + """bool SDL_SetHint(const char *name, const char *value)""" + + @staticmethod + def SDL_SetHintWithPriority(name: Any, value: Any, priority: Any, /) -> bool: + """bool SDL_SetHintWithPriority(const char *name, const char *value, SDL_HintPriority priority)""" + + @staticmethod + def SDL_SetInitialized(state: Any, initialized: bool, /) -> None: + """void SDL_SetInitialized(SDL_InitState *state, bool initialized)""" + + @staticmethod + def SDL_SetJoystickEventsEnabled(enabled: bool, /) -> None: + """void SDL_SetJoystickEventsEnabled(bool enabled)""" + + @staticmethod + def SDL_SetJoystickLED(joystick: Any, red: Any, green: Any, blue: Any, /) -> bool: + """bool SDL_SetJoystickLED(SDL_Joystick *joystick, Uint8 red, Uint8 green, Uint8 blue)""" + + @staticmethod + def SDL_SetJoystickPlayerIndex(joystick: Any, player_index: int, /) -> bool: + """bool SDL_SetJoystickPlayerIndex(SDL_Joystick *joystick, int player_index)""" + + @staticmethod + def SDL_SetJoystickVirtualAxis(joystick: Any, axis: int, value: Any, /) -> bool: + """bool SDL_SetJoystickVirtualAxis(SDL_Joystick *joystick, int axis, Sint16 value)""" + + @staticmethod + def SDL_SetJoystickVirtualBall(joystick: Any, ball: int, xrel: Any, yrel: Any, /) -> bool: + """bool SDL_SetJoystickVirtualBall(SDL_Joystick *joystick, int ball, Sint16 xrel, Sint16 yrel)""" + + @staticmethod + def SDL_SetJoystickVirtualButton(joystick: Any, button: int, down: bool, /) -> bool: + """bool SDL_SetJoystickVirtualButton(SDL_Joystick *joystick, int button, bool down)""" + + @staticmethod + def SDL_SetJoystickVirtualHat(joystick: Any, hat: int, value: Any, /) -> bool: + """bool SDL_SetJoystickVirtualHat(SDL_Joystick *joystick, int hat, Uint8 value)""" + + @staticmethod + def SDL_SetJoystickVirtualTouchpad( + joystick: Any, touchpad: int, finger: int, down: bool, x: float, y: float, pressure: float, / + ) -> bool: + """bool SDL_SetJoystickVirtualTouchpad(SDL_Joystick *joystick, int touchpad, int finger, bool down, float x, float y, float pressure)""" + + @staticmethod + def SDL_SetLogOutputFunction(callback: Any, userdata: Any, /) -> None: + """void SDL_SetLogOutputFunction(SDL_LogOutputFunction callback, void *userdata)""" + + @staticmethod + def SDL_SetLogPriorities(priority: Any, /) -> None: + """void SDL_SetLogPriorities(SDL_LogPriority priority)""" + + @staticmethod + def SDL_SetLogPriority(category: int, priority: Any, /) -> None: + """void SDL_SetLogPriority(int category, SDL_LogPriority priority)""" + + @staticmethod + def SDL_SetLogPriorityPrefix(priority: Any, prefix: Any, /) -> bool: + """bool SDL_SetLogPriorityPrefix(SDL_LogPriority priority, const char *prefix)""" + + @staticmethod + def SDL_SetMemoryFunctions(malloc_func: Any, calloc_func: Any, realloc_func: Any, free_func: Any, /) -> bool: + """bool SDL_SetMemoryFunctions(SDL_malloc_func malloc_func, SDL_calloc_func calloc_func, SDL_realloc_func realloc_func, SDL_free_func free_func)""" + + @staticmethod + def SDL_SetModState(modstate: Any, /) -> None: + """void SDL_SetModState(SDL_Keymod modstate)""" + + @staticmethod + def SDL_SetNumberProperty(props: Any, name: Any, value: Any, /) -> bool: + """bool SDL_SetNumberProperty(SDL_PropertiesID props, const char *name, Sint64 value)""" + + @staticmethod + def SDL_SetPaletteColors(palette: Any, colors: Any, firstcolor: int, ncolors: int, /) -> bool: + """bool SDL_SetPaletteColors(SDL_Palette *palette, const SDL_Color *colors, int firstcolor, int ncolors)""" + + @staticmethod + def SDL_SetPointerProperty(props: Any, name: Any, value: Any, /) -> bool: + """bool SDL_SetPointerProperty(SDL_PropertiesID props, const char *name, void *value)""" + + @staticmethod + def SDL_SetPointerPropertyWithCleanup(props: Any, name: Any, value: Any, cleanup: Any, userdata: Any, /) -> bool: + """bool SDL_SetPointerPropertyWithCleanup(SDL_PropertiesID props, const char *name, void *value, SDL_CleanupPropertyCallback cleanup, void *userdata)""" + + @staticmethod + def SDL_SetPrimarySelectionText(text: Any, /) -> bool: + """bool SDL_SetPrimarySelectionText(const char *text)""" + + @staticmethod + def SDL_SetRenderClipRect(renderer: Any, rect: Any, /) -> bool: + """bool SDL_SetRenderClipRect(SDL_Renderer *renderer, const SDL_Rect *rect)""" + + @staticmethod + def SDL_SetRenderColorScale(renderer: Any, scale: float, /) -> bool: + """bool SDL_SetRenderColorScale(SDL_Renderer *renderer, float scale)""" + + @staticmethod + def SDL_SetRenderDrawBlendMode(renderer: Any, blendMode: Any, /) -> bool: + """bool SDL_SetRenderDrawBlendMode(SDL_Renderer *renderer, SDL_BlendMode blendMode)""" + + @staticmethod + def SDL_SetRenderDrawColor(renderer: Any, r: Any, g: Any, b: Any, a: Any, /) -> bool: + """bool SDL_SetRenderDrawColor(SDL_Renderer *renderer, Uint8 r, Uint8 g, Uint8 b, Uint8 a)""" + + @staticmethod + def SDL_SetRenderDrawColorFloat(renderer: Any, r: float, g: float, b: float, a: float, /) -> bool: + """bool SDL_SetRenderDrawColorFloat(SDL_Renderer *renderer, float r, float g, float b, float a)""" + + @staticmethod + def SDL_SetRenderLogicalPresentation(renderer: Any, w: int, h: int, mode: Any, /) -> bool: + """bool SDL_SetRenderLogicalPresentation(SDL_Renderer *renderer, int w, int h, SDL_RendererLogicalPresentation mode)""" + + @staticmethod + def SDL_SetRenderScale(renderer: Any, scaleX: float, scaleY: float, /) -> bool: + """bool SDL_SetRenderScale(SDL_Renderer *renderer, float scaleX, float scaleY)""" + + @staticmethod + def SDL_SetRenderTarget(renderer: Any, texture: Any, /) -> bool: + """bool SDL_SetRenderTarget(SDL_Renderer *renderer, SDL_Texture *texture)""" + + @staticmethod + def SDL_SetRenderVSync(renderer: Any, vsync: int, /) -> bool: + """bool SDL_SetRenderVSync(SDL_Renderer *renderer, int vsync)""" + + @staticmethod + def SDL_SetRenderViewport(renderer: Any, rect: Any, /) -> bool: + """bool SDL_SetRenderViewport(SDL_Renderer *renderer, const SDL_Rect *rect)""" + + @staticmethod + def SDL_SetScancodeName(scancode: Any, name: Any, /) -> bool: + """bool SDL_SetScancodeName(SDL_Scancode scancode, const char *name)""" + + @staticmethod + def SDL_SetStringProperty(props: Any, name: Any, value: Any, /) -> bool: + """bool SDL_SetStringProperty(SDL_PropertiesID props, const char *name, const char *value)""" + + @staticmethod + def SDL_SetSurfaceAlphaMod(surface: Any, alpha: Any, /) -> bool: + """bool SDL_SetSurfaceAlphaMod(SDL_Surface *surface, Uint8 alpha)""" + + @staticmethod + def SDL_SetSurfaceBlendMode(surface: Any, blendMode: Any, /) -> bool: + """bool SDL_SetSurfaceBlendMode(SDL_Surface *surface, SDL_BlendMode blendMode)""" + + @staticmethod + def SDL_SetSurfaceClipRect(surface: Any, rect: Any, /) -> bool: + """bool SDL_SetSurfaceClipRect(SDL_Surface *surface, const SDL_Rect *rect)""" + + @staticmethod + def SDL_SetSurfaceColorKey(surface: Any, enabled: bool, key: Any, /) -> bool: + """bool SDL_SetSurfaceColorKey(SDL_Surface *surface, bool enabled, Uint32 key)""" + + @staticmethod + def SDL_SetSurfaceColorMod(surface: Any, r: Any, g: Any, b: Any, /) -> bool: + """bool SDL_SetSurfaceColorMod(SDL_Surface *surface, Uint8 r, Uint8 g, Uint8 b)""" + + @staticmethod + def SDL_SetSurfaceColorspace(surface: Any, colorspace: Any, /) -> bool: + """bool SDL_SetSurfaceColorspace(SDL_Surface *surface, SDL_Colorspace colorspace)""" + + @staticmethod + def SDL_SetSurfacePalette(surface: Any, palette: Any, /) -> bool: + """bool SDL_SetSurfacePalette(SDL_Surface *surface, SDL_Palette *palette)""" + + @staticmethod + def SDL_SetSurfaceRLE(surface: Any, enabled: bool, /) -> bool: + """bool SDL_SetSurfaceRLE(SDL_Surface *surface, bool enabled)""" + + @staticmethod + def SDL_SetTLS(id: Any, value: Any, destructor: Any, /) -> bool: + """bool SDL_SetTLS(SDL_TLSID *id, const void *value, SDL_TLSDestructorCallback destructor)""" + + @staticmethod + def SDL_SetTextInputArea(window: Any, rect: Any, cursor: int, /) -> bool: + """bool SDL_SetTextInputArea(SDL_Window *window, const SDL_Rect *rect, int cursor)""" + + @staticmethod + def SDL_SetTextureAlphaMod(texture: Any, alpha: Any, /) -> bool: + """bool SDL_SetTextureAlphaMod(SDL_Texture *texture, Uint8 alpha)""" + + @staticmethod + def SDL_SetTextureAlphaModFloat(texture: Any, alpha: float, /) -> bool: + """bool SDL_SetTextureAlphaModFloat(SDL_Texture *texture, float alpha)""" + + @staticmethod + def SDL_SetTextureBlendMode(texture: Any, blendMode: Any, /) -> bool: + """bool SDL_SetTextureBlendMode(SDL_Texture *texture, SDL_BlendMode blendMode)""" + + @staticmethod + def SDL_SetTextureColorMod(texture: Any, r: Any, g: Any, b: Any, /) -> bool: + """bool SDL_SetTextureColorMod(SDL_Texture *texture, Uint8 r, Uint8 g, Uint8 b)""" + + @staticmethod + def SDL_SetTextureColorModFloat(texture: Any, r: float, g: float, b: float, /) -> bool: + """bool SDL_SetTextureColorModFloat(SDL_Texture *texture, float r, float g, float b)""" + + @staticmethod + def SDL_SetTextureScaleMode(texture: Any, scaleMode: Any, /) -> bool: + """bool SDL_SetTextureScaleMode(SDL_Texture *texture, SDL_ScaleMode scaleMode)""" + + @staticmethod + def SDL_SetTrayEntryCallback(entry: Any, callback: Any, userdata: Any, /) -> None: + """void SDL_SetTrayEntryCallback(SDL_TrayEntry *entry, SDL_TrayCallback callback, void *userdata)""" + + @staticmethod + def SDL_SetTrayEntryChecked(entry: Any, checked: bool, /) -> None: + """void SDL_SetTrayEntryChecked(SDL_TrayEntry *entry, bool checked)""" + + @staticmethod + def SDL_SetTrayEntryEnabled(entry: Any, enabled: bool, /) -> None: + """void SDL_SetTrayEntryEnabled(SDL_TrayEntry *entry, bool enabled)""" + + @staticmethod + def SDL_SetTrayEntryLabel(entry: Any, label: Any, /) -> None: + """void SDL_SetTrayEntryLabel(SDL_TrayEntry *entry, const char *label)""" + + @staticmethod + def SDL_SetTrayIcon(tray: Any, icon: Any, /) -> None: + """void SDL_SetTrayIcon(SDL_Tray *tray, SDL_Surface *icon)""" + + @staticmethod + def SDL_SetTrayTooltip(tray: Any, tooltip: Any, /) -> None: + """void SDL_SetTrayTooltip(SDL_Tray *tray, const char *tooltip)""" + + @staticmethod + def SDL_SetWindowAlwaysOnTop(window: Any, on_top: bool, /) -> bool: + """bool SDL_SetWindowAlwaysOnTop(SDL_Window *window, bool on_top)""" + + @staticmethod + def SDL_SetWindowAspectRatio(window: Any, min_aspect: float, max_aspect: float, /) -> bool: + """bool SDL_SetWindowAspectRatio(SDL_Window *window, float min_aspect, float max_aspect)""" + + @staticmethod + def SDL_SetWindowBordered(window: Any, bordered: bool, /) -> bool: + """bool SDL_SetWindowBordered(SDL_Window *window, bool bordered)""" + + @staticmethod + def SDL_SetWindowFocusable(window: Any, focusable: bool, /) -> bool: + """bool SDL_SetWindowFocusable(SDL_Window *window, bool focusable)""" + + @staticmethod + def SDL_SetWindowFullscreen(window: Any, fullscreen: bool, /) -> bool: + """bool SDL_SetWindowFullscreen(SDL_Window *window, bool fullscreen)""" + + @staticmethod + def SDL_SetWindowFullscreenMode(window: Any, mode: Any, /) -> bool: + """bool SDL_SetWindowFullscreenMode(SDL_Window *window, const SDL_DisplayMode *mode)""" + + @staticmethod + def SDL_SetWindowHitTest(window: Any, callback: Any, callback_data: Any, /) -> bool: + """bool SDL_SetWindowHitTest(SDL_Window *window, SDL_HitTest callback, void *callback_data)""" + + @staticmethod + def SDL_SetWindowIcon(window: Any, icon: Any, /) -> bool: + """bool SDL_SetWindowIcon(SDL_Window *window, SDL_Surface *icon)""" + + @staticmethod + def SDL_SetWindowKeyboardGrab(window: Any, grabbed: bool, /) -> bool: + """bool SDL_SetWindowKeyboardGrab(SDL_Window *window, bool grabbed)""" + + @staticmethod + def SDL_SetWindowMaximumSize(window: Any, max_w: int, max_h: int, /) -> bool: + """bool SDL_SetWindowMaximumSize(SDL_Window *window, int max_w, int max_h)""" + + @staticmethod + def SDL_SetWindowMinimumSize(window: Any, min_w: int, min_h: int, /) -> bool: + """bool SDL_SetWindowMinimumSize(SDL_Window *window, int min_w, int min_h)""" + + @staticmethod + def SDL_SetWindowModal(window: Any, modal: bool, /) -> bool: + """bool SDL_SetWindowModal(SDL_Window *window, bool modal)""" + + @staticmethod + def SDL_SetWindowMouseGrab(window: Any, grabbed: bool, /) -> bool: + """bool SDL_SetWindowMouseGrab(SDL_Window *window, bool grabbed)""" + + @staticmethod + def SDL_SetWindowMouseRect(window: Any, rect: Any, /) -> bool: + """bool SDL_SetWindowMouseRect(SDL_Window *window, const SDL_Rect *rect)""" + + @staticmethod + def SDL_SetWindowOpacity(window: Any, opacity: float, /) -> bool: + """bool SDL_SetWindowOpacity(SDL_Window *window, float opacity)""" + + @staticmethod + def SDL_SetWindowParent(window: Any, parent: Any, /) -> bool: + """bool SDL_SetWindowParent(SDL_Window *window, SDL_Window *parent)""" + + @staticmethod + def SDL_SetWindowPosition(window: Any, x: int, y: int, /) -> bool: + """bool SDL_SetWindowPosition(SDL_Window *window, int x, int y)""" + + @staticmethod + def SDL_SetWindowRelativeMouseMode(window: Any, enabled: bool, /) -> bool: + """bool SDL_SetWindowRelativeMouseMode(SDL_Window *window, bool enabled)""" + + @staticmethod + def SDL_SetWindowResizable(window: Any, resizable: bool, /) -> bool: + """bool SDL_SetWindowResizable(SDL_Window *window, bool resizable)""" + + @staticmethod + def SDL_SetWindowShape(window: Any, shape: Any, /) -> bool: + """bool SDL_SetWindowShape(SDL_Window *window, SDL_Surface *shape)""" + + @staticmethod + def SDL_SetWindowSize(window: Any, w: int, h: int, /) -> bool: + """bool SDL_SetWindowSize(SDL_Window *window, int w, int h)""" + + @staticmethod + def SDL_SetWindowSurfaceVSync(window: Any, vsync: int, /) -> bool: + """bool SDL_SetWindowSurfaceVSync(SDL_Window *window, int vsync)""" + + @staticmethod + def SDL_SetWindowTitle(window: Any, title: Any, /) -> bool: + """bool SDL_SetWindowTitle(SDL_Window *window, const char *title)""" + + @staticmethod + def SDL_SetX11EventHook(callback: Any, userdata: Any, /) -> None: + """void SDL_SetX11EventHook(SDL_X11EventHook callback, void *userdata)""" + + @staticmethod + def SDL_ShouldInit(state: Any, /) -> bool: + """bool SDL_ShouldInit(SDL_InitState *state)""" + + @staticmethod + def SDL_ShouldQuit(state: Any, /) -> bool: + """bool SDL_ShouldQuit(SDL_InitState *state)""" + + @staticmethod + def SDL_ShowCursor() -> bool: + """bool SDL_ShowCursor(void)""" + + @staticmethod + def SDL_ShowFileDialogWithProperties(type: Any, callback: Any, userdata: Any, props: Any, /) -> None: + """void SDL_ShowFileDialogWithProperties(SDL_FileDialogType type, SDL_DialogFileCallback callback, void *userdata, SDL_PropertiesID props)""" + + @staticmethod + def SDL_ShowMessageBox(messageboxdata: Any, buttonid: Any, /) -> bool: + """bool SDL_ShowMessageBox(const SDL_MessageBoxData *messageboxdata, int *buttonid)""" + + @staticmethod + def SDL_ShowOpenFileDialog( + callback: Any, + userdata: Any, + window: Any, + filters: Any, + nfilters: int, + default_location: Any, + allow_many: bool, + /, + ) -> None: + """void SDL_ShowOpenFileDialog(SDL_DialogFileCallback callback, void *userdata, SDL_Window *window, const SDL_DialogFileFilter *filters, int nfilters, const char *default_location, bool allow_many)""" + + @staticmethod + def SDL_ShowOpenFolderDialog( + callback: Any, userdata: Any, window: Any, default_location: Any, allow_many: bool, / + ) -> None: + """void SDL_ShowOpenFolderDialog(SDL_DialogFileCallback callback, void *userdata, SDL_Window *window, const char *default_location, bool allow_many)""" + + @staticmethod + def SDL_ShowSaveFileDialog( + callback: Any, userdata: Any, window: Any, filters: Any, nfilters: int, default_location: Any, / + ) -> None: + """void SDL_ShowSaveFileDialog(SDL_DialogFileCallback callback, void *userdata, SDL_Window *window, const SDL_DialogFileFilter *filters, int nfilters, const char *default_location)""" + + @staticmethod + def SDL_ShowSimpleMessageBox(flags: Any, title: Any, message: Any, window: Any, /) -> bool: + """bool SDL_ShowSimpleMessageBox(SDL_MessageBoxFlags flags, const char *title, const char *message, SDL_Window *window)""" + + @staticmethod + def SDL_ShowWindow(window: Any, /) -> bool: + """bool SDL_ShowWindow(SDL_Window *window)""" + + @staticmethod + def SDL_ShowWindowSystemMenu(window: Any, x: int, y: int, /) -> bool: + """bool SDL_ShowWindowSystemMenu(SDL_Window *window, int x, int y)""" + + @staticmethod + def SDL_SignalAsyncIOQueue(queue: Any, /) -> None: + """void SDL_SignalAsyncIOQueue(SDL_AsyncIOQueue *queue)""" + + @staticmethod + def SDL_SignalCondition(cond: Any, /) -> None: + """void SDL_SignalCondition(SDL_Condition *cond)""" + + @staticmethod + def SDL_SignalSemaphore(sem: Any, /) -> None: + """void SDL_SignalSemaphore(SDL_Semaphore *sem)""" + + @staticmethod + def SDL_StartTextInput(window: Any, /) -> bool: + """bool SDL_StartTextInput(SDL_Window *window)""" + + @staticmethod + def SDL_StartTextInputWithProperties(window: Any, props: Any, /) -> bool: + """bool SDL_StartTextInputWithProperties(SDL_Window *window, SDL_PropertiesID props)""" + + @staticmethod + def SDL_StepBackUTF8(start: Any, pstr: Any, /) -> Any: + """Uint32 SDL_StepBackUTF8(const char *start, const char **pstr)""" + + @staticmethod + def SDL_StepUTF8(pstr: Any, pslen: Any, /) -> Any: + """Uint32 SDL_StepUTF8(const char **pstr, size_t *pslen)""" + + @staticmethod + def SDL_StopHapticEffect(haptic: Any, effect: int, /) -> bool: + """bool SDL_StopHapticEffect(SDL_Haptic *haptic, int effect)""" + + @staticmethod + def SDL_StopHapticEffects(haptic: Any, /) -> bool: + """bool SDL_StopHapticEffects(SDL_Haptic *haptic)""" + + @staticmethod + def SDL_StopHapticRumble(haptic: Any, /) -> bool: + """bool SDL_StopHapticRumble(SDL_Haptic *haptic)""" + + @staticmethod + def SDL_StopTextInput(window: Any, /) -> bool: + """bool SDL_StopTextInput(SDL_Window *window)""" + + @staticmethod + def SDL_StorageReady(storage: Any, /) -> bool: + """bool SDL_StorageReady(SDL_Storage *storage)""" + + @staticmethod + def SDL_StretchSurface(src: Any, srcrect: Any, dst: Any, dstrect: Any, scaleMode: Any, /) -> bool: + """bool SDL_StretchSurface(SDL_Surface *src, const SDL_Rect *srcrect, SDL_Surface *dst, const SDL_Rect *dstrect, SDL_ScaleMode scaleMode)""" + + @staticmethod + def SDL_StringToGUID(pchGUID: Any, /) -> Any: + """SDL_GUID SDL_StringToGUID(const char *pchGUID)""" + + @staticmethod + def SDL_SubmitGPUCommandBuffer(command_buffer: Any, /) -> bool: + """bool SDL_SubmitGPUCommandBuffer(SDL_GPUCommandBuffer *command_buffer)""" + + @staticmethod + def SDL_SubmitGPUCommandBufferAndAcquireFence(command_buffer: Any, /) -> Any: + """SDL_GPUFence *SDL_SubmitGPUCommandBufferAndAcquireFence(SDL_GPUCommandBuffer *command_buffer)""" + + @staticmethod + def SDL_SurfaceHasAlternateImages(surface: Any, /) -> bool: + """bool SDL_SurfaceHasAlternateImages(SDL_Surface *surface)""" + + @staticmethod + def SDL_SurfaceHasColorKey(surface: Any, /) -> bool: + """bool SDL_SurfaceHasColorKey(SDL_Surface *surface)""" + + @staticmethod + def SDL_SurfaceHasRLE(surface: Any, /) -> bool: + """bool SDL_SurfaceHasRLE(SDL_Surface *surface)""" + + @staticmethod + def SDL_SyncWindow(window: Any, /) -> bool: + """bool SDL_SyncWindow(SDL_Window *window)""" + + @staticmethod + def SDL_TellIO(context: Any, /) -> Any: + """Sint64 SDL_TellIO(SDL_IOStream *context)""" + + @staticmethod + def SDL_TextInputActive(window: Any, /) -> bool: + """bool SDL_TextInputActive(SDL_Window *window)""" + + @staticmethod + def SDL_TimeFromWindows(dwLowDateTime: Any, dwHighDateTime: Any, /) -> Any: + """SDL_Time SDL_TimeFromWindows(Uint32 dwLowDateTime, Uint32 dwHighDateTime)""" + + @staticmethod + def SDL_TimeToDateTime(ticks: Any, dt: Any, localTime: bool, /) -> bool: + """bool SDL_TimeToDateTime(SDL_Time ticks, SDL_DateTime *dt, bool localTime)""" + + @staticmethod + def SDL_TimeToWindows(ticks: Any, dwLowDateTime: Any, dwHighDateTime: Any, /) -> None: + """void SDL_TimeToWindows(SDL_Time ticks, Uint32 *dwLowDateTime, Uint32 *dwHighDateTime)""" + + @staticmethod + def SDL_TryLockMutex(mutex: Any, /) -> bool: + """bool SDL_TryLockMutex(SDL_Mutex *mutex)""" + + @staticmethod + def SDL_TryLockRWLockForReading(rwlock: Any, /) -> bool: + """bool SDL_TryLockRWLockForReading(SDL_RWLock *rwlock)""" + + @staticmethod + def SDL_TryLockRWLockForWriting(rwlock: Any, /) -> bool: + """bool SDL_TryLockRWLockForWriting(SDL_RWLock *rwlock)""" + + @staticmethod + def SDL_TryLockSpinlock(lock: Any, /) -> bool: + """bool SDL_TryLockSpinlock(SDL_SpinLock *lock)""" + + @staticmethod + def SDL_TryWaitSemaphore(sem: Any, /) -> bool: + """bool SDL_TryWaitSemaphore(SDL_Semaphore *sem)""" + + @staticmethod + def SDL_UCS4ToUTF8(codepoint: Any, dst: Any, /) -> Any: + """char *SDL_UCS4ToUTF8(Uint32 codepoint, char *dst)""" + + @staticmethod + def SDL_UnbindAudioStream(stream: Any, /) -> None: + """void SDL_UnbindAudioStream(SDL_AudioStream *stream)""" + + @staticmethod + def SDL_UnbindAudioStreams(streams: Any, num_streams: int, /) -> None: + """void SDL_UnbindAudioStreams(SDL_AudioStream * const *streams, int num_streams)""" + + @staticmethod + def SDL_UnloadObject(handle: Any, /) -> None: + """void SDL_UnloadObject(SDL_SharedObject *handle)""" + + @staticmethod + def SDL_UnlockAudioStream(stream: Any, /) -> bool: + """bool SDL_UnlockAudioStream(SDL_AudioStream *stream)""" + + @staticmethod + def SDL_UnlockJoysticks() -> None: + """void SDL_UnlockJoysticks(void)""" + + @staticmethod + def SDL_UnlockMutex(mutex: Any, /) -> None: + """void SDL_UnlockMutex(SDL_Mutex *mutex)""" + + @staticmethod + def SDL_UnlockProperties(props: Any, /) -> None: + """void SDL_UnlockProperties(SDL_PropertiesID props)""" + + @staticmethod + def SDL_UnlockRWLock(rwlock: Any, /) -> None: + """void SDL_UnlockRWLock(SDL_RWLock *rwlock)""" + + @staticmethod + def SDL_UnlockSpinlock(lock: Any, /) -> None: + """void SDL_UnlockSpinlock(SDL_SpinLock *lock)""" + + @staticmethod + def SDL_UnlockSurface(surface: Any, /) -> None: + """void SDL_UnlockSurface(SDL_Surface *surface)""" + + @staticmethod + def SDL_UnlockTexture(texture: Any, /) -> None: + """void SDL_UnlockTexture(SDL_Texture *texture)""" + + @staticmethod + def SDL_UnmapGPUTransferBuffer(device: Any, transfer_buffer: Any, /) -> None: + """void SDL_UnmapGPUTransferBuffer(SDL_GPUDevice *device, SDL_GPUTransferBuffer *transfer_buffer)""" + + @staticmethod + def SDL_UnsetEnvironmentVariable(env: Any, name: Any, /) -> bool: + """bool SDL_UnsetEnvironmentVariable(SDL_Environment *env, const char *name)""" + + @staticmethod + def SDL_UpdateGamepads() -> None: + """void SDL_UpdateGamepads(void)""" + + @staticmethod + def SDL_UpdateHapticEffect(haptic: Any, effect: int, data: Any, /) -> bool: + """bool SDL_UpdateHapticEffect(SDL_Haptic *haptic, int effect, const SDL_HapticEffect *data)""" + + @staticmethod + def SDL_UpdateJoysticks() -> None: + """void SDL_UpdateJoysticks(void)""" + + @staticmethod + def SDL_UpdateNVTexture(texture: Any, rect: Any, Yplane: Any, Ypitch: int, UVplane: Any, UVpitch: int, /) -> bool: + """bool SDL_UpdateNVTexture(SDL_Texture *texture, const SDL_Rect *rect, const Uint8 *Yplane, int Ypitch, const Uint8 *UVplane, int UVpitch)""" + + @staticmethod + def SDL_UpdateSensors() -> None: + """void SDL_UpdateSensors(void)""" + + @staticmethod + def SDL_UpdateTexture(texture: Any, rect: Any, pixels: Any, pitch: int, /) -> bool: + """bool SDL_UpdateTexture(SDL_Texture *texture, const SDL_Rect *rect, const void *pixels, int pitch)""" + + @staticmethod + def SDL_UpdateTrays() -> None: + """void SDL_UpdateTrays(void)""" + + @staticmethod + def SDL_UpdateWindowSurface(window: Any, /) -> bool: + """bool SDL_UpdateWindowSurface(SDL_Window *window)""" + + @staticmethod + def SDL_UpdateWindowSurfaceRects(window: Any, rects: Any, numrects: int, /) -> bool: + """bool SDL_UpdateWindowSurfaceRects(SDL_Window *window, const SDL_Rect *rects, int numrects)""" + + @staticmethod + def SDL_UpdateYUVTexture( + texture: Any, rect: Any, Yplane: Any, Ypitch: int, Uplane: Any, Upitch: int, Vplane: Any, Vpitch: int, / + ) -> bool: + """bool SDL_UpdateYUVTexture(SDL_Texture *texture, const SDL_Rect *rect, const Uint8 *Yplane, int Ypitch, const Uint8 *Uplane, int Upitch, const Uint8 *Vplane, int Vpitch)""" + + @staticmethod + def SDL_UploadToGPUBuffer(copy_pass: Any, source: Any, destination: Any, cycle: bool, /) -> None: + """void SDL_UploadToGPUBuffer(SDL_GPUCopyPass *copy_pass, const SDL_GPUTransferBufferLocation *source, const SDL_GPUBufferRegion *destination, bool cycle)""" + + @staticmethod + def SDL_UploadToGPUTexture(copy_pass: Any, source: Any, destination: Any, cycle: bool, /) -> None: + """void SDL_UploadToGPUTexture(SDL_GPUCopyPass *copy_pass, const SDL_GPUTextureTransferInfo *source, const SDL_GPUTextureRegion *destination, bool cycle)""" + + @staticmethod + def SDL_WaitAndAcquireGPUSwapchainTexture( + command_buffer: Any, + window: Any, + swapchain_texture: Any, + swapchain_texture_width: Any, + swapchain_texture_height: Any, + /, + ) -> bool: + """bool SDL_WaitAndAcquireGPUSwapchainTexture(SDL_GPUCommandBuffer *command_buffer, SDL_Window *window, SDL_GPUTexture **swapchain_texture, Uint32 *swapchain_texture_width, Uint32 *swapchain_texture_height)""" + + @staticmethod + def SDL_WaitAsyncIOResult(queue: Any, outcome: Any, timeoutMS: Any, /) -> bool: + """bool SDL_WaitAsyncIOResult(SDL_AsyncIOQueue *queue, SDL_AsyncIOOutcome *outcome, Sint32 timeoutMS)""" + + @staticmethod + def SDL_WaitCondition(cond: Any, mutex: Any, /) -> None: + """void SDL_WaitCondition(SDL_Condition *cond, SDL_Mutex *mutex)""" + + @staticmethod + def SDL_WaitConditionTimeout(cond: Any, mutex: Any, timeoutMS: Any, /) -> bool: + """bool SDL_WaitConditionTimeout(SDL_Condition *cond, SDL_Mutex *mutex, Sint32 timeoutMS)""" + + @staticmethod + def SDL_WaitEvent(event: Any, /) -> bool: + """bool SDL_WaitEvent(SDL_Event *event)""" + + @staticmethod + def SDL_WaitEventTimeout(event: Any, timeoutMS: Any, /) -> bool: + """bool SDL_WaitEventTimeout(SDL_Event *event, Sint32 timeoutMS)""" + + @staticmethod + def SDL_WaitForGPUFences(device: Any, wait_all: bool, fences: Any, num_fences: Any, /) -> bool: + """bool SDL_WaitForGPUFences(SDL_GPUDevice *device, bool wait_all, SDL_GPUFence * const *fences, Uint32 num_fences)""" + + @staticmethod + def SDL_WaitForGPUIdle(device: Any, /) -> bool: + """bool SDL_WaitForGPUIdle(SDL_GPUDevice *device)""" + + @staticmethod + def SDL_WaitForGPUSwapchain(device: Any, window: Any, /) -> bool: + """bool SDL_WaitForGPUSwapchain(SDL_GPUDevice *device, SDL_Window *window)""" + + @staticmethod + def SDL_WaitProcess(process: Any, block: bool, exitcode: Any, /) -> bool: + """bool SDL_WaitProcess(SDL_Process *process, bool block, int *exitcode)""" + + @staticmethod + def SDL_WaitSemaphore(sem: Any, /) -> None: + """void SDL_WaitSemaphore(SDL_Semaphore *sem)""" + + @staticmethod + def SDL_WaitSemaphoreTimeout(sem: Any, timeoutMS: Any, /) -> bool: + """bool SDL_WaitSemaphoreTimeout(SDL_Semaphore *sem, Sint32 timeoutMS)""" + + @staticmethod + def SDL_WaitThread(thread: Any, status: Any, /) -> None: + """void SDL_WaitThread(SDL_Thread *thread, int *status)""" + + @staticmethod + def SDL_WarpMouseGlobal(x: float, y: float, /) -> bool: + """bool SDL_WarpMouseGlobal(float x, float y)""" + + @staticmethod + def SDL_WarpMouseInWindow(window: Any, x: float, y: float, /) -> None: + """void SDL_WarpMouseInWindow(SDL_Window *window, float x, float y)""" + + @staticmethod + def SDL_WasInit(flags: Any, /) -> Any: + """SDL_InitFlags SDL_WasInit(SDL_InitFlags flags)""" + + @staticmethod + def SDL_WindowHasSurface(window: Any, /) -> bool: + """bool SDL_WindowHasSurface(SDL_Window *window)""" + + @staticmethod + def SDL_WindowSupportsGPUPresentMode(device: Any, window: Any, present_mode: Any, /) -> bool: + """bool SDL_WindowSupportsGPUPresentMode(SDL_GPUDevice *device, SDL_Window *window, SDL_GPUPresentMode present_mode)""" + + @staticmethod + def SDL_WindowSupportsGPUSwapchainComposition(device: Any, window: Any, swapchain_composition: Any, /) -> bool: + """bool SDL_WindowSupportsGPUSwapchainComposition(SDL_GPUDevice *device, SDL_Window *window, SDL_GPUSwapchainComposition swapchain_composition)""" + + @staticmethod + def SDL_WriteAsyncIO(asyncio: Any, ptr: Any, offset: Any, size: Any, queue: Any, userdata: Any, /) -> bool: + """bool SDL_WriteAsyncIO(SDL_AsyncIO *asyncio, void *ptr, Uint64 offset, Uint64 size, SDL_AsyncIOQueue *queue, void *userdata)""" + + @staticmethod + def SDL_WriteIO(context: Any, ptr: Any, size: int, /) -> int: + """size_t SDL_WriteIO(SDL_IOStream *context, const void *ptr, size_t size)""" + + @staticmethod + def SDL_WriteS16BE(dst: Any, value: Any, /) -> bool: + """bool SDL_WriteS16BE(SDL_IOStream *dst, Sint16 value)""" + + @staticmethod + def SDL_WriteS16LE(dst: Any, value: Any, /) -> bool: + """bool SDL_WriteS16LE(SDL_IOStream *dst, Sint16 value)""" + + @staticmethod + def SDL_WriteS32BE(dst: Any, value: Any, /) -> bool: + """bool SDL_WriteS32BE(SDL_IOStream *dst, Sint32 value)""" + + @staticmethod + def SDL_WriteS32LE(dst: Any, value: Any, /) -> bool: + """bool SDL_WriteS32LE(SDL_IOStream *dst, Sint32 value)""" + + @staticmethod + def SDL_WriteS64BE(dst: Any, value: Any, /) -> bool: + """bool SDL_WriteS64BE(SDL_IOStream *dst, Sint64 value)""" + + @staticmethod + def SDL_WriteS64LE(dst: Any, value: Any, /) -> bool: + """bool SDL_WriteS64LE(SDL_IOStream *dst, Sint64 value)""" + + @staticmethod + def SDL_WriteS8(dst: Any, value: Any, /) -> bool: + """bool SDL_WriteS8(SDL_IOStream *dst, Sint8 value)""" + + @staticmethod + def SDL_WriteStorageFile(storage: Any, path: Any, source: Any, length: Any, /) -> bool: + """bool SDL_WriteStorageFile(SDL_Storage *storage, const char *path, const void *source, Uint64 length)""" + + @staticmethod + def SDL_WriteSurfacePixel(surface: Any, x: int, y: int, r: Any, g: Any, b: Any, a: Any, /) -> bool: + """bool SDL_WriteSurfacePixel(SDL_Surface *surface, int x, int y, Uint8 r, Uint8 g, Uint8 b, Uint8 a)""" + + @staticmethod + def SDL_WriteSurfacePixelFloat(surface: Any, x: int, y: int, r: float, g: float, b: float, a: float, /) -> bool: + """bool SDL_WriteSurfacePixelFloat(SDL_Surface *surface, int x, int y, float r, float g, float b, float a)""" + + @staticmethod + def SDL_WriteU16BE(dst: Any, value: Any, /) -> bool: + """bool SDL_WriteU16BE(SDL_IOStream *dst, Uint16 value)""" + + @staticmethod + def SDL_WriteU16LE(dst: Any, value: Any, /) -> bool: + """bool SDL_WriteU16LE(SDL_IOStream *dst, Uint16 value)""" + + @staticmethod + def SDL_WriteU32BE(dst: Any, value: Any, /) -> bool: + """bool SDL_WriteU32BE(SDL_IOStream *dst, Uint32 value)""" + + @staticmethod + def SDL_WriteU32LE(dst: Any, value: Any, /) -> bool: + """bool SDL_WriteU32LE(SDL_IOStream *dst, Uint32 value)""" + + @staticmethod + def SDL_WriteU64BE(dst: Any, value: Any, /) -> bool: + """bool SDL_WriteU64BE(SDL_IOStream *dst, Uint64 value)""" + + @staticmethod + def SDL_WriteU64LE(dst: Any, value: Any, /) -> bool: + """bool SDL_WriteU64LE(SDL_IOStream *dst, Uint64 value)""" + + @staticmethod + def SDL_WriteU8(dst: Any, value: Any, /) -> bool: + """bool SDL_WriteU8(SDL_IOStream *dst, Uint8 value)""" + + @staticmethod + def SDL_abs(x: int, /) -> int: + """int SDL_abs(int x)""" + + @staticmethod + def SDL_acos(x: float, /) -> float: + """double SDL_acos(double x)""" + + @staticmethod + def SDL_acosf(x: float, /) -> float: + """float SDL_acosf(float x)""" + + @staticmethod + def SDL_aligned_alloc(alignment: int, size: int, /) -> Any: + """void *SDL_aligned_alloc(size_t alignment, size_t size)""" + + @staticmethod + def SDL_aligned_free(mem: Any, /) -> None: + """void SDL_aligned_free(void *mem)""" + + @staticmethod + def SDL_asin(x: float, /) -> float: + """double SDL_asin(double x)""" + + @staticmethod + def SDL_asinf(x: float, /) -> float: + """float SDL_asinf(float x)""" + + @staticmethod + def SDL_asprintf(strp: Any, fmt: Any, /, *__args: Any) -> int: + """int SDL_asprintf(char **strp, const char *fmt, ...)""" + + @staticmethod + def SDL_atan(x: float, /) -> float: + """double SDL_atan(double x)""" + + @staticmethod + def SDL_atan2(y: float, x: float, /) -> float: + """double SDL_atan2(double y, double x)""" + + @staticmethod + def SDL_atan2f(y: float, x: float, /) -> float: + """float SDL_atan2f(float y, float x)""" + + @staticmethod + def SDL_atanf(x: float, /) -> float: + """float SDL_atanf(float x)""" + + @staticmethod + def SDL_atof(str: Any, /) -> float: + """double SDL_atof(const char *str)""" + + @staticmethod + def SDL_atoi(str: Any, /) -> int: + """int SDL_atoi(const char *str)""" + + @staticmethod + def SDL_bsearch(key: Any, base: Any, nmemb: int, size: int, compare: Any, /) -> Any: + """void *SDL_bsearch(const void *key, const void *base, size_t nmemb, size_t size, SDL_CompareCallback compare)""" + + @staticmethod + def SDL_bsearch_r(key: Any, base: Any, nmemb: int, size: int, compare: Any, userdata: Any, /) -> Any: + """void *SDL_bsearch_r(const void *key, const void *base, size_t nmemb, size_t size, SDL_CompareCallback_r compare, void *userdata)""" + + @staticmethod + def SDL_calloc(nmemb: int, size: int, /) -> Any: + """void *SDL_calloc(size_t nmemb, size_t size)""" + + @staticmethod + def SDL_ceil(x: float, /) -> float: + """double SDL_ceil(double x)""" + + @staticmethod + def SDL_ceilf(x: float, /) -> float: + """float SDL_ceilf(float x)""" + + @staticmethod + def SDL_copysign(x: float, y: float, /) -> float: + """double SDL_copysign(double x, double y)""" + + @staticmethod + def SDL_copysignf(x: float, y: float, /) -> float: + """float SDL_copysignf(float x, float y)""" + + @staticmethod + def SDL_cos(x: float, /) -> float: + """double SDL_cos(double x)""" + + @staticmethod + def SDL_cosf(x: float, /) -> float: + """float SDL_cosf(float x)""" + + @staticmethod + def SDL_crc16(crc: Any, data: Any, len: int, /) -> Any: + """Uint16 SDL_crc16(Uint16 crc, const void *data, size_t len)""" + + @staticmethod + def SDL_crc32(crc: Any, data: Any, len: int, /) -> Any: + """Uint32 SDL_crc32(Uint32 crc, const void *data, size_t len)""" + + @staticmethod + def SDL_exp(x: float, /) -> float: + """double SDL_exp(double x)""" + + @staticmethod + def SDL_expf(x: float, /) -> float: + """float SDL_expf(float x)""" + + @staticmethod + def SDL_fabs(x: float, /) -> float: + """double SDL_fabs(double x)""" + + @staticmethod + def SDL_fabsf(x: float, /) -> float: + """float SDL_fabsf(float x)""" + + @staticmethod + def SDL_floor(x: float, /) -> float: + """double SDL_floor(double x)""" + + @staticmethod + def SDL_floorf(x: float, /) -> float: + """float SDL_floorf(float x)""" + + @staticmethod + def SDL_fmod(x: float, y: float, /) -> float: + """double SDL_fmod(double x, double y)""" + + @staticmethod + def SDL_fmodf(x: float, y: float, /) -> float: + """float SDL_fmodf(float x, float y)""" + + @staticmethod + def SDL_free(mem: Any, /) -> None: + """void SDL_free(void *mem)""" + + @staticmethod + def SDL_getenv(name: Any, /) -> Any: + """const char *SDL_getenv(const char *name)""" + + @staticmethod + def SDL_getenv_unsafe(name: Any, /) -> Any: + """const char *SDL_getenv_unsafe(const char *name)""" + + @staticmethod + def SDL_hid_ble_scan(active: bool, /) -> None: + """void SDL_hid_ble_scan(bool active)""" + + @staticmethod + def SDL_hid_close(dev: Any, /) -> int: + """int SDL_hid_close(SDL_hid_device *dev)""" + + @staticmethod + def SDL_hid_device_change_count() -> Any: + """Uint32 SDL_hid_device_change_count(void)""" + + @staticmethod + def SDL_hid_enumerate(vendor_id: Any, product_id: Any, /) -> Any: + """SDL_hid_device_info *SDL_hid_enumerate(unsigned short vendor_id, unsigned short product_id)""" + + @staticmethod + def SDL_hid_exit() -> int: + """int SDL_hid_exit(void)""" + + @staticmethod + def SDL_hid_free_enumeration(devs: Any, /) -> None: + """void SDL_hid_free_enumeration(SDL_hid_device_info *devs)""" + + @staticmethod + def SDL_hid_get_device_info(dev: Any, /) -> Any: + """SDL_hid_device_info *SDL_hid_get_device_info(SDL_hid_device *dev)""" + + @staticmethod + def SDL_hid_get_feature_report(dev: Any, data: Any, length: int, /) -> int: + """int SDL_hid_get_feature_report(SDL_hid_device *dev, unsigned char *data, size_t length)""" + + @staticmethod + def SDL_hid_get_indexed_string(dev: Any, string_index: int, string: Any, maxlen: int, /) -> int: + """int SDL_hid_get_indexed_string(SDL_hid_device *dev, int string_index, wchar_t *string, size_t maxlen)""" + + @staticmethod + def SDL_hid_get_input_report(dev: Any, data: Any, length: int, /) -> int: + """int SDL_hid_get_input_report(SDL_hid_device *dev, unsigned char *data, size_t length)""" + + @staticmethod + def SDL_hid_get_manufacturer_string(dev: Any, string: Any, maxlen: int, /) -> int: + """int SDL_hid_get_manufacturer_string(SDL_hid_device *dev, wchar_t *string, size_t maxlen)""" + + @staticmethod + def SDL_hid_get_product_string(dev: Any, string: Any, maxlen: int, /) -> int: + """int SDL_hid_get_product_string(SDL_hid_device *dev, wchar_t *string, size_t maxlen)""" + + @staticmethod + def SDL_hid_get_report_descriptor(dev: Any, buf: Any, buf_size: int, /) -> int: + """int SDL_hid_get_report_descriptor(SDL_hid_device *dev, unsigned char *buf, size_t buf_size)""" + + @staticmethod + def SDL_hid_get_serial_number_string(dev: Any, string: Any, maxlen: int, /) -> int: + """int SDL_hid_get_serial_number_string(SDL_hid_device *dev, wchar_t *string, size_t maxlen)""" + + @staticmethod + def SDL_hid_init() -> int: + """int SDL_hid_init(void)""" + + @staticmethod + def SDL_hid_open(vendor_id: Any, product_id: Any, serial_number: Any, /) -> Any: + """SDL_hid_device *SDL_hid_open(unsigned short vendor_id, unsigned short product_id, const wchar_t *serial_number)""" + + @staticmethod + def SDL_hid_open_path(path: Any, /) -> Any: + """SDL_hid_device *SDL_hid_open_path(const char *path)""" + + @staticmethod + def SDL_hid_read(dev: Any, data: Any, length: int, /) -> int: + """int SDL_hid_read(SDL_hid_device *dev, unsigned char *data, size_t length)""" + + @staticmethod + def SDL_hid_read_timeout(dev: Any, data: Any, length: int, milliseconds: int, /) -> int: + """int SDL_hid_read_timeout(SDL_hid_device *dev, unsigned char *data, size_t length, int milliseconds)""" + + @staticmethod + def SDL_hid_send_feature_report(dev: Any, data: Any, length: int, /) -> int: + """int SDL_hid_send_feature_report(SDL_hid_device *dev, const unsigned char *data, size_t length)""" + + @staticmethod + def SDL_hid_set_nonblocking(dev: Any, nonblock: int, /) -> int: + """int SDL_hid_set_nonblocking(SDL_hid_device *dev, int nonblock)""" + + @staticmethod + def SDL_hid_write(dev: Any, data: Any, length: int, /) -> int: + """int SDL_hid_write(SDL_hid_device *dev, const unsigned char *data, size_t length)""" + + @staticmethod + def SDL_iconv(cd: Any, inbuf: Any, inbytesleft: Any, outbuf: Any, outbytesleft: Any, /) -> int: + """size_t SDL_iconv(SDL_iconv_t cd, const char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft)""" + + @staticmethod + def SDL_iconv_close(cd: Any, /) -> int: + """int SDL_iconv_close(SDL_iconv_t cd)""" + + @staticmethod + def SDL_iconv_open(tocode: Any, fromcode: Any, /) -> Any: + """SDL_iconv_t SDL_iconv_open(const char *tocode, const char *fromcode)""" + + @staticmethod + def SDL_iconv_string(tocode: Any, fromcode: Any, inbuf: Any, inbytesleft: int, /) -> Any: + """char *SDL_iconv_string(const char *tocode, const char *fromcode, const char *inbuf, size_t inbytesleft)""" + + @staticmethod + def SDL_isalnum(x: int, /) -> int: + """int SDL_isalnum(int x)""" + + @staticmethod + def SDL_isalpha(x: int, /) -> int: + """int SDL_isalpha(int x)""" + + @staticmethod + def SDL_isblank(x: int, /) -> int: + """int SDL_isblank(int x)""" + + @staticmethod + def SDL_iscntrl(x: int, /) -> int: + """int SDL_iscntrl(int x)""" + + @staticmethod + def SDL_isdigit(x: int, /) -> int: + """int SDL_isdigit(int x)""" + + @staticmethod + def SDL_isgraph(x: int, /) -> int: + """int SDL_isgraph(int x)""" + + @staticmethod + def SDL_isinf(x: float, /) -> int: + """int SDL_isinf(double x)""" + + @staticmethod + def SDL_isinff(x: float, /) -> int: + """int SDL_isinff(float x)""" + + @staticmethod + def SDL_islower(x: int, /) -> int: + """int SDL_islower(int x)""" + + @staticmethod + def SDL_isnan(x: float, /) -> int: + """int SDL_isnan(double x)""" + + @staticmethod + def SDL_isnanf(x: float, /) -> int: + """int SDL_isnanf(float x)""" + + @staticmethod + def SDL_isprint(x: int, /) -> int: + """int SDL_isprint(int x)""" + + @staticmethod + def SDL_ispunct(x: int, /) -> int: + """int SDL_ispunct(int x)""" + + @staticmethod + def SDL_isspace(x: int, /) -> int: + """int SDL_isspace(int x)""" + + @staticmethod + def SDL_isupper(x: int, /) -> int: + """int SDL_isupper(int x)""" + + @staticmethod + def SDL_isxdigit(x: int, /) -> int: + """int SDL_isxdigit(int x)""" + + @staticmethod + def SDL_itoa(value: int, str: Any, radix: int, /) -> Any: + """char *SDL_itoa(int value, char *str, int radix)""" + + @staticmethod + def SDL_lltoa(value: Any, str: Any, radix: int, /) -> Any: + """char *SDL_lltoa(long long value, char *str, int radix)""" + + @staticmethod + def SDL_log(x: float, /) -> float: + """double SDL_log(double x)""" + + @staticmethod + def SDL_log10(x: float, /) -> float: + """double SDL_log10(double x)""" + + @staticmethod + def SDL_log10f(x: float, /) -> float: + """float SDL_log10f(float x)""" + + @staticmethod + def SDL_logf(x: float, /) -> float: + """float SDL_logf(float x)""" + + @staticmethod + def SDL_lround(x: float, /) -> Any: + """long SDL_lround(double x)""" + + @staticmethod + def SDL_lroundf(x: float, /) -> Any: + """long SDL_lroundf(float x)""" + + @staticmethod + def SDL_ltoa(value: Any, str: Any, radix: int, /) -> Any: + """char *SDL_ltoa(long value, char *str, int radix)""" + + @staticmethod + def SDL_malloc(size: int, /) -> Any: + """void *SDL_malloc(size_t size)""" + + @staticmethod + def SDL_memcmp(s1: Any, s2: Any, len: int, /) -> int: + """int SDL_memcmp(const void *s1, const void *s2, size_t len)""" + + @staticmethod + def SDL_memcpy(dst: Any, src: Any, len: int, /) -> Any: + """void *SDL_memcpy(void *dst, const void *src, size_t len)""" + + @staticmethod + def SDL_memmove(dst: Any, src: Any, len: int, /) -> Any: + """void *SDL_memmove(void *dst, const void *src, size_t len)""" + + @staticmethod + def SDL_memset(dst: Any, c: int, len: int, /) -> Any: + """void *SDL_memset(void *dst, int c, size_t len)""" + + @staticmethod + def SDL_memset4(dst: Any, val: Any, dwords: int, /) -> Any: + """void *SDL_memset4(void *dst, Uint32 val, size_t dwords)""" + + @staticmethod + def SDL_modf(x: float, y: Any, /) -> float: + """double SDL_modf(double x, double *y)""" + + @staticmethod + def SDL_modff(x: float, y: Any, /) -> float: + """float SDL_modff(float x, float *y)""" + + @staticmethod + def SDL_murmur3_32(data: Any, len: int, seed: Any, /) -> Any: + """Uint32 SDL_murmur3_32(const void *data, size_t len, Uint32 seed)""" + + @staticmethod + def SDL_pow(x: float, y: float, /) -> float: + """double SDL_pow(double x, double y)""" + + @staticmethod + def SDL_powf(x: float, y: float, /) -> float: + """float SDL_powf(float x, float y)""" + + @staticmethod + def SDL_qsort(base: Any, nmemb: int, size: int, compare: Any, /) -> None: + """void SDL_qsort(void *base, size_t nmemb, size_t size, SDL_CompareCallback compare)""" + + @staticmethod + def SDL_qsort_r(base: Any, nmemb: int, size: int, compare: Any, userdata: Any, /) -> None: + """void SDL_qsort_r(void *base, size_t nmemb, size_t size, SDL_CompareCallback_r compare, void *userdata)""" + + @staticmethod + def SDL_rand(n: Any, /) -> Any: + """Sint32 SDL_rand(Sint32 n)""" + + @staticmethod + def SDL_rand_bits() -> Any: + """Uint32 SDL_rand_bits(void)""" + + @staticmethod + def SDL_rand_bits_r(state: Any, /) -> Any: + """Uint32 SDL_rand_bits_r(Uint64 *state)""" + + @staticmethod + def SDL_rand_r(state: Any, n: Any, /) -> Any: + """Sint32 SDL_rand_r(Uint64 *state, Sint32 n)""" + + @staticmethod + def SDL_randf() -> float: + """float SDL_randf(void)""" + + @staticmethod + def SDL_randf_r(state: Any, /) -> float: + """float SDL_randf_r(Uint64 *state)""" + + @staticmethod + def SDL_realloc(mem: Any, size: int, /) -> Any: + """void *SDL_realloc(void *mem, size_t size)""" + + @staticmethod + def SDL_round(x: float, /) -> float: + """double SDL_round(double x)""" + + @staticmethod + def SDL_roundf(x: float, /) -> float: + """float SDL_roundf(float x)""" + + @staticmethod + def SDL_scalbn(x: float, n: int, /) -> float: + """double SDL_scalbn(double x, int n)""" + + @staticmethod + def SDL_scalbnf(x: float, n: int, /) -> float: + """float SDL_scalbnf(float x, int n)""" + + @staticmethod + def SDL_setenv_unsafe(name: Any, value: Any, overwrite: int, /) -> int: + """int SDL_setenv_unsafe(const char *name, const char *value, int overwrite)""" + + @staticmethod + def SDL_sin(x: float, /) -> float: + """double SDL_sin(double x)""" + + @staticmethod + def SDL_sinf(x: float, /) -> float: + """float SDL_sinf(float x)""" + + @staticmethod + def SDL_snprintf(text: Any, maxlen: int, fmt: Any, /, *__args: Any) -> int: + """int SDL_snprintf(char *text, size_t maxlen, const char *fmt, ...)""" + + @staticmethod + def SDL_sqrt(x: float, /) -> float: + """double SDL_sqrt(double x)""" + + @staticmethod + def SDL_sqrtf(x: float, /) -> float: + """float SDL_sqrtf(float x)""" + + @staticmethod + def SDL_srand(seed: Any, /) -> None: + """void SDL_srand(Uint64 seed)""" + + @staticmethod + def SDL_sscanf(text: Any, fmt: Any, /, *__args: Any) -> int: + """int SDL_sscanf(const char *text, const char *fmt, ...)""" + + @staticmethod + def SDL_strcasecmp(str1: Any, str2: Any, /) -> int: + """int SDL_strcasecmp(const char *str1, const char *str2)""" + + @staticmethod + def SDL_strcasestr(haystack: Any, needle: Any, /) -> Any: + """char *SDL_strcasestr(const char *haystack, const char *needle)""" + + @staticmethod + def SDL_strchr(str: Any, c: int, /) -> Any: + """char *SDL_strchr(const char *str, int c)""" + + @staticmethod + def SDL_strcmp(str1: Any, str2: Any, /) -> int: + """int SDL_strcmp(const char *str1, const char *str2)""" + + @staticmethod + def SDL_strdup(str: Any, /) -> Any: + """char *SDL_strdup(const char *str)""" + + @staticmethod + def SDL_strlcat(dst: Any, src: Any, maxlen: int, /) -> int: + """size_t SDL_strlcat(char *dst, const char *src, size_t maxlen)""" + + @staticmethod + def SDL_strlcpy(dst: Any, src: Any, maxlen: int, /) -> int: + """size_t SDL_strlcpy(char *dst, const char *src, size_t maxlen)""" + + @staticmethod + def SDL_strlen(str: Any, /) -> int: + """size_t SDL_strlen(const char *str)""" + + @staticmethod + def SDL_strlwr(str: Any, /) -> Any: + """char *SDL_strlwr(char *str)""" + + @staticmethod + def SDL_strncasecmp(str1: Any, str2: Any, maxlen: int, /) -> int: + """int SDL_strncasecmp(const char *str1, const char *str2, size_t maxlen)""" + + @staticmethod + def SDL_strncmp(str1: Any, str2: Any, maxlen: int, /) -> int: + """int SDL_strncmp(const char *str1, const char *str2, size_t maxlen)""" + + @staticmethod + def SDL_strndup(str: Any, maxlen: int, /) -> Any: + """char *SDL_strndup(const char *str, size_t maxlen)""" + + @staticmethod + def SDL_strnlen(str: Any, maxlen: int, /) -> int: + """size_t SDL_strnlen(const char *str, size_t maxlen)""" + + @staticmethod + def SDL_strnstr(haystack: Any, needle: Any, maxlen: int, /) -> Any: + """char *SDL_strnstr(const char *haystack, const char *needle, size_t maxlen)""" + + @staticmethod + def SDL_strpbrk(str: Any, breakset: Any, /) -> Any: + """char *SDL_strpbrk(const char *str, const char *breakset)""" + + @staticmethod + def SDL_strrchr(str: Any, c: int, /) -> Any: + """char *SDL_strrchr(const char *str, int c)""" + + @staticmethod + def SDL_strrev(str: Any, /) -> Any: + """char *SDL_strrev(char *str)""" + + @staticmethod + def SDL_strstr(haystack: Any, needle: Any, /) -> Any: + """char *SDL_strstr(const char *haystack, const char *needle)""" + + @staticmethod + def SDL_strtod(str: Any, endp: Any, /) -> float: + """double SDL_strtod(const char *str, char **endp)""" + + @staticmethod + def SDL_strtok_r(str: Any, delim: Any, saveptr: Any, /) -> Any: + """char *SDL_strtok_r(char *str, const char *delim, char **saveptr)""" + + @staticmethod + def SDL_strtol(str: Any, endp: Any, base: int, /) -> Any: + """long SDL_strtol(const char *str, char **endp, int base)""" + + @staticmethod + def SDL_strtoll(str: Any, endp: Any, base: int, /) -> Any: + """long long SDL_strtoll(const char *str, char **endp, int base)""" + + @staticmethod + def SDL_strtoul(str: Any, endp: Any, base: int, /) -> Any: + """unsigned long SDL_strtoul(const char *str, char **endp, int base)""" + + @staticmethod + def SDL_strtoull(str: Any, endp: Any, base: int, /) -> Any: + """unsigned long long SDL_strtoull(const char *str, char **endp, int base)""" + + @staticmethod + def SDL_strupr(str: Any, /) -> Any: + """char *SDL_strupr(char *str)""" + + @staticmethod + def SDL_swprintf(text: Any, maxlen: int, fmt: Any, /, *__args: Any) -> int: + """int SDL_swprintf(wchar_t *text, size_t maxlen, const wchar_t *fmt, ...)""" + + @staticmethod + def SDL_tan(x: float, /) -> float: + """double SDL_tan(double x)""" + + @staticmethod + def SDL_tanf(x: float, /) -> float: + """float SDL_tanf(float x)""" + + @staticmethod + def SDL_tolower(x: int, /) -> int: + """int SDL_tolower(int x)""" + + @staticmethod + def SDL_toupper(x: int, /) -> int: + """int SDL_toupper(int x)""" + + @staticmethod + def SDL_trunc(x: float, /) -> float: + """double SDL_trunc(double x)""" + + @staticmethod + def SDL_truncf(x: float, /) -> float: + """float SDL_truncf(float x)""" + + @staticmethod + def SDL_uitoa(value: int, str: Any, radix: int, /) -> Any: + """char *SDL_uitoa(unsigned int value, char *str, int radix)""" + + @staticmethod + def SDL_ulltoa(value: Any, str: Any, radix: int, /) -> Any: + """char *SDL_ulltoa(unsigned long long value, char *str, int radix)""" + + @staticmethod + def SDL_ultoa(value: Any, str: Any, radix: int, /) -> Any: + """char *SDL_ultoa(unsigned long value, char *str, int radix)""" + + @staticmethod + def SDL_unsetenv_unsafe(name: Any, /) -> int: + """int SDL_unsetenv_unsafe(const char *name)""" + + @staticmethod + def SDL_utf8strlcpy(dst: Any, src: Any, dst_bytes: int, /) -> int: + """size_t SDL_utf8strlcpy(char *dst, const char *src, size_t dst_bytes)""" + + @staticmethod + def SDL_utf8strlen(str: Any, /) -> int: + """size_t SDL_utf8strlen(const char *str)""" + + @staticmethod + def SDL_utf8strnlen(str: Any, bytes: int, /) -> int: + """size_t SDL_utf8strnlen(const char *str, size_t bytes)""" + + @staticmethod + def SDL_wcscasecmp(str1: Any, str2: Any, /) -> int: + """int SDL_wcscasecmp(const wchar_t *str1, const wchar_t *str2)""" + + @staticmethod + def SDL_wcscmp(str1: Any, str2: Any, /) -> int: + """int SDL_wcscmp(const wchar_t *str1, const wchar_t *str2)""" + + @staticmethod + def SDL_wcsdup(wstr: Any, /) -> Any: + """wchar_t *SDL_wcsdup(const wchar_t *wstr)""" + + @staticmethod + def SDL_wcslcat(dst: Any, src: Any, maxlen: int, /) -> int: + """size_t SDL_wcslcat(wchar_t *dst, const wchar_t *src, size_t maxlen)""" + + @staticmethod + def SDL_wcslcpy(dst: Any, src: Any, maxlen: int, /) -> int: + """size_t SDL_wcslcpy(wchar_t *dst, const wchar_t *src, size_t maxlen)""" + + @staticmethod + def SDL_wcslen(wstr: Any, /) -> int: + """size_t SDL_wcslen(const wchar_t *wstr)""" + + @staticmethod + def SDL_wcsncasecmp(str1: Any, str2: Any, maxlen: int, /) -> int: + """int SDL_wcsncasecmp(const wchar_t *str1, const wchar_t *str2, size_t maxlen)""" + + @staticmethod + def SDL_wcsncmp(str1: Any, str2: Any, maxlen: int, /) -> int: + """int SDL_wcsncmp(const wchar_t *str1, const wchar_t *str2, size_t maxlen)""" + + @staticmethod + def SDL_wcsnlen(wstr: Any, maxlen: int, /) -> int: + """size_t SDL_wcsnlen(const wchar_t *wstr, size_t maxlen)""" + + @staticmethod + def SDL_wcsnstr(haystack: Any, needle: Any, maxlen: int, /) -> Any: + """wchar_t *SDL_wcsnstr(const wchar_t *haystack, const wchar_t *needle, size_t maxlen)""" + + @staticmethod + def SDL_wcsstr(haystack: Any, needle: Any, /) -> Any: + """wchar_t *SDL_wcsstr(const wchar_t *haystack, const wchar_t *needle)""" + + @staticmethod + def SDL_wcstol(str: Any, endp: Any, base: int, /) -> Any: + """long SDL_wcstol(const wchar_t *str, wchar_t **endp, int base)""" + + @staticmethod + def TCOD_bsp_contains(node: Any, x: int, y: int, /) -> bool: + """bool TCOD_bsp_contains(TCOD_bsp_t *node, int x, int y)""" + + @staticmethod + def TCOD_bsp_delete(node: Any, /) -> None: + """void TCOD_bsp_delete(TCOD_bsp_t *node)""" + + @staticmethod + def TCOD_bsp_father(node: Any, /) -> Any: + """TCOD_bsp_t *TCOD_bsp_father(TCOD_bsp_t *node)""" + + @staticmethod + def TCOD_bsp_find_node(node: Any, x: int, y: int, /) -> Any: + """TCOD_bsp_t *TCOD_bsp_find_node(TCOD_bsp_t *node, int x, int y)""" + + @staticmethod + def TCOD_bsp_is_leaf(node: Any, /) -> bool: + """bool TCOD_bsp_is_leaf(TCOD_bsp_t *node)""" + + @staticmethod + def TCOD_bsp_left(node: Any, /) -> Any: + """TCOD_bsp_t *TCOD_bsp_left(TCOD_bsp_t *node)""" + + @staticmethod + def TCOD_bsp_new() -> Any: + """TCOD_bsp_t *TCOD_bsp_new(void)""" + + @staticmethod + def TCOD_bsp_new_with_size(x: int, y: int, w: int, h: int, /) -> Any: + """TCOD_bsp_t *TCOD_bsp_new_with_size(int x, int y, int w, int h)""" + + @staticmethod + def TCOD_bsp_remove_sons(node: Any, /) -> None: + """void TCOD_bsp_remove_sons(TCOD_bsp_t *node)""" + + @staticmethod + def TCOD_bsp_resize(node: Any, x: int, y: int, w: int, h: int, /) -> None: + """void TCOD_bsp_resize(TCOD_bsp_t *node, int x, int y, int w, int h)""" + + @staticmethod + def TCOD_bsp_right(node: Any, /) -> Any: + """TCOD_bsp_t *TCOD_bsp_right(TCOD_bsp_t *node)""" + + @staticmethod + def TCOD_bsp_split_once(node: Any, horizontal: bool, position: int, /) -> None: + """void TCOD_bsp_split_once(TCOD_bsp_t *node, bool horizontal, int position)""" + + @staticmethod + def TCOD_bsp_split_recursive( + node: Any, randomizer: Any, nb: int, minHSize: int, minVSize: int, maxHRatio: float, maxVRatio: float, / + ) -> None: + """void TCOD_bsp_split_recursive(TCOD_bsp_t *node, TCOD_Random *randomizer, int nb, int minHSize, int minVSize, float maxHRatio, float maxVRatio)""" + + @staticmethod + def TCOD_bsp_traverse_in_order(node: Any, listener: Any, userData: Any, /) -> bool: + """bool TCOD_bsp_traverse_in_order(TCOD_bsp_t *node, TCOD_bsp_callback_t listener, void *userData)""" + + @staticmethod + def TCOD_bsp_traverse_inverted_level_order(node: Any, listener: Any, userData: Any, /) -> bool: + """bool TCOD_bsp_traverse_inverted_level_order(TCOD_bsp_t *node, TCOD_bsp_callback_t listener, void *userData)""" + + @staticmethod + def TCOD_bsp_traverse_level_order(node: Any, listener: Any, userData: Any, /) -> bool: + """bool TCOD_bsp_traverse_level_order(TCOD_bsp_t *node, TCOD_bsp_callback_t listener, void *userData)""" + + @staticmethod + def TCOD_bsp_traverse_post_order(node: Any, listener: Any, userData: Any, /) -> bool: + """bool TCOD_bsp_traverse_post_order(TCOD_bsp_t *node, TCOD_bsp_callback_t listener, void *userData)""" + + @staticmethod + def TCOD_bsp_traverse_pre_order(node: Any, listener: Any, userData: Any, /) -> bool: + """bool TCOD_bsp_traverse_pre_order(TCOD_bsp_t *node, TCOD_bsp_callback_t listener, void *userData)""" + + @staticmethod + def TCOD_clear_error() -> None: + """void TCOD_clear_error(void)""" + + @staticmethod + def TCOD_close_library(arg0: Any, /) -> None: + """void TCOD_close_library(TCOD_library_t)""" + + @staticmethod + def TCOD_color_HSV(hue: float, saturation: float, value: float, /) -> Any: + """TCOD_color_t TCOD_color_HSV(float hue, float saturation, float value)""" + + @staticmethod + def TCOD_color_RGB(r: Any, g: Any, b: Any, /) -> Any: + """TCOD_color_t TCOD_color_RGB(uint8_t r, uint8_t g, uint8_t b)""" + + @staticmethod + def TCOD_color_add(c1: Any, c2: Any, /) -> Any: + """TCOD_color_t TCOD_color_add(TCOD_color_t c1, TCOD_color_t c2)""" + + @staticmethod + def TCOD_color_add_wrapper(c1: Any, c2: Any, /) -> Any: + """colornum_t TCOD_color_add_wrapper(colornum_t c1, colornum_t c2)""" + + @staticmethod + def TCOD_color_alpha_blend(dst: Any, src: Any, /) -> None: + """void TCOD_color_alpha_blend(TCOD_ColorRGBA *dst, const TCOD_ColorRGBA *src)""" + + @staticmethod + def TCOD_color_equals(c1: Any, c2: Any, /) -> bool: + """bool TCOD_color_equals(TCOD_color_t c1, TCOD_color_t c2)""" + + @staticmethod + def TCOD_color_equals_wrapper(c1: Any, c2: Any, /) -> bool: + """bool TCOD_color_equals_wrapper(colornum_t c1, colornum_t c2)""" + + @staticmethod + def TCOD_color_gen_map(map: Any, nb_key: int, key_color: Any, key_index: Any, /) -> None: + """void TCOD_color_gen_map(TCOD_color_t *map, int nb_key, const TCOD_color_t *key_color, const int *key_index)""" + + @staticmethod + def TCOD_color_get_HSV(color: Any, hue: Any, saturation: Any, value: Any, /) -> None: + """void TCOD_color_get_HSV(TCOD_color_t color, float *hue, float *saturation, float *value)""" + + @staticmethod + def TCOD_color_get_HSV_wrapper(c: Any, h: Any, s: Any, v: Any, /) -> None: + """void TCOD_color_get_HSV_wrapper(colornum_t c, float *h, float *s, float *v)""" + + @staticmethod + def TCOD_color_get_hue(color: Any, /) -> float: + """float TCOD_color_get_hue(TCOD_color_t color)""" + + @staticmethod + def TCOD_color_get_hue_wrapper(c: Any, /) -> float: + """float TCOD_color_get_hue_wrapper(colornum_t c)""" + + @staticmethod + def TCOD_color_get_saturation(color: Any, /) -> float: + """float TCOD_color_get_saturation(TCOD_color_t color)""" + + @staticmethod + def TCOD_color_get_saturation_wrapper(c: Any, /) -> float: + """float TCOD_color_get_saturation_wrapper(colornum_t c)""" + + @staticmethod + def TCOD_color_get_value(color: Any, /) -> float: + """float TCOD_color_get_value(TCOD_color_t color)""" + + @staticmethod + def TCOD_color_get_value_wrapper(c: Any, /) -> float: + """float TCOD_color_get_value_wrapper(colornum_t c)""" + + @staticmethod + def TCOD_color_lerp(c1: Any, c2: Any, coef: float, /) -> Any: + """TCOD_color_t TCOD_color_lerp(TCOD_color_t c1, TCOD_color_t c2, float coef)""" + + @staticmethod + def TCOD_color_lerp_wrapper(c1: Any, c2: Any, coef: float, /) -> Any: + """colornum_t TCOD_color_lerp_wrapper(colornum_t c1, colornum_t c2, float coef)""" + + @staticmethod + def TCOD_color_multiply(c1: Any, c2: Any, /) -> Any: + """TCOD_color_t TCOD_color_multiply(TCOD_color_t c1, TCOD_color_t c2)""" + + @staticmethod + def TCOD_color_multiply_scalar(c1: Any, value: float, /) -> Any: + """TCOD_color_t TCOD_color_multiply_scalar(TCOD_color_t c1, float value)""" + + @staticmethod + def TCOD_color_multiply_scalar_wrapper(c1: Any, value: float, /) -> Any: + """colornum_t TCOD_color_multiply_scalar_wrapper(colornum_t c1, float value)""" + + @staticmethod + def TCOD_color_multiply_wrapper(c1: Any, c2: Any, /) -> Any: + """colornum_t TCOD_color_multiply_wrapper(colornum_t c1, colornum_t c2)""" + + @staticmethod + def TCOD_color_scale_HSV(color: Any, saturation_coef: float, value_coef: float, /) -> None: + """void TCOD_color_scale_HSV(TCOD_color_t *color, float saturation_coef, float value_coef)""" + + @staticmethod + def TCOD_color_set_HSV(color: Any, hue: float, saturation: float, value: float, /) -> None: + """void TCOD_color_set_HSV(TCOD_color_t *color, float hue, float saturation, float value)""" + + @staticmethod + def TCOD_color_set_hue(color: Any, hue: float, /) -> None: + """void TCOD_color_set_hue(TCOD_color_t *color, float hue)""" + + @staticmethod + def TCOD_color_set_saturation(color: Any, saturation: float, /) -> None: + """void TCOD_color_set_saturation(TCOD_color_t *color, float saturation)""" + + @staticmethod + def TCOD_color_set_value(color: Any, value: float, /) -> None: + """void TCOD_color_set_value(TCOD_color_t *color, float value)""" + + @staticmethod + def TCOD_color_shift_hue(color: Any, shift: float, /) -> None: + """void TCOD_color_shift_hue(TCOD_color_t *color, float shift)""" + + @staticmethod + def TCOD_color_subtract(c1: Any, c2: Any, /) -> Any: + """TCOD_color_t TCOD_color_subtract(TCOD_color_t c1, TCOD_color_t c2)""" + + @staticmethod + def TCOD_color_subtract_wrapper(c1: Any, c2: Any, /) -> Any: + """colornum_t TCOD_color_subtract_wrapper(colornum_t c1, colornum_t c2)""" + + @staticmethod + def TCOD_condition_broadcast(sem: Any, /) -> None: + """void TCOD_condition_broadcast(TCOD_cond_t sem)""" + + @staticmethod + def TCOD_condition_delete(sem: Any, /) -> None: + """void TCOD_condition_delete(TCOD_cond_t sem)""" + + @staticmethod + def TCOD_condition_new() -> Any: + """TCOD_cond_t TCOD_condition_new(void)""" + + @staticmethod + def TCOD_condition_signal(sem: Any, /) -> None: + """void TCOD_condition_signal(TCOD_cond_t sem)""" + + @staticmethod + def TCOD_condition_wait(sem: Any, mut: Any, /) -> None: + """void TCOD_condition_wait(TCOD_cond_t sem, TCOD_mutex_t mut)""" + + @staticmethod + def TCOD_console_blit( + src: Any, + xSrc: int, + ySrc: int, + wSrc: int, + hSrc: int, + dst: Any, + xDst: int, + yDst: int, + foreground_alpha: float, + background_alpha: float, + /, + ) -> None: + """void TCOD_console_blit(const TCOD_Console *src, int xSrc, int ySrc, int wSrc, int hSrc, TCOD_Console *dst, int xDst, int yDst, float foreground_alpha, float background_alpha)""" + + @staticmethod + def TCOD_console_blit_key_color( + src: Any, + xSrc: int, + ySrc: int, + wSrc: int, + hSrc: int, + dst: Any, + xDst: int, + yDst: int, + foreground_alpha: float, + background_alpha: float, + key_color: Any, + /, + ) -> None: + """void TCOD_console_blit_key_color(const TCOD_Console *src, int xSrc, int ySrc, int wSrc, int hSrc, TCOD_Console *dst, int xDst, int yDst, float foreground_alpha, float background_alpha, const TCOD_color_t *key_color)""" + + @staticmethod + def TCOD_console_check_for_keypress(flags: int, /) -> Any: + """TCOD_key_t TCOD_console_check_for_keypress(int flags)""" + + @staticmethod + def TCOD_console_check_for_keypress_wrapper(holder: Any, flags: int, /) -> bool: + """bool TCOD_console_check_for_keypress_wrapper(TCOD_key_t *holder, int flags)""" + + @staticmethod + def TCOD_console_clear(con: Any, /) -> None: + """void TCOD_console_clear(TCOD_Console *con)""" + + @staticmethod + def TCOD_console_credits() -> None: + """void TCOD_console_credits(void)""" + + @staticmethod + def TCOD_console_credits_render(x: int, y: int, alpha: bool, /) -> bool: + """bool TCOD_console_credits_render(int x, int y, bool alpha)""" + + @staticmethod + def TCOD_console_credits_render_ex(console: Any, x: int, y: int, alpha: bool, delta_time: float, /) -> bool: + """bool TCOD_console_credits_render_ex(TCOD_Console *console, int x, int y, bool alpha, float delta_time)""" + + @staticmethod + def TCOD_console_credits_reset() -> None: + """void TCOD_console_credits_reset(void)""" + + @staticmethod + def TCOD_console_delete(console: Any, /) -> None: + """void TCOD_console_delete(TCOD_Console *console)""" + + @staticmethod + def TCOD_console_disable_keyboard_repeat() -> None: + """void TCOD_console_disable_keyboard_repeat(void)""" + + @staticmethod + def TCOD_console_double_hline(con: Any, x: int, y: int, l: int, flag: Any, /) -> None: + """void TCOD_console_double_hline(TCOD_console_t con, int x, int y, int l, TCOD_bkgnd_flag_t flag)""" + + @staticmethod + def TCOD_console_double_vline(con: Any, x: int, y: int, l: int, flag: Any, /) -> None: + """void TCOD_console_double_vline(TCOD_console_t con, int x, int y, int l, TCOD_bkgnd_flag_t flag)""" + + @staticmethod + def TCOD_console_draw_frame_rgb( + con: Any, x: int, y: int, width: int, height: int, decoration: Any, fg: Any, bg: Any, flag: Any, clear: bool, / + ) -> Any: + """TCOD_Error TCOD_console_draw_frame_rgb(struct TCOD_Console *con, int x, int y, int width, int height, const int *decoration, const TCOD_ColorRGB *fg, const TCOD_ColorRGB *bg, TCOD_bkgnd_flag_t flag, bool clear)""" + + @staticmethod + def TCOD_console_draw_rect_rgb( + console: Any, x: int, y: int, width: int, height: int, ch: int, fg: Any, bg: Any, flag: Any, / + ) -> Any: + """TCOD_Error TCOD_console_draw_rect_rgb(TCOD_Console *console, int x, int y, int width, int height, int ch, const TCOD_color_t *fg, const TCOD_color_t *bg, TCOD_bkgnd_flag_t flag)""" + + @staticmethod + def TCOD_console_fill_background(con: Any, r: Any, g: Any, b: Any, /) -> None: + """void TCOD_console_fill_background(TCOD_console_t con, int *r, int *g, int *b)""" + + @staticmethod + def TCOD_console_fill_char(con: Any, arr: Any, /) -> None: + """void TCOD_console_fill_char(TCOD_console_t con, int *arr)""" + + @staticmethod + def TCOD_console_fill_foreground(con: Any, r: Any, g: Any, b: Any, /) -> None: + """void TCOD_console_fill_foreground(TCOD_console_t con, int *r, int *g, int *b)""" + + @staticmethod + def TCOD_console_flush() -> Any: + """TCOD_Error TCOD_console_flush(void)""" + + @staticmethod + def TCOD_console_flush_ex(console: Any, viewport: Any, /) -> Any: + """TCOD_Error TCOD_console_flush_ex(TCOD_Console *console, struct TCOD_ViewportOptions *viewport)""" + + @staticmethod + def TCOD_console_forward(s: Any, l: int, /) -> Any: + """unsigned char *TCOD_console_forward(unsigned char *s, int l)""" + + @staticmethod + def TCOD_console_from_file(filename: Any, /) -> Any: + """TCOD_console_t TCOD_console_from_file(const char *filename)""" + + @staticmethod + def TCOD_console_from_xp(filename: Any, /) -> Any: + """TCOD_console_t TCOD_console_from_xp(const char *filename)""" + + @staticmethod + def TCOD_console_get_alignment(con: Any, /) -> Any: + """TCOD_alignment_t TCOD_console_get_alignment(TCOD_Console *con)""" + + @staticmethod + def TCOD_console_get_background_flag(con: Any, /) -> Any: + """TCOD_bkgnd_flag_t TCOD_console_get_background_flag(TCOD_Console *con)""" + + @staticmethod + def TCOD_console_get_char(con: Any, x: int, y: int, /) -> int: + """int TCOD_console_get_char(const TCOD_Console *con, int x, int y)""" + + @staticmethod + def TCOD_console_get_char_background(con: Any, x: int, y: int, /) -> Any: + """TCOD_color_t TCOD_console_get_char_background(const TCOD_Console *con, int x, int y)""" + + @staticmethod + def TCOD_console_get_char_background_wrapper(con: Any, x: int, y: int, /) -> Any: + """colornum_t TCOD_console_get_char_background_wrapper(TCOD_console_t con, int x, int y)""" + + @staticmethod + def TCOD_console_get_char_foreground(con: Any, x: int, y: int, /) -> Any: + """TCOD_color_t TCOD_console_get_char_foreground(const TCOD_Console *con, int x, int y)""" + + @staticmethod + def TCOD_console_get_char_foreground_wrapper(con: Any, x: int, y: int, /) -> Any: + """colornum_t TCOD_console_get_char_foreground_wrapper(TCOD_console_t con, int x, int y)""" + + @staticmethod + def TCOD_console_get_default_background(con: Any, /) -> Any: + """TCOD_color_t TCOD_console_get_default_background(TCOD_Console *con)""" + + @staticmethod + def TCOD_console_get_default_background_wrapper(con: Any, /) -> Any: + """colornum_t TCOD_console_get_default_background_wrapper(TCOD_console_t con)""" + + @staticmethod + def TCOD_console_get_default_foreground(con: Any, /) -> Any: + """TCOD_color_t TCOD_console_get_default_foreground(TCOD_Console *con)""" + + @staticmethod + def TCOD_console_get_default_foreground_wrapper(con: Any, /) -> Any: + """colornum_t TCOD_console_get_default_foreground_wrapper(TCOD_console_t con)""" + + @staticmethod + def TCOD_console_get_fade() -> Any: + """uint8_t TCOD_console_get_fade(void)""" + + @staticmethod + def TCOD_console_get_fading_color() -> Any: + """TCOD_color_t TCOD_console_get_fading_color(void)""" + + @staticmethod + def TCOD_console_get_fading_color_wrapper() -> Any: + """colornum_t TCOD_console_get_fading_color_wrapper(void)""" + + @staticmethod + def TCOD_console_get_height(con: Any, /) -> int: + """int TCOD_console_get_height(const TCOD_Console *con)""" + + @staticmethod + def TCOD_console_get_height_rect(con: Any, x: int, y: int, w: int, h: int, fmt: Any, /, *__args: Any) -> int: + """int TCOD_console_get_height_rect(TCOD_Console *con, int x, int y, int w, int h, const char *fmt, ...)""" + + @staticmethod + def TCOD_console_get_height_rect_fmt(con: Any, x: int, y: int, w: int, h: int, fmt: Any, /, *__args: Any) -> int: + """int TCOD_console_get_height_rect_fmt(TCOD_Console *con, int x, int y, int w, int h, const char *fmt, ...)""" + + @staticmethod + def TCOD_console_get_height_rect_n( + console: Any, x: int, y: int, width: int, height: int, n: int, str: Any, / + ) -> int: + """int TCOD_console_get_height_rect_n(TCOD_Console *console, int x, int y, int width, int height, size_t n, const char *str)""" + + @staticmethod + def TCOD_console_get_height_rect_utf(con: Any, x: int, y: int, w: int, h: int, fmt: Any, /, *__args: Any) -> int: + """int TCOD_console_get_height_rect_utf(TCOD_Console *con, int x, int y, int w, int h, const wchar_t *fmt, ...)""" + + @staticmethod + def TCOD_console_get_height_rect_wn(width: int, n: int, str: Any, /) -> int: + """int TCOD_console_get_height_rect_wn(int width, size_t n, const char *str)""" + + @staticmethod + def TCOD_console_get_width(con: Any, /) -> int: + """int TCOD_console_get_width(const TCOD_Console *con)""" + + @staticmethod + def TCOD_console_has_mouse_focus() -> bool: + """bool TCOD_console_has_mouse_focus(void)""" + + @staticmethod + def TCOD_console_hline(con: Any, x: int, y: int, l: int, flag: Any, /) -> None: + """void TCOD_console_hline(TCOD_Console *con, int x, int y, int l, TCOD_bkgnd_flag_t flag)""" + + @staticmethod + def TCOD_console_init_root(w: int, h: int, title: Any, fullscreen: bool, renderer: Any, /) -> Any: + """TCOD_Error TCOD_console_init_root(int w, int h, const char *title, bool fullscreen, TCOD_renderer_t renderer)""" + + @staticmethod + def TCOD_console_init_root_(w: int, h: int, title: Any, fullscreen: bool, renderer: Any, vsync: bool, /) -> Any: + """TCOD_Error TCOD_console_init_root_(int w, int h, const char *title, bool fullscreen, TCOD_renderer_t renderer, bool vsync)""" + + @staticmethod + def TCOD_console_is_active() -> bool: + """bool TCOD_console_is_active(void)""" + + @staticmethod + def TCOD_console_is_fullscreen() -> bool: + """bool TCOD_console_is_fullscreen(void)""" + + @staticmethod + def TCOD_console_is_index_valid_(console: Any, x: int, y: int, /) -> bool: + """inline static bool TCOD_console_is_index_valid_(const TCOD_Console *console, int x, int y)""" + + @staticmethod + def TCOD_console_is_key_pressed(key: Any, /) -> bool: + """bool TCOD_console_is_key_pressed(TCOD_keycode_t key)""" + + @staticmethod + def TCOD_console_is_window_closed() -> bool: + """bool TCOD_console_is_window_closed(void)""" + + @staticmethod + def TCOD_console_list_from_xp(filename: Any, /) -> Any: + """TCOD_list_t TCOD_console_list_from_xp(const char *filename)""" + + @staticmethod + def TCOD_console_list_save_xp(console_list: Any, filename: Any, compress_level: int, /) -> bool: + """bool TCOD_console_list_save_xp(TCOD_list_t console_list, const char *filename, int compress_level)""" + + @staticmethod + def TCOD_console_load_apf(con: Any, filename: Any, /) -> bool: + """bool TCOD_console_load_apf(TCOD_console_t con, const char *filename)""" + + @staticmethod + def TCOD_console_load_asc(con: Any, filename: Any, /) -> bool: + """bool TCOD_console_load_asc(TCOD_console_t con, const char *filename)""" + + @staticmethod + def TCOD_console_load_xp(con: Any, filename: Any, /) -> bool: + """bool TCOD_console_load_xp(TCOD_Console *con, const char *filename)""" + + @staticmethod + def TCOD_console_map_ascii_code_to_font(asciiCode: int, fontCharX: int, fontCharY: int, /) -> None: + """void TCOD_console_map_ascii_code_to_font(int asciiCode, int fontCharX, int fontCharY)""" + + @staticmethod + def TCOD_console_map_ascii_codes_to_font(asciiCode: int, nbCodes: int, fontCharX: int, fontCharY: int, /) -> None: + """void TCOD_console_map_ascii_codes_to_font(int asciiCode, int nbCodes, int fontCharX, int fontCharY)""" + + @staticmethod + def TCOD_console_map_string_to_font(s: Any, fontCharX: int, fontCharY: int, /) -> None: + """void TCOD_console_map_string_to_font(const char *s, int fontCharX, int fontCharY)""" + + @staticmethod + def TCOD_console_map_string_to_font_utf(s: Any, fontCharX: int, fontCharY: int, /) -> None: + """void TCOD_console_map_string_to_font_utf(const wchar_t *s, int fontCharX, int fontCharY)""" + + @staticmethod + def TCOD_console_new(w: int, h: int, /) -> Any: + """TCOD_Console *TCOD_console_new(int w, int h)""" + + @staticmethod + def TCOD_console_print(con: Any, x: int, y: int, fmt: Any, /, *__args: Any) -> None: + """void TCOD_console_print(TCOD_Console *con, int x, int y, const char *fmt, ...)""" + + @staticmethod + def TCOD_console_print_double_frame( + con: Any, x: int, y: int, w: int, h: int, empty: bool, flag: Any, fmt: Any, /, *__args: Any + ) -> None: + """void TCOD_console_print_double_frame(TCOD_console_t con, int x, int y, int w, int h, bool empty, TCOD_bkgnd_flag_t flag, const char *fmt, ...)""" + + @staticmethod + def TCOD_console_print_ex(con: Any, x: int, y: int, flag: Any, alignment: Any, fmt: Any, /, *__args: Any) -> None: + """void TCOD_console_print_ex(TCOD_Console *con, int x, int y, TCOD_bkgnd_flag_t flag, TCOD_alignment_t alignment, const char *fmt, ...)""" + + @staticmethod + def TCOD_console_print_ex_utf( + con: Any, x: int, y: int, flag: Any, alignment: Any, fmt: Any, /, *__args: Any + ) -> None: + """void TCOD_console_print_ex_utf(TCOD_Console *con, int x, int y, TCOD_bkgnd_flag_t flag, TCOD_alignment_t alignment, const wchar_t *fmt, ...)""" + + @staticmethod + def TCOD_console_print_frame( + con: Any, x: int, y: int, w: int, h: int, empty: bool, flag: Any, fmt: Any, /, *__args: Any + ) -> None: + """void TCOD_console_print_frame(TCOD_console_t con, int x, int y, int w, int h, bool empty, TCOD_bkgnd_flag_t flag, const char *fmt, ...)""" + + @staticmethod + def TCOD_console_print_internal( + con: Any, x: int, y: int, w: int, h: int, flag: Any, align: Any, msg: Any, can_split: bool, count_only: bool, / + ) -> int: + """int TCOD_console_print_internal(TCOD_Console *con, int x, int y, int w, int h, TCOD_bkgnd_flag_t flag, TCOD_alignment_t align, char *msg, bool can_split, bool count_only)""" + + @staticmethod + def TCOD_console_print_internal_utf( + con: Any, + x: int, + y: int, + rw: int, + rh: int, + flag: Any, + align: Any, + msg: Any, + can_split: bool, + count_only: bool, + /, + ) -> int: + """int TCOD_console_print_internal_utf(TCOD_console_t con, int x, int y, int rw, int rh, TCOD_bkgnd_flag_t flag, TCOD_alignment_t align, wchar_t *msg, bool can_split, bool count_only)""" + + @staticmethod + def TCOD_console_print_rect(con: Any, x: int, y: int, w: int, h: int, fmt: Any, /, *__args: Any) -> int: + """int TCOD_console_print_rect(TCOD_Console *con, int x, int y, int w, int h, const char *fmt, ...)""" + + @staticmethod + def TCOD_console_print_rect_ex( + con: Any, x: int, y: int, w: int, h: int, flag: Any, alignment: Any, fmt: Any, /, *__args: Any + ) -> int: + """int TCOD_console_print_rect_ex(TCOD_Console *con, int x, int y, int w, int h, TCOD_bkgnd_flag_t flag, TCOD_alignment_t alignment, const char *fmt, ...)""" + + @staticmethod + def TCOD_console_print_rect_ex_utf( + con: Any, x: int, y: int, w: int, h: int, flag: Any, alignment: Any, fmt: Any, /, *__args: Any + ) -> int: + """int TCOD_console_print_rect_ex_utf(TCOD_Console *con, int x, int y, int w, int h, TCOD_bkgnd_flag_t flag, TCOD_alignment_t alignment, const wchar_t *fmt, ...)""" + + @staticmethod + def TCOD_console_print_rect_utf(con: Any, x: int, y: int, w: int, h: int, fmt: Any, /, *__args: Any) -> int: + """int TCOD_console_print_rect_utf(TCOD_Console *con, int x, int y, int w, int h, const wchar_t *fmt, ...)""" + + @staticmethod + def TCOD_console_print_return_string( + con: Any, + x: int, + y: int, + rw: int, + rh: int, + flag: Any, + align: Any, + msg: Any, + can_split: bool, + count_only: bool, + /, + ) -> Any: + """char *TCOD_console_print_return_string(TCOD_console_t con, int x, int y, int rw, int rh, TCOD_bkgnd_flag_t flag, TCOD_alignment_t align, char *msg, bool can_split, bool count_only)""" + + @staticmethod + def TCOD_console_print_utf(con: Any, x: int, y: int, fmt: Any, /, *__args: Any) -> None: + """void TCOD_console_print_utf(TCOD_Console *con, int x, int y, const wchar_t *fmt, ...)""" + + @staticmethod + def TCOD_console_printf(con: Any, x: int, y: int, fmt: Any, /, *__args: Any) -> Any: + """TCOD_Error TCOD_console_printf(TCOD_Console *con, int x, int y, const char *fmt, ...)""" + + @staticmethod + def TCOD_console_printf_ex(con: Any, x: int, y: int, flag: Any, alignment: Any, fmt: Any, /, *__args: Any) -> Any: + """TCOD_Error TCOD_console_printf_ex(TCOD_Console *con, int x, int y, TCOD_bkgnd_flag_t flag, TCOD_alignment_t alignment, const char *fmt, ...)""" + + @staticmethod + def TCOD_console_printf_frame( + con: Any, x: int, y: int, w: int, h: int, empty: int, flag: Any, fmt: Any, /, *__args: Any + ) -> Any: + """TCOD_Error TCOD_console_printf_frame(TCOD_Console *con, int x, int y, int w, int h, int empty, TCOD_bkgnd_flag_t flag, const char *fmt, ...)""" + + @staticmethod + def TCOD_console_printf_rect(con: Any, x: int, y: int, w: int, h: int, fmt: Any, /, *__args: Any) -> int: + """int TCOD_console_printf_rect(TCOD_Console *con, int x, int y, int w, int h, const char *fmt, ...)""" + + @staticmethod + def TCOD_console_printf_rect_ex( + con: Any, x: int, y: int, w: int, h: int, flag: Any, alignment: Any, fmt: Any, /, *__args: Any + ) -> int: + """int TCOD_console_printf_rect_ex(TCOD_Console *con, int x, int y, int w, int h, TCOD_bkgnd_flag_t flag, TCOD_alignment_t alignment, const char *fmt, ...)""" + + @staticmethod + def TCOD_console_printn( + console: Any, x: int, y: int, n: int, str: Any, fg: Any, bg: Any, flag: Any, alignment: Any, / + ) -> Any: + """TCOD_Error TCOD_console_printn(TCOD_Console *console, int x, int y, size_t n, const char *str, const TCOD_ColorRGB *fg, const TCOD_ColorRGB *bg, TCOD_bkgnd_flag_t flag, TCOD_alignment_t alignment)""" + + @staticmethod + def TCOD_console_printn_frame( + console: Any, + x: int, + y: int, + width: int, + height: int, + n: int, + title: Any, + fg: Any, + bg: Any, + flag: Any, + clear: bool, + /, + ) -> Any: + """TCOD_Error TCOD_console_printn_frame(TCOD_Console *console, int x, int y, int width, int height, size_t n, const char *title, const TCOD_ColorRGB *fg, const TCOD_ColorRGB *bg, TCOD_bkgnd_flag_t flag, bool clear)""" + + @staticmethod + def TCOD_console_printn_rect( + console: Any, + x: int, + y: int, + width: int, + height: int, + n: int, + str: Any, + fg: Any, + bg: Any, + flag: Any, + alignment: Any, + /, + ) -> int: + """int TCOD_console_printn_rect(TCOD_Console *console, int x, int y, int width, int height, size_t n, const char *str, const TCOD_ColorRGB *fg, const TCOD_ColorRGB *bg, TCOD_bkgnd_flag_t flag, TCOD_alignment_t alignment)""" + + @staticmethod + def TCOD_console_put_char(con: Any, x: int, y: int, c: int, flag: Any, /) -> None: + """void TCOD_console_put_char(TCOD_Console *con, int x, int y, int c, TCOD_bkgnd_flag_t flag)""" + + @staticmethod + def TCOD_console_put_char_ex(con: Any, x: int, y: int, c: int, fore: Any, back: Any, /) -> None: + """void TCOD_console_put_char_ex(TCOD_Console *con, int x, int y, int c, TCOD_color_t fore, TCOD_color_t back)""" + + @staticmethod + def TCOD_console_put_char_ex_wrapper(con: Any, x: int, y: int, c: int, fore: Any, back: Any, /) -> None: + """void TCOD_console_put_char_ex_wrapper(TCOD_console_t con, int x, int y, int c, colornum_t fore, colornum_t back)""" + + @staticmethod + def TCOD_console_put_rgb(console: Any, x: int, y: int, ch: int, fg: Any, bg: Any, flag: Any, /) -> None: + """void TCOD_console_put_rgb(TCOD_Console *console, int x, int y, int ch, const TCOD_color_t *fg, const TCOD_color_t *bg, TCOD_bkgnd_flag_t flag)""" + + @staticmethod + def TCOD_console_rect(con: Any, x: int, y: int, rw: int, rh: int, clear: bool, flag: Any, /) -> None: + """void TCOD_console_rect(TCOD_Console *con, int x, int y, int rw, int rh, bool clear, TCOD_bkgnd_flag_t flag)""" + + @staticmethod + def TCOD_console_resize_(console: Any, width: int, height: int, /) -> None: + """void TCOD_console_resize_(TCOD_Console *console, int width, int height)""" + + @staticmethod + def TCOD_console_save_apf(con: Any, filename: Any, /) -> bool: + """bool TCOD_console_save_apf(TCOD_console_t con, const char *filename)""" + + @staticmethod + def TCOD_console_save_asc(con: Any, filename: Any, /) -> bool: + """bool TCOD_console_save_asc(TCOD_console_t con, const char *filename)""" + + @staticmethod + def TCOD_console_save_xp(con: Any, filename: Any, compress_level: int, /) -> bool: + """bool TCOD_console_save_xp(const TCOD_Console *con, const char *filename, int compress_level)""" + + @staticmethod + def TCOD_console_set_alignment(con: Any, alignment: Any, /) -> None: + """void TCOD_console_set_alignment(TCOD_Console *con, TCOD_alignment_t alignment)""" + + @staticmethod + def TCOD_console_set_background_flag(con: Any, flag: Any, /) -> None: + """void TCOD_console_set_background_flag(TCOD_Console *con, TCOD_bkgnd_flag_t flag)""" + + @staticmethod + def TCOD_console_set_char(con: Any, x: int, y: int, c: int, /) -> None: + """void TCOD_console_set_char(TCOD_Console *con, int x, int y, int c)""" + + @staticmethod + def TCOD_console_set_char_background(con: Any, x: int, y: int, col: Any, flag: Any, /) -> None: + """void TCOD_console_set_char_background(TCOD_Console *con, int x, int y, TCOD_color_t col, TCOD_bkgnd_flag_t flag)""" + + @staticmethod + def TCOD_console_set_char_background_wrapper(con: Any, x: int, y: int, col: Any, flag: Any, /) -> None: + """void TCOD_console_set_char_background_wrapper(TCOD_console_t con, int x, int y, colornum_t col, TCOD_bkgnd_flag_t flag)""" + + @staticmethod + def TCOD_console_set_char_foreground(con: Any, x: int, y: int, col: Any, /) -> None: + """void TCOD_console_set_char_foreground(TCOD_Console *con, int x, int y, TCOD_color_t col)""" + + @staticmethod + def TCOD_console_set_char_foreground_wrapper(con: Any, x: int, y: int, col: Any, /) -> None: + """void TCOD_console_set_char_foreground_wrapper(TCOD_console_t con, int x, int y, colornum_t col)""" + + @staticmethod + def TCOD_console_set_color_control(con: Any, fore: Any, back: Any, /) -> None: + """void TCOD_console_set_color_control(TCOD_colctrl_t con, TCOD_color_t fore, TCOD_color_t back)""" + + @staticmethod + def TCOD_console_set_color_control_wrapper(con: Any, fore: Any, back: Any, /) -> None: + """void TCOD_console_set_color_control_wrapper(TCOD_colctrl_t con, colornum_t fore, colornum_t back)""" + + @staticmethod + def TCOD_console_set_custom_font(fontFile: Any, flags: int, nb_char_horiz: int, nb_char_vertic: int, /) -> Any: + """TCOD_Error TCOD_console_set_custom_font(const char *fontFile, int flags, int nb_char_horiz, int nb_char_vertic)""" + + @staticmethod + def TCOD_console_set_default_background(con: Any, col: Any, /) -> None: + """void TCOD_console_set_default_background(TCOD_Console *con, TCOD_color_t col)""" + + @staticmethod + def TCOD_console_set_default_background_wrapper(con: Any, col: Any, /) -> None: + """void TCOD_console_set_default_background_wrapper(TCOD_console_t con, colornum_t col)""" + + @staticmethod + def TCOD_console_set_default_foreground(con: Any, col: Any, /) -> None: + """void TCOD_console_set_default_foreground(TCOD_Console *con, TCOD_color_t col)""" + + @staticmethod + def TCOD_console_set_default_foreground_wrapper(con: Any, col: Any, /) -> None: + """void TCOD_console_set_default_foreground_wrapper(TCOD_console_t con, colornum_t col)""" + + @staticmethod + def TCOD_console_set_dirty(x: int, y: int, w: int, h: int, /) -> None: + """void TCOD_console_set_dirty(int x, int y, int w, int h)""" + + @staticmethod + def TCOD_console_set_fade(val: Any, fade_color: Any, /) -> None: + """void TCOD_console_set_fade(uint8_t val, TCOD_color_t fade_color)""" + + @staticmethod + def TCOD_console_set_fade_wrapper(val: Any, fade: Any, /) -> None: + """void TCOD_console_set_fade_wrapper(uint8_t val, colornum_t fade)""" + + @staticmethod + def TCOD_console_set_fullscreen(fullscreen: bool, /) -> None: + """void TCOD_console_set_fullscreen(bool fullscreen)""" + + @staticmethod + def TCOD_console_set_key_color(con: Any, col: Any, /) -> None: + """void TCOD_console_set_key_color(TCOD_Console *con, TCOD_color_t col)""" + + @staticmethod + def TCOD_console_set_key_color_wrapper(con: Any, c: Any, /) -> None: + """void TCOD_console_set_key_color_wrapper(TCOD_console_t con, colornum_t c)""" + + @staticmethod + def TCOD_console_set_keyboard_repeat(initial_delay: int, interval: int, /) -> None: + """void TCOD_console_set_keyboard_repeat(int initial_delay, int interval)""" + + @staticmethod + def TCOD_console_set_window_title(title: Any, /) -> None: + """void TCOD_console_set_window_title(const char *title)""" + + @staticmethod + def TCOD_console_stringLength(s: Any, /) -> int: + """int TCOD_console_stringLength(const unsigned char *s)""" + + @staticmethod + def TCOD_console_validate_(console: Any, /) -> Any: + """inline static TCOD_Console *TCOD_console_validate_(const TCOD_Console *console)""" + + @staticmethod + def TCOD_console_vline(con: Any, x: int, y: int, l: int, flag: Any, /) -> None: + """void TCOD_console_vline(TCOD_Console *con, int x, int y, int l, TCOD_bkgnd_flag_t flag)""" + + @staticmethod + def TCOD_console_wait_for_keypress(flush: bool, /) -> Any: + """TCOD_key_t TCOD_console_wait_for_keypress(bool flush)""" + + @staticmethod + def TCOD_console_wait_for_keypress_wrapper(holder: Any, flush: bool, /) -> None: + """void TCOD_console_wait_for_keypress_wrapper(TCOD_key_t *holder, bool flush)""" + + @staticmethod + def TCOD_context_change_tileset(self: Any, tileset: Any, /) -> Any: + """TCOD_Error TCOD_context_change_tileset(struct TCOD_Context *self, TCOD_Tileset *tileset)""" + + @staticmethod + def TCOD_context_convert_event_coordinates(context: Any, event: Any, /) -> Any: + """TCOD_Error TCOD_context_convert_event_coordinates(struct TCOD_Context *context, union SDL_Event *event)""" + + @staticmethod + def TCOD_context_delete(renderer: Any, /) -> None: + """void TCOD_context_delete(struct TCOD_Context *renderer)""" + + @staticmethod + def TCOD_context_get_renderer_type(context: Any, /) -> int: + """int TCOD_context_get_renderer_type(struct TCOD_Context *context)""" + + @staticmethod + def TCOD_context_get_sdl_renderer(context: Any, /) -> Any: + """struct SDL_Renderer *TCOD_context_get_sdl_renderer(struct TCOD_Context *context)""" + + @staticmethod + def TCOD_context_get_sdl_window(context: Any, /) -> Any: + """struct SDL_Window *TCOD_context_get_sdl_window(struct TCOD_Context *context)""" + + @staticmethod + def TCOD_context_new(params: Any, out: Any, /) -> Any: + """TCOD_Error TCOD_context_new(const TCOD_ContextParams *params, TCOD_Context **out)""" + + @staticmethod + def TCOD_context_new_() -> Any: + """struct TCOD_Context *TCOD_context_new_(void)""" + + @staticmethod + def TCOD_context_present(context: Any, console: Any, viewport: Any, /) -> Any: + """TCOD_Error TCOD_context_present(struct TCOD_Context *context, const struct TCOD_Console *console, const struct TCOD_ViewportOptions *viewport)""" + + @staticmethod + def TCOD_context_recommended_console_size(context: Any, magnification: float, columns: Any, rows: Any, /) -> Any: + """TCOD_Error TCOD_context_recommended_console_size(struct TCOD_Context *context, float magnification, int *columns, int *rows)""" + + @staticmethod + def TCOD_context_save_screenshot(context: Any, filename: Any, /) -> Any: + """TCOD_Error TCOD_context_save_screenshot(struct TCOD_Context *context, const char *filename)""" + + @staticmethod + def TCOD_context_screen_capture(context: Any, out_pixels: Any, width: Any, height: Any, /) -> Any: + """TCOD_Error TCOD_context_screen_capture(struct TCOD_Context *context, TCOD_ColorRGBA *out_pixels, int *width, int *height)""" + + @staticmethod + def TCOD_context_screen_capture_alloc(context: Any, width: Any, height: Any, /) -> Any: + """TCOD_ColorRGBA *TCOD_context_screen_capture_alloc(struct TCOD_Context *context, int *width, int *height)""" + + @staticmethod + def TCOD_context_screen_pixel_to_tile_d(context: Any, x: Any, y: Any, /) -> Any: + """TCOD_Error TCOD_context_screen_pixel_to_tile_d(struct TCOD_Context *context, double *x, double *y)""" + + @staticmethod + def TCOD_context_screen_pixel_to_tile_i(context: Any, x: Any, y: Any, /) -> Any: + """TCOD_Error TCOD_context_screen_pixel_to_tile_i(struct TCOD_Context *context, int *x, int *y)""" + + @staticmethod + def TCOD_context_set_mouse_transform(context: Any, transform: Any, /) -> Any: + """TCOD_Error TCOD_context_set_mouse_transform(struct TCOD_Context *context, const TCOD_MouseTransform *transform)""" + + @staticmethod + def TCOD_dijkstra_compute(dijkstra: Any, root_x: int, root_y: int, /) -> None: + """void TCOD_dijkstra_compute(TCOD_Dijkstra *dijkstra, int root_x, int root_y)""" + + @staticmethod + def TCOD_dijkstra_delete(dijkstra: Any, /) -> None: + """void TCOD_dijkstra_delete(TCOD_Dijkstra *dijkstra)""" + + @staticmethod + def TCOD_dijkstra_get(path: Any, index: int, x: Any, y: Any, /) -> None: + """void TCOD_dijkstra_get(TCOD_Dijkstra *path, int index, int *x, int *y)""" + + @staticmethod + def TCOD_dijkstra_get_distance(dijkstra: Any, x: int, y: int, /) -> float: + """float TCOD_dijkstra_get_distance(TCOD_Dijkstra *dijkstra, int x, int y)""" + + @staticmethod + def TCOD_dijkstra_is_empty(path: Any, /) -> bool: + """bool TCOD_dijkstra_is_empty(TCOD_Dijkstra *path)""" + + @staticmethod + def TCOD_dijkstra_new(map: Any, diagonalCost: float, /) -> Any: + """TCOD_Dijkstra *TCOD_dijkstra_new(TCOD_Map *map, float diagonalCost)""" + + @staticmethod + def TCOD_dijkstra_new_using_function( + map_width: int, map_height: int, func: Any, user_data: Any, diagonalCost: float, / + ) -> Any: + """TCOD_Dijkstra *TCOD_dijkstra_new_using_function(int map_width, int map_height, TCOD_path_func_t func, void *user_data, float diagonalCost)""" + + @staticmethod + def TCOD_dijkstra_path_set(dijkstra: Any, x: int, y: int, /) -> bool: + """bool TCOD_dijkstra_path_set(TCOD_Dijkstra *dijkstra, int x, int y)""" + + @staticmethod + def TCOD_dijkstra_path_walk(dijkstra: Any, x: Any, y: Any, /) -> bool: + """bool TCOD_dijkstra_path_walk(TCOD_Dijkstra *dijkstra, int *x, int *y)""" + + @staticmethod + def TCOD_dijkstra_reverse(path: Any, /) -> None: + """void TCOD_dijkstra_reverse(TCOD_Dijkstra *path)""" + + @staticmethod + def TCOD_dijkstra_size(path: Any, /) -> int: + """int TCOD_dijkstra_size(TCOD_Dijkstra *path)""" + + @staticmethod + def TCOD_frontier_clear(frontier: Any, /) -> Any: + """TCOD_Error TCOD_frontier_clear(struct TCOD_Frontier *frontier)""" + + @staticmethod + def TCOD_frontier_delete(frontier: Any, /) -> None: + """void TCOD_frontier_delete(struct TCOD_Frontier *frontier)""" + + @staticmethod + def TCOD_frontier_new(ndim: int, /) -> Any: + """struct TCOD_Frontier *TCOD_frontier_new(int ndim)""" + + @staticmethod + def TCOD_frontier_pop(frontier: Any, /) -> Any: + """TCOD_Error TCOD_frontier_pop(struct TCOD_Frontier *frontier)""" + + @staticmethod + def TCOD_frontier_push(frontier: Any, index: Any, dist: int, heuristic: int, /) -> Any: + """TCOD_Error TCOD_frontier_push(struct TCOD_Frontier *frontier, const int *index, int dist, int heuristic)""" + + @staticmethod + def TCOD_frontier_size(frontier: Any, /) -> int: + """int TCOD_frontier_size(const struct TCOD_Frontier *frontier)""" + + @staticmethod + def TCOD_get_default_tileset() -> Any: + """TCOD_Tileset *TCOD_get_default_tileset(void)""" + + @staticmethod + def TCOD_get_error() -> Any: + """const char *TCOD_get_error(void)""" + + @staticmethod + def TCOD_get_function_address(library: Any, function_name: Any, /) -> Any: + """void *TCOD_get_function_address(TCOD_library_t library, const char *function_name)""" + + @staticmethod + def TCOD_heap_clear(heap: Any, /) -> None: + """void TCOD_heap_clear(struct TCOD_Heap *heap)""" + + @staticmethod + def TCOD_heap_init(heap: Any, data_size: int, /) -> int: + """int TCOD_heap_init(struct TCOD_Heap *heap, size_t data_size)""" + + @staticmethod + def TCOD_heap_uninit(heap: Any, /) -> None: + """void TCOD_heap_uninit(struct TCOD_Heap *heap)""" + + @staticmethod + def TCOD_heightmap_add(hm: Any, value: float, /) -> None: + """void TCOD_heightmap_add(TCOD_heightmap_t *hm, float value)""" + + @staticmethod + def TCOD_heightmap_add_fbm( + hm: Any, + noise: Any, + mul_x: float, + mul_y: float, + add_x: float, + add_y: float, + octaves: float, + delta: float, + scale: float, + /, + ) -> None: + """void TCOD_heightmap_add_fbm(TCOD_heightmap_t *hm, TCOD_noise_t noise, float mul_x, float mul_y, float add_x, float add_y, float octaves, float delta, float scale)""" + + @staticmethod + def TCOD_heightmap_add_hill(hm: Any, hx: float, hy: float, h_radius: float, h_height: float, /) -> None: + """void TCOD_heightmap_add_hill(TCOD_heightmap_t *hm, float hx, float hy, float h_radius, float h_height)""" + + @staticmethod + def TCOD_heightmap_add_hm(hm1: Any, hm2: Any, out: Any, /) -> None: + """void TCOD_heightmap_add_hm(const TCOD_heightmap_t *hm1, const TCOD_heightmap_t *hm2, TCOD_heightmap_t *out)""" + + @staticmethod + def TCOD_heightmap_add_voronoi(hm: Any, nbPoints: int, nbCoef: int, coef: Any, rnd: Any, /) -> None: + """void TCOD_heightmap_add_voronoi(TCOD_heightmap_t *hm, int nbPoints, int nbCoef, const float *coef, TCOD_Random *rnd)""" + + @staticmethod + def TCOD_heightmap_clamp(hm: Any, min: float, max: float, /) -> None: + """void TCOD_heightmap_clamp(TCOD_heightmap_t *hm, float min, float max)""" + + @staticmethod + def TCOD_heightmap_clear(hm: Any, /) -> None: + """void TCOD_heightmap_clear(TCOD_heightmap_t *hm)""" + + @staticmethod + def TCOD_heightmap_copy(hm_source: Any, hm_dest: Any, /) -> None: + """void TCOD_heightmap_copy(const TCOD_heightmap_t *hm_source, TCOD_heightmap_t *hm_dest)""" + + @staticmethod + def TCOD_heightmap_count_cells(hm: Any, min: float, max: float, /) -> int: + """int TCOD_heightmap_count_cells(const TCOD_heightmap_t *hm, float min, float max)""" + + @staticmethod + def TCOD_heightmap_delete(hm: Any, /) -> None: + """void TCOD_heightmap_delete(TCOD_heightmap_t *hm)""" + + @staticmethod + def TCOD_heightmap_dig_bezier( + hm: Any, px: Any, py: Any, startRadius: float, startDepth: float, endRadius: float, endDepth: float, / + ) -> None: + """void TCOD_heightmap_dig_bezier(TCOD_heightmap_t *hm, int px[4], int py[4], float startRadius, float startDepth, float endRadius, float endDepth)""" + + @staticmethod + def TCOD_heightmap_dig_hill(hm: Any, hx: float, hy: float, h_radius: float, h_height: float, /) -> None: + """void TCOD_heightmap_dig_hill(TCOD_heightmap_t *hm, float hx, float hy, float h_radius, float h_height)""" + + @staticmethod + def TCOD_heightmap_get_interpolated_value(hm: Any, x: float, y: float, /) -> float: + """float TCOD_heightmap_get_interpolated_value(const TCOD_heightmap_t *hm, float x, float y)""" + + @staticmethod + def TCOD_heightmap_get_minmax(hm: Any, min: Any, max: Any, /) -> None: + """void TCOD_heightmap_get_minmax(const TCOD_heightmap_t *hm, float *min, float *max)""" + + @staticmethod + def TCOD_heightmap_get_normal(hm: Any, x: float, y: float, n: Any, waterLevel: float, /) -> None: + """void TCOD_heightmap_get_normal(const TCOD_heightmap_t *hm, float x, float y, float n[3], float waterLevel)""" + + @staticmethod + def TCOD_heightmap_get_slope(hm: Any, x: int, y: int, /) -> float: + """float TCOD_heightmap_get_slope(const TCOD_heightmap_t *hm, int x, int y)""" + + @staticmethod + def TCOD_heightmap_get_value(hm: Any, x: int, y: int, /) -> float: + """float TCOD_heightmap_get_value(const TCOD_heightmap_t *hm, int x, int y)""" + + @staticmethod + def TCOD_heightmap_has_land_on_border(hm: Any, waterLevel: float, /) -> bool: + """bool TCOD_heightmap_has_land_on_border(const TCOD_heightmap_t *hm, float waterLevel)""" + + @staticmethod + def TCOD_heightmap_islandify(hm: Any, seaLevel: float, rnd: Any, /) -> None: + """void TCOD_heightmap_islandify(TCOD_heightmap_t *hm, float seaLevel, TCOD_Random *rnd)""" + + @staticmethod + def TCOD_heightmap_kernel_transform( + hm: Any, kernel_size: int, dx: Any, dy: Any, weight: Any, minLevel: float, maxLevel: float, / + ) -> None: + """void TCOD_heightmap_kernel_transform(TCOD_heightmap_t *hm, int kernel_size, const int *dx, const int *dy, const float *weight, float minLevel, float maxLevel)""" + + @staticmethod + def TCOD_heightmap_lerp_hm(hm1: Any, hm2: Any, out: Any, coef: float, /) -> None: + """void TCOD_heightmap_lerp_hm(const TCOD_heightmap_t *hm1, const TCOD_heightmap_t *hm2, TCOD_heightmap_t *out, float coef)""" + + @staticmethod + def TCOD_heightmap_mid_point_displacement(hm: Any, rnd: Any, roughness: float, /) -> None: + """void TCOD_heightmap_mid_point_displacement(TCOD_heightmap_t *hm, TCOD_Random *rnd, float roughness)""" + + @staticmethod + def TCOD_heightmap_multiply_hm(hm1: Any, hm2: Any, out: Any, /) -> None: + """void TCOD_heightmap_multiply_hm(const TCOD_heightmap_t *hm1, const TCOD_heightmap_t *hm2, TCOD_heightmap_t *out)""" + + @staticmethod + def TCOD_heightmap_new(w: int, h: int, /) -> Any: + """TCOD_heightmap_t *TCOD_heightmap_new(int w, int h)""" + + @staticmethod + def TCOD_heightmap_normalize(hm: Any, min: float, max: float, /) -> None: + """void TCOD_heightmap_normalize(TCOD_heightmap_t *hm, float min, float max)""" + + @staticmethod + def TCOD_heightmap_rain_erosion( + hm: Any, nbDrops: int, erosionCoef: float, sedimentationCoef: float, rnd: Any, / + ) -> None: + """void TCOD_heightmap_rain_erosion(TCOD_heightmap_t *hm, int nbDrops, float erosionCoef, float sedimentationCoef, TCOD_Random *rnd)""" + + @staticmethod + def TCOD_heightmap_scale(hm: Any, value: float, /) -> None: + """void TCOD_heightmap_scale(TCOD_heightmap_t *hm, float value)""" + + @staticmethod + def TCOD_heightmap_scale_fbm( + hm: Any, + noise: Any, + mul_x: float, + mul_y: float, + add_x: float, + add_y: float, + octaves: float, + delta: float, + scale: float, + /, + ) -> None: + """void TCOD_heightmap_scale_fbm(TCOD_heightmap_t *hm, TCOD_noise_t noise, float mul_x, float mul_y, float add_x, float add_y, float octaves, float delta, float scale)""" + + @staticmethod + def TCOD_heightmap_set_value(hm: Any, x: int, y: int, value: float, /) -> None: + """void TCOD_heightmap_set_value(TCOD_heightmap_t *hm, int x, int y, float value)""" + + @staticmethod + def TCOD_image_blit( + image: Any, console: Any, x: float, y: float, bkgnd_flag: Any, scale_x: float, scale_y: float, angle: float, / + ) -> None: + """void TCOD_image_blit(TCOD_Image *image, TCOD_console_t console, float x, float y, TCOD_bkgnd_flag_t bkgnd_flag, float scale_x, float scale_y, float angle)""" + + @staticmethod + def TCOD_image_blit_2x(image: Any, dest: Any, dx: int, dy: int, sx: int, sy: int, w: int, h: int, /) -> None: + """void TCOD_image_blit_2x(const TCOD_Image *image, TCOD_Console *dest, int dx, int dy, int sx, int sy, int w, int h)""" + + @staticmethod + def TCOD_image_blit_rect(image: Any, console: Any, x: int, y: int, w: int, h: int, bkgnd_flag: Any, /) -> None: + """void TCOD_image_blit_rect(TCOD_Image *image, TCOD_console_t console, int x, int y, int w, int h, TCOD_bkgnd_flag_t bkgnd_flag)""" + + @staticmethod + def TCOD_image_clear(image: Any, color: Any, /) -> None: + """void TCOD_image_clear(TCOD_Image *image, TCOD_color_t color)""" + + @staticmethod + def TCOD_image_clear_wrapper(image: Any, color: Any, /) -> None: + """void TCOD_image_clear_wrapper(TCOD_image_t image, colornum_t color)""" + + @staticmethod + def TCOD_image_delete(image: Any, /) -> None: + """void TCOD_image_delete(TCOD_Image *image)""" + + @staticmethod + def TCOD_image_from_console(console: Any, /) -> Any: + """TCOD_Image *TCOD_image_from_console(const TCOD_Console *console)""" + + @staticmethod + def TCOD_image_get_alpha(image: Any, x: int, y: int, /) -> int: + """int TCOD_image_get_alpha(const TCOD_Image *image, int x, int y)""" + + @staticmethod + def TCOD_image_get_mipmap_pixel(image: Any, x0: float, y0: float, x1: float, y1: float, /) -> Any: + """TCOD_color_t TCOD_image_get_mipmap_pixel(TCOD_Image *image, float x0, float y0, float x1, float y1)""" + + @staticmethod + def TCOD_image_get_mipmap_pixel_wrapper(image: Any, x0: float, y0: float, x1: float, y1: float, /) -> Any: + """colornum_t TCOD_image_get_mipmap_pixel_wrapper(TCOD_image_t image, float x0, float y0, float x1, float y1)""" + + @staticmethod + def TCOD_image_get_pixel(image: Any, x: int, y: int, /) -> Any: + """TCOD_color_t TCOD_image_get_pixel(const TCOD_Image *image, int x, int y)""" + + @staticmethod + def TCOD_image_get_pixel_wrapper(image: Any, x: int, y: int, /) -> Any: + """colornum_t TCOD_image_get_pixel_wrapper(TCOD_image_t image, int x, int y)""" + + @staticmethod + def TCOD_image_get_size(image: Any, w: Any, h: Any, /) -> None: + """void TCOD_image_get_size(const TCOD_Image *image, int *w, int *h)""" + + @staticmethod + def TCOD_image_hflip(image: Any, /) -> None: + """void TCOD_image_hflip(TCOD_Image *image)""" + + @staticmethod + def TCOD_image_invert(image: Any, /) -> None: + """void TCOD_image_invert(TCOD_Image *image)""" + + @staticmethod + def TCOD_image_is_pixel_transparent(image: Any, x: int, y: int, /) -> bool: + """bool TCOD_image_is_pixel_transparent(const TCOD_Image *image, int x, int y)""" + + @staticmethod + def TCOD_image_load(filename: Any, /) -> Any: + """TCOD_Image *TCOD_image_load(const char *filename)""" + + @staticmethod + def TCOD_image_new(width: int, height: int, /) -> Any: + """TCOD_Image *TCOD_image_new(int width, int height)""" + + @staticmethod + def TCOD_image_put_pixel(image: Any, x: int, y: int, col: Any, /) -> None: + """void TCOD_image_put_pixel(TCOD_Image *image, int x, int y, TCOD_color_t col)""" + + @staticmethod + def TCOD_image_put_pixel_wrapper(image: Any, x: int, y: int, col: Any, /) -> None: + """void TCOD_image_put_pixel_wrapper(TCOD_image_t image, int x, int y, colornum_t col)""" + + @staticmethod + def TCOD_image_refresh_console(image: Any, console: Any, /) -> None: + """void TCOD_image_refresh_console(TCOD_Image *image, const TCOD_Console *console)""" + + @staticmethod + def TCOD_image_rotate90(image: Any, numRotations: int, /) -> None: + """void TCOD_image_rotate90(TCOD_Image *image, int numRotations)""" + + @staticmethod + def TCOD_image_save(image: Any, filename: Any, /) -> Any: + """TCOD_Error TCOD_image_save(const TCOD_Image *image, const char *filename)""" + + @staticmethod + def TCOD_image_scale(image: Any, new_w: int, new_h: int, /) -> None: + """void TCOD_image_scale(TCOD_Image *image, int new_w, int new_h)""" + + @staticmethod + def TCOD_image_set_key_color(image: Any, key_color: Any, /) -> None: + """void TCOD_image_set_key_color(TCOD_Image *image, TCOD_color_t key_color)""" + + @staticmethod + def TCOD_image_set_key_color_wrapper(image: Any, key_color: Any, /) -> None: + """void TCOD_image_set_key_color_wrapper(TCOD_image_t image, colornum_t key_color)""" + + @staticmethod + def TCOD_image_vflip(image: Any, /) -> None: + """void TCOD_image_vflip(TCOD_Image *image)""" + + @staticmethod + def TCOD_lex_delete(lex: Any, /) -> None: + """void TCOD_lex_delete(TCOD_lex_t *lex)""" + + @staticmethod + def TCOD_lex_expect_token_type(lex: Any, token_type: int, /) -> bool: + """bool TCOD_lex_expect_token_type(TCOD_lex_t *lex, int token_type)""" + + @staticmethod + def TCOD_lex_expect_token_value(lex: Any, token_type: int, token_value: Any, /) -> bool: + """bool TCOD_lex_expect_token_value(TCOD_lex_t *lex, int token_type, const char *token_value)""" + + @staticmethod + def TCOD_lex_get_last_javadoc(lex: Any, /) -> Any: + """char *TCOD_lex_get_last_javadoc(TCOD_lex_t *lex)""" + + @staticmethod + def TCOD_lex_get_token_name(token_type: int, /) -> Any: + """const char *TCOD_lex_get_token_name(int token_type)""" + + @staticmethod + def TCOD_lex_hextoint(c: Any, /) -> int: + """int TCOD_lex_hextoint(char c)""" + + @staticmethod + def TCOD_lex_new( + symbols: Any, + keywords: Any, + simpleComment: Any, + commentStart: Any, + commentStop: Any, + javadocCommentStart: Any, + stringDelim: Any, + flags: int, + /, + ) -> Any: + """TCOD_lex_t *TCOD_lex_new(const char * const *symbols, const char * const *keywords, const char *simpleComment, const char *commentStart, const char *commentStop, const char *javadocCommentStart, const char *stringDelim, int flags)""" + + @staticmethod + def TCOD_lex_new_intern() -> Any: + """TCOD_lex_t *TCOD_lex_new_intern(void)""" + + @staticmethod + def TCOD_lex_parse(lex: Any, /) -> int: + """int TCOD_lex_parse(TCOD_lex_t *lex)""" + + @staticmethod + def TCOD_lex_parse_until_token_type(lex: Any, token_type: int, /) -> int: + """int TCOD_lex_parse_until_token_type(TCOD_lex_t *lex, int token_type)""" + + @staticmethod + def TCOD_lex_parse_until_token_value(lex: Any, token_value: Any, /) -> int: + """int TCOD_lex_parse_until_token_value(TCOD_lex_t *lex, const char *token_value)""" + + @staticmethod + def TCOD_lex_restore(lex: Any, savepoint: Any, /) -> None: + """void TCOD_lex_restore(TCOD_lex_t *lex, TCOD_lex_t *savepoint)""" + + @staticmethod + def TCOD_lex_savepoint(lex: Any, savepoint: Any, /) -> None: + """void TCOD_lex_savepoint(TCOD_lex_t *lex, TCOD_lex_t *savepoint)""" + + @staticmethod + def TCOD_lex_set_data_buffer(lex: Any, dat: Any, /) -> None: + """void TCOD_lex_set_data_buffer(TCOD_lex_t *lex, char *dat)""" + + @staticmethod + def TCOD_lex_set_data_file(lex: Any, filename: Any, /) -> bool: + """bool TCOD_lex_set_data_file(TCOD_lex_t *lex, const char *filename)""" + + @staticmethod + def TCOD_line(xFrom: int, yFrom: int, xTo: int, yTo: int, listener: Any, /) -> bool: + """bool TCOD_line(int xFrom, int yFrom, int xTo, int yTo, TCOD_line_listener_t listener)""" + + @staticmethod + def TCOD_line_init(xFrom: int, yFrom: int, xTo: int, yTo: int, /) -> None: + """void TCOD_line_init(int xFrom, int yFrom, int xTo, int yTo)""" + + @staticmethod + def TCOD_line_init_mt(xFrom: int, yFrom: int, xTo: int, yTo: int, data: Any, /) -> None: + """void TCOD_line_init_mt(int xFrom, int yFrom, int xTo, int yTo, TCOD_bresenham_data_t *data)""" + + @staticmethod + def TCOD_line_mt(xFrom: int, yFrom: int, xTo: int, yTo: int, listener: Any, data: Any, /) -> bool: + """bool TCOD_line_mt(int xFrom, int yFrom, int xTo, int yTo, TCOD_line_listener_t listener, TCOD_bresenham_data_t *data)""" + + @staticmethod + def TCOD_line_step(xCur: Any, yCur: Any, /) -> bool: + """bool TCOD_line_step(int *xCur, int *yCur)""" + + @staticmethod + def TCOD_line_step_mt(xCur: Any, yCur: Any, data: Any, /) -> bool: + """bool TCOD_line_step_mt(int *xCur, int *yCur, TCOD_bresenham_data_t *data)""" + + @staticmethod + def TCOD_list_add_all(l: Any, l2: Any, /) -> None: + """void TCOD_list_add_all(TCOD_list_t l, TCOD_list_t l2)""" + + @staticmethod + def TCOD_list_allocate(nb_elements: int, /) -> Any: + """TCOD_list_t TCOD_list_allocate(int nb_elements)""" + + @staticmethod + def TCOD_list_begin(l: Any, /) -> Any: + """void **TCOD_list_begin(TCOD_list_t l)""" + + @staticmethod + def TCOD_list_clear(l: Any, /) -> None: + """void TCOD_list_clear(TCOD_list_t l)""" + + @staticmethod + def TCOD_list_clear_and_delete(l: Any, /) -> None: + """void TCOD_list_clear_and_delete(TCOD_list_t l)""" + + @staticmethod + def TCOD_list_contains(l: Any, elt: Any, /) -> bool: + """bool TCOD_list_contains(TCOD_list_t l, const void *elt)""" + + @staticmethod + def TCOD_list_delete(l: Any, /) -> None: + """void TCOD_list_delete(TCOD_list_t l)""" + + @staticmethod + def TCOD_list_duplicate(l: Any, /) -> Any: + """TCOD_list_t TCOD_list_duplicate(TCOD_list_t l)""" + + @staticmethod + def TCOD_list_end(l: Any, /) -> Any: + """void **TCOD_list_end(TCOD_list_t l)""" + + @staticmethod + def TCOD_list_get(l: Any, idx: int, /) -> Any: + """void *TCOD_list_get(TCOD_list_t l, int idx)""" + + @staticmethod + def TCOD_list_insert_before(l: Any, elt: Any, before: int, /) -> Any: + """void **TCOD_list_insert_before(TCOD_list_t l, const void *elt, int before)""" + + @staticmethod + def TCOD_list_is_empty(l: Any, /) -> bool: + """bool TCOD_list_is_empty(TCOD_list_t l)""" + + @staticmethod + def TCOD_list_new() -> Any: + """TCOD_list_t TCOD_list_new(void)""" + + @staticmethod + def TCOD_list_peek(l: Any, /) -> Any: + """void *TCOD_list_peek(TCOD_list_t l)""" + + @staticmethod + def TCOD_list_pop(l: Any, /) -> Any: + """void *TCOD_list_pop(TCOD_list_t l)""" + + @staticmethod + def TCOD_list_push(l: Any, elt: Any, /) -> None: + """void TCOD_list_push(TCOD_list_t l, const void *elt)""" + + @staticmethod + def TCOD_list_remove(l: Any, elt: Any, /) -> None: + """void TCOD_list_remove(TCOD_list_t l, const void *elt)""" + + @staticmethod + def TCOD_list_remove_fast(l: Any, elt: Any, /) -> None: + """void TCOD_list_remove_fast(TCOD_list_t l, const void *elt)""" + + @staticmethod + def TCOD_list_remove_iterator(l: Any, elt: Any, /) -> Any: + """void **TCOD_list_remove_iterator(TCOD_list_t l, void **elt)""" + + @staticmethod + def TCOD_list_remove_iterator_fast(l: Any, elt: Any, /) -> Any: + """void **TCOD_list_remove_iterator_fast(TCOD_list_t l, void **elt)""" + + @staticmethod + def TCOD_list_reverse(l: Any, /) -> None: + """void TCOD_list_reverse(TCOD_list_t l)""" + + @staticmethod + def TCOD_list_set(l: Any, elt: Any, idx: int, /) -> None: + """void TCOD_list_set(TCOD_list_t l, const void *elt, int idx)""" + + @staticmethod + def TCOD_list_set_size(l: Any, size: int, /) -> None: + """void TCOD_list_set_size(TCOD_list_t l, int size)""" + + @staticmethod + def TCOD_list_size(l: Any, /) -> int: + """int TCOD_list_size(TCOD_list_t l)""" + + @staticmethod + def TCOD_load_bdf(path: Any, /) -> Any: + """TCOD_Tileset *TCOD_load_bdf(const char *path)""" + + @staticmethod + def TCOD_load_bdf_memory(size: int, buffer: Any, /) -> Any: + """TCOD_Tileset *TCOD_load_bdf_memory(int size, const unsigned char *buffer)""" + + @staticmethod + def TCOD_load_library(path: Any, /) -> Any: + """TCOD_library_t TCOD_load_library(const char *path)""" + + @staticmethod + def TCOD_load_truetype_font_(path: Any, tile_width: int, tile_height: int, /) -> Any: + """TCOD_Tileset *TCOD_load_truetype_font_(const char *path, int tile_width, int tile_height)""" + + @staticmethod + def TCOD_load_xp(path: Any, n: int, out: Any, /) -> int: + """int TCOD_load_xp(const char *path, int n, TCOD_Console **out)""" + + @staticmethod + def TCOD_load_xp_from_memory(n_data: int, data: Any, n_out: int, out: Any, /) -> int: + """int TCOD_load_xp_from_memory(int n_data, const unsigned char *data, int n_out, TCOD_Console **out)""" + + @staticmethod + def TCOD_log_verbose_(msg: Any, level: int, source: Any, line: int, /) -> None: + """void TCOD_log_verbose_(const char *msg, int level, const char *source, int line)""" + + @staticmethod + def TCOD_log_verbose_fmt_(level: int, source: Any, line: int, fmt: Any, /, *__args: Any) -> None: + """void TCOD_log_verbose_fmt_(int level, const char *source, int line, const char *fmt, ...)""" + + @staticmethod + def TCOD_map_clear(map: Any, transparent: bool, walkable: bool, /) -> None: + """void TCOD_map_clear(TCOD_Map *map, bool transparent, bool walkable)""" + + @staticmethod + def TCOD_map_compute_fov(map: Any, pov_x: int, pov_y: int, max_radius: int, light_walls: bool, algo: Any, /) -> Any: + """TCOD_Error TCOD_map_compute_fov(TCOD_Map *map, int pov_x, int pov_y, int max_radius, bool light_walls, TCOD_fov_algorithm_t algo)""" + + @staticmethod + def TCOD_map_compute_fov_circular_raycasting( + map: Any, pov_x: int, pov_y: int, max_radius: int, light_walls: bool, / + ) -> Any: + """TCOD_Error TCOD_map_compute_fov_circular_raycasting(TCOD_Map *map, int pov_x, int pov_y, int max_radius, bool light_walls)""" + + @staticmethod + def TCOD_map_compute_fov_diamond_raycasting( + map: Any, pov_x: int, pov_y: int, max_radius: int, light_walls: bool, / + ) -> Any: + """TCOD_Error TCOD_map_compute_fov_diamond_raycasting(TCOD_Map *map, int pov_x, int pov_y, int max_radius, bool light_walls)""" + + @staticmethod + def TCOD_map_compute_fov_permissive2( + map: Any, pov_x: int, pov_y: int, max_radius: int, light_walls: bool, permissiveness: int, / + ) -> Any: + """TCOD_Error TCOD_map_compute_fov_permissive2(TCOD_Map *map, int pov_x, int pov_y, int max_radius, bool light_walls, int permissiveness)""" + + @staticmethod + def TCOD_map_compute_fov_recursive_shadowcasting( + map: Any, pov_x: int, pov_y: int, max_radius: int, light_walls: bool, / + ) -> Any: + """TCOD_Error TCOD_map_compute_fov_recursive_shadowcasting(TCOD_Map *map, int pov_x, int pov_y, int max_radius, bool light_walls)""" + + @staticmethod + def TCOD_map_compute_fov_restrictive_shadowcasting( + map: Any, pov_x: int, pov_y: int, max_radius: int, light_walls: bool, / + ) -> Any: + """TCOD_Error TCOD_map_compute_fov_restrictive_shadowcasting(TCOD_Map *map, int pov_x, int pov_y, int max_radius, bool light_walls)""" + + @staticmethod + def TCOD_map_compute_fov_symmetric_shadowcast( + map: Any, pov_x: int, pov_y: int, max_radius: int, light_walls: bool, / + ) -> Any: + """TCOD_Error TCOD_map_compute_fov_symmetric_shadowcast(TCOD_Map *map, int pov_x, int pov_y, int max_radius, bool light_walls)""" + + @staticmethod + def TCOD_map_copy(source: Any, dest: Any, /) -> Any: + """TCOD_Error TCOD_map_copy(const TCOD_Map *source, TCOD_Map *dest)""" + + @staticmethod + def TCOD_map_delete(map: Any, /) -> None: + """void TCOD_map_delete(TCOD_Map *map)""" + + @staticmethod + def TCOD_map_get_height(map: Any, /) -> int: + """int TCOD_map_get_height(const TCOD_Map *map)""" + + @staticmethod + def TCOD_map_get_nb_cells(map: Any, /) -> int: + """int TCOD_map_get_nb_cells(const TCOD_Map *map)""" + + @staticmethod + def TCOD_map_get_width(map: Any, /) -> int: + """int TCOD_map_get_width(const TCOD_Map *map)""" + + @staticmethod + def TCOD_map_in_bounds(map: Any, x: int, y: int, /) -> bool: + """inline static bool TCOD_map_in_bounds(const struct TCOD_Map *map, int x, int y)""" + + @staticmethod + def TCOD_map_is_in_fov(map: Any, x: int, y: int, /) -> bool: + """bool TCOD_map_is_in_fov(const TCOD_Map *map, int x, int y)""" + + @staticmethod + def TCOD_map_is_transparent(map: Any, x: int, y: int, /) -> bool: + """bool TCOD_map_is_transparent(const TCOD_Map *map, int x, int y)""" + + @staticmethod + def TCOD_map_is_walkable(map: Any, x: int, y: int, /) -> bool: + """bool TCOD_map_is_walkable(TCOD_Map *map, int x, int y)""" + + @staticmethod + def TCOD_map_new(width: int, height: int, /) -> Any: + """TCOD_Map *TCOD_map_new(int width, int height)""" + + @staticmethod + def TCOD_map_postprocess(map: Any, pov_x: int, pov_y: int, radius: int, /) -> Any: + """TCOD_Error TCOD_map_postprocess(TCOD_Map *map, int pov_x, int pov_y, int radius)""" + + @staticmethod + def TCOD_map_set_in_fov(map: Any, x: int, y: int, fov: bool, /) -> None: + """void TCOD_map_set_in_fov(TCOD_Map *map, int x, int y, bool fov)""" + + @staticmethod + def TCOD_map_set_properties(map: Any, x: int, y: int, is_transparent: bool, is_walkable: bool, /) -> None: + """void TCOD_map_set_properties(TCOD_Map *map, int x, int y, bool is_transparent, bool is_walkable)""" + + @staticmethod + def TCOD_minheap_heapify(minheap: Any, /) -> None: + """void TCOD_minheap_heapify(struct TCOD_Heap *minheap)""" + + @staticmethod + def TCOD_minheap_pop(minheap: Any, out: Any, /) -> None: + """void TCOD_minheap_pop(struct TCOD_Heap *minheap, void *out)""" + + @staticmethod + def TCOD_minheap_push(minheap: Any, priority: int, data: Any, /) -> int: + """int TCOD_minheap_push(struct TCOD_Heap *minheap, int priority, const void *data)""" + + @staticmethod + def TCOD_mouse_get_status() -> Any: + """TCOD_mouse_t TCOD_mouse_get_status(void)""" + + @staticmethod + def TCOD_mouse_get_status_wrapper(holder: Any, /) -> None: + """void TCOD_mouse_get_status_wrapper(TCOD_mouse_t *holder)""" + + @staticmethod + def TCOD_mouse_includes_touch(enable: bool, /) -> None: + """void TCOD_mouse_includes_touch(bool enable)""" + + @staticmethod + def TCOD_mouse_is_cursor_visible() -> bool: + """bool TCOD_mouse_is_cursor_visible(void)""" + + @staticmethod + def TCOD_mouse_move(x: int, y: int, /) -> None: + """void TCOD_mouse_move(int x, int y)""" + + @staticmethod + def TCOD_mouse_show_cursor(visible: bool, /) -> None: + """void TCOD_mouse_show_cursor(bool visible)""" + + @staticmethod + def TCOD_mutex_delete(mut: Any, /) -> None: + """void TCOD_mutex_delete(TCOD_mutex_t mut)""" + + @staticmethod + def TCOD_mutex_in(mut: Any, /) -> None: + """void TCOD_mutex_in(TCOD_mutex_t mut)""" + + @staticmethod + def TCOD_mutex_new() -> Any: + """TCOD_mutex_t TCOD_mutex_new(void)""" + + @staticmethod + def TCOD_mutex_out(mut: Any, /) -> None: + """void TCOD_mutex_out(TCOD_mutex_t mut)""" + + @staticmethod + def TCOD_namegen_destroy() -> None: + """void TCOD_namegen_destroy(void)""" + + @staticmethod + def TCOD_namegen_generate(name: Any, allocate: bool, /) -> Any: + """char *TCOD_namegen_generate(const char *name, bool allocate)""" + + @staticmethod + def TCOD_namegen_generate_custom(name: Any, rule: Any, allocate: bool, /) -> Any: + """char *TCOD_namegen_generate_custom(const char *name, const char *rule, bool allocate)""" + + @staticmethod + def TCOD_namegen_get_nb_sets_wrapper() -> int: + """int TCOD_namegen_get_nb_sets_wrapper(void)""" + + @staticmethod + def TCOD_namegen_get_sets() -> Any: + """TCOD_list_t TCOD_namegen_get_sets(void)""" + + @staticmethod + def TCOD_namegen_get_sets_wrapper(sets: Any, /) -> None: + """void TCOD_namegen_get_sets_wrapper(char **sets)""" + + @staticmethod + def TCOD_namegen_parse(filename: Any, random: Any, /) -> None: + """void TCOD_namegen_parse(const char *filename, TCOD_Random *random)""" + + @staticmethod + def TCOD_noise_delete(noise: Any, /) -> None: + """void TCOD_noise_delete(TCOD_Noise *noise)""" + + @staticmethod + def TCOD_noise_get(noise: Any, f: Any, /) -> float: + """float TCOD_noise_get(TCOD_Noise *noise, const float *f)""" + + @staticmethod + def TCOD_noise_get_ex(noise: Any, f: Any, type: Any, /) -> float: + """float TCOD_noise_get_ex(TCOD_Noise *noise, const float *f, TCOD_noise_type_t type)""" + + @staticmethod + def TCOD_noise_get_fbm(noise: Any, f: Any, octaves: float, /) -> float: + """float TCOD_noise_get_fbm(TCOD_Noise *noise, const float *f, float octaves)""" + + @staticmethod + def TCOD_noise_get_fbm_ex(noise: Any, f: Any, octaves: float, type: Any, /) -> float: + """float TCOD_noise_get_fbm_ex(TCOD_Noise *noise, const float *f, float octaves, TCOD_noise_type_t type)""" + + @staticmethod + def TCOD_noise_get_fbm_vectorized( + noise: Any, type: Any, octaves: float, n: int, x: Any, y: Any, z: Any, w: Any, out: Any, / + ) -> None: + """void TCOD_noise_get_fbm_vectorized(TCOD_Noise *noise, TCOD_noise_type_t type, float octaves, int n, float *x, float *y, float *z, float *w, float *out)""" + + @staticmethod + def TCOD_noise_get_turbulence(noise: Any, f: Any, octaves: float, /) -> float: + """float TCOD_noise_get_turbulence(TCOD_Noise *noise, const float *f, float octaves)""" + + @staticmethod + def TCOD_noise_get_turbulence_ex(noise: Any, f: Any, octaves: float, type: Any, /) -> float: + """float TCOD_noise_get_turbulence_ex(TCOD_Noise *noise, const float *f, float octaves, TCOD_noise_type_t type)""" + + @staticmethod + def TCOD_noise_get_turbulence_vectorized( + noise: Any, type: Any, octaves: float, n: int, x: Any, y: Any, z: Any, w: Any, out: Any, / + ) -> None: + """void TCOD_noise_get_turbulence_vectorized(TCOD_Noise *noise, TCOD_noise_type_t type, float octaves, int n, float *x, float *y, float *z, float *w, float *out)""" + + @staticmethod + def TCOD_noise_get_vectorized(noise: Any, type: Any, n: int, x: Any, y: Any, z: Any, w: Any, out: Any, /) -> None: + """void TCOD_noise_get_vectorized(TCOD_Noise *noise, TCOD_noise_type_t type, int n, float *x, float *y, float *z, float *w, float *out)""" + + @staticmethod + def TCOD_noise_new(dimensions: int, hurst: float, lacunarity: float, random: Any, /) -> Any: + """TCOD_Noise *TCOD_noise_new(int dimensions, float hurst, float lacunarity, TCOD_Random *random)""" + + @staticmethod + def TCOD_noise_set_type(noise: Any, type: Any, /) -> None: + """void TCOD_noise_set_type(TCOD_Noise *noise, TCOD_noise_type_t type)""" + + @staticmethod + def TCOD_parse_bool_value() -> Any: + """TCOD_value_t TCOD_parse_bool_value(void)""" + + @staticmethod + def TCOD_parse_char_value() -> Any: + """TCOD_value_t TCOD_parse_char_value(void)""" + + @staticmethod + def TCOD_parse_color_value() -> Any: + """TCOD_value_t TCOD_parse_color_value(void)""" + + @staticmethod + def TCOD_parse_dice_value() -> Any: + """TCOD_value_t TCOD_parse_dice_value(void)""" + + @staticmethod + def TCOD_parse_float_value() -> Any: + """TCOD_value_t TCOD_parse_float_value(void)""" + + @staticmethod + def TCOD_parse_integer_value() -> Any: + """TCOD_value_t TCOD_parse_integer_value(void)""" + + @staticmethod + def TCOD_parse_property_value(parser: Any, def_: Any, propname: Any, list: bool, /) -> Any: + """TCOD_value_t TCOD_parse_property_value(TCOD_Parser *parser, TCOD_ParserStruct *def, char *propname, bool list)""" + + @staticmethod + def TCOD_parse_string_value() -> Any: + """TCOD_value_t TCOD_parse_string_value(void)""" + + @staticmethod + def TCOD_parse_value_list_value(def_: Any, list_num: int, /) -> Any: + """TCOD_value_t TCOD_parse_value_list_value(TCOD_ParserStruct *def, int list_num)""" + + @staticmethod + def TCOD_parser_delete(parser: Any, /) -> None: + """void TCOD_parser_delete(TCOD_Parser *parser)""" + + @staticmethod + def TCOD_parser_error(msg: Any, /, *__args: Any) -> None: + """void TCOD_parser_error(const char *msg, ...)""" + + @staticmethod + def TCOD_parser_get_bool_property(parser: Any, name: Any, /) -> bool: + """bool TCOD_parser_get_bool_property(TCOD_Parser *parser, const char *name)""" + + @staticmethod + def TCOD_parser_get_char_property(parser: Any, name: Any, /) -> int: + """int TCOD_parser_get_char_property(TCOD_Parser *parser, const char *name)""" + + @staticmethod + def TCOD_parser_get_color_property(parser: Any, name: Any, /) -> Any: + """TCOD_color_t TCOD_parser_get_color_property(TCOD_Parser *parser, const char *name)""" + + @staticmethod + def TCOD_parser_get_color_property_wrapper(parser: Any, name: Any, /) -> Any: + """colornum_t TCOD_parser_get_color_property_wrapper(TCOD_parser_t parser, const char *name)""" + + @staticmethod + def TCOD_parser_get_custom_property(parser: Any, name: Any, /) -> Any: + """void *TCOD_parser_get_custom_property(TCOD_Parser *parser, const char *name)""" + + @staticmethod + def TCOD_parser_get_dice_property(parser: Any, name: Any, /) -> Any: + """TCOD_dice_t TCOD_parser_get_dice_property(TCOD_Parser *parser, const char *name)""" + + @staticmethod + def TCOD_parser_get_dice_property_py(parser: Any, name: Any, dice: Any, /) -> None: + """void TCOD_parser_get_dice_property_py(TCOD_Parser *parser, const char *name, TCOD_dice_t *dice)""" + + @staticmethod + def TCOD_parser_get_float_property(parser: Any, name: Any, /) -> float: + """float TCOD_parser_get_float_property(TCOD_Parser *parser, const char *name)""" + + @staticmethod + def TCOD_parser_get_int_property(parser: Any, name: Any, /) -> int: + """int TCOD_parser_get_int_property(TCOD_Parser *parser, const char *name)""" + + @staticmethod + def TCOD_parser_get_list_property(parser: Any, name: Any, type: Any, /) -> Any: + """TCOD_list_t TCOD_parser_get_list_property(TCOD_Parser *parser, const char *name, TCOD_value_type_t type)""" + + @staticmethod + def TCOD_parser_get_string_property(parser: Any, name: Any, /) -> Any: + """const char *TCOD_parser_get_string_property(TCOD_Parser *parser, const char *name)""" + + @staticmethod + def TCOD_parser_has_property(parser: Any, name: Any, /) -> bool: + """bool TCOD_parser_has_property(TCOD_Parser *parser, const char *name)""" + + @staticmethod + def TCOD_parser_new() -> Any: + """TCOD_Parser *TCOD_parser_new(void)""" + + @staticmethod + def TCOD_parser_new_custom_type(parser: Any, custom_type_parser: Any, /) -> Any: + """TCOD_value_type_t TCOD_parser_new_custom_type(TCOD_Parser *parser, TCOD_parser_custom_t custom_type_parser)""" + + @staticmethod + def TCOD_parser_new_struct(parser: Any, name: Any, /) -> Any: + """TCOD_ParserStruct *TCOD_parser_new_struct(TCOD_Parser *parser, const char *name)""" + + @staticmethod + def TCOD_parser_run(parser: Any, filename: Any, listener: Any, /) -> None: + """void TCOD_parser_run(TCOD_Parser *parser, const char *filename, TCOD_parser_listener_t *listener)""" + + @staticmethod + def TCOD_path_compute(path: Any, ox: int, oy: int, dx: int, dy: int, /) -> bool: + """bool TCOD_path_compute(TCOD_path_t path, int ox, int oy, int dx, int dy)""" + + @staticmethod + def TCOD_path_delete(path: Any, /) -> None: + """void TCOD_path_delete(TCOD_path_t path)""" + + @staticmethod + def TCOD_path_get(path: Any, index: int, x: Any, y: Any, /) -> None: + """void TCOD_path_get(TCOD_path_t path, int index, int *x, int *y)""" + + @staticmethod + def TCOD_path_get_destination(path: Any, x: Any, y: Any, /) -> None: + """void TCOD_path_get_destination(TCOD_path_t path, int *x, int *y)""" + + @staticmethod + def TCOD_path_get_origin(path: Any, x: Any, y: Any, /) -> None: + """void TCOD_path_get_origin(TCOD_path_t path, int *x, int *y)""" + + @staticmethod + def TCOD_path_is_empty(path: Any, /) -> bool: + """bool TCOD_path_is_empty(TCOD_path_t path)""" + + @staticmethod + def TCOD_path_new_using_function( + map_width: int, map_height: int, func: Any, user_data: Any, diagonalCost: float, / + ) -> Any: + """TCOD_path_t TCOD_path_new_using_function(int map_width, int map_height, TCOD_path_func_t func, void *user_data, float diagonalCost)""" + + @staticmethod + def TCOD_path_new_using_map(map: Any, diagonalCost: float, /) -> Any: + """TCOD_path_t TCOD_path_new_using_map(TCOD_Map *map, float diagonalCost)""" + + @staticmethod + def TCOD_path_reverse(path: Any, /) -> None: + """void TCOD_path_reverse(TCOD_path_t path)""" + + @staticmethod + def TCOD_path_size(path: Any, /) -> int: + """int TCOD_path_size(TCOD_path_t path)""" + + @staticmethod + def TCOD_path_walk(path: Any, x: Any, y: Any, recalculate_when_needed: bool, /) -> bool: + """bool TCOD_path_walk(TCOD_path_t path, int *x, int *y, bool recalculate_when_needed)""" + + @staticmethod + def TCOD_pf_compute(path: Any, /) -> int: + """int TCOD_pf_compute(struct TCOD_Pathfinder *path)""" + + @staticmethod + def TCOD_pf_compute_step(path: Any, /) -> int: + """int TCOD_pf_compute_step(struct TCOD_Pathfinder *path)""" + + @staticmethod + def TCOD_pf_delete(path: Any, /) -> None: + """void TCOD_pf_delete(struct TCOD_Pathfinder *path)""" + + @staticmethod + def TCOD_pf_new(ndim: int, shape: Any, /) -> Any: + """struct TCOD_Pathfinder *TCOD_pf_new(int ndim, const size_t *shape)""" + + @staticmethod + def TCOD_pf_recompile(path: Any, /) -> int: + """int TCOD_pf_recompile(struct TCOD_Pathfinder *path)""" + + @staticmethod + def TCOD_pf_set_distance_pointer(path: Any, data: Any, int_type: int, strides: Any, /) -> None: + """void TCOD_pf_set_distance_pointer(struct TCOD_Pathfinder *path, void *data, int int_type, const size_t *strides)""" + + @staticmethod + def TCOD_pf_set_graph2d_pointer( + path: Any, data: Any, int_type: int, strides: Any, cardinal: int, diagonal: int, / + ) -> None: + """void TCOD_pf_set_graph2d_pointer(struct TCOD_Pathfinder *path, void *data, int int_type, const size_t *strides, int cardinal, int diagonal)""" + + @staticmethod + def TCOD_pf_set_traversal_pointer(path: Any, data: Any, int_type: int, strides: Any, /) -> None: + """void TCOD_pf_set_traversal_pointer(struct TCOD_Pathfinder *path, void *data, int int_type, const size_t *strides)""" + + @staticmethod + def TCOD_printf_rgb(console: Any, params: Any, fmt: Any, /, *__args: Any) -> int: + """int TCOD_printf_rgb(TCOD_Console *console, TCOD_PrintParamsRGB params, const char *fmt, ...)""" + + @staticmethod + def TCOD_printn_rgb(console: Any, params: Any, n: int, str: Any, /) -> int: + """int TCOD_printn_rgb(TCOD_Console *console, TCOD_PrintParamsRGB params, int n, const char *str)""" + + @staticmethod + def TCOD_quit() -> None: + """void TCOD_quit(void)""" + + @staticmethod + def TCOD_random_delete(mersenne: Any, /) -> None: + """void TCOD_random_delete(TCOD_Random *mersenne)""" + + @staticmethod + def TCOD_random_dice_new(s: Any, /) -> Any: + """TCOD_dice_t TCOD_random_dice_new(const char *s)""" + + @staticmethod + def TCOD_random_dice_roll(mersenne: Any, dice: Any, /) -> int: + """int TCOD_random_dice_roll(TCOD_Random *mersenne, TCOD_dice_t dice)""" + + @staticmethod + def TCOD_random_dice_roll_s(mersenne: Any, s: Any, /) -> int: + """int TCOD_random_dice_roll_s(TCOD_Random *mersenne, const char *s)""" + + @staticmethod + def TCOD_random_get_d(mersenne: Any, min: float, max: float, /) -> float: + """double TCOD_random_get_d(TCOD_random_t mersenne, double min, double max)""" + + @staticmethod + def TCOD_random_get_double(mersenne: Any, min: float, max: float, /) -> float: + """double TCOD_random_get_double(TCOD_Random *mersenne, double min, double max)""" + + @staticmethod + def TCOD_random_get_double_mean(mersenne: Any, min: float, max: float, mean: float, /) -> float: + """double TCOD_random_get_double_mean(TCOD_Random *mersenne, double min, double max, double mean)""" + + @staticmethod + def TCOD_random_get_float(mersenne: Any, min: float, max: float, /) -> float: + """float TCOD_random_get_float(TCOD_Random *mersenne, float min, float max)""" + + @staticmethod + def TCOD_random_get_float_mean(mersenne: Any, min: float, max: float, mean: float, /) -> float: + """float TCOD_random_get_float_mean(TCOD_Random *mersenne, float min, float max, float mean)""" + + @staticmethod + def TCOD_random_get_gaussian_double(mersenne: Any, mean: float, std_deviation: float, /) -> float: + """double TCOD_random_get_gaussian_double(TCOD_random_t mersenne, double mean, double std_deviation)""" + + @staticmethod + def TCOD_random_get_gaussian_double_inv(mersenne: Any, mean: float, std_deviation: float, /) -> float: + """double TCOD_random_get_gaussian_double_inv(TCOD_random_t mersenne, double mean, double std_deviation)""" + + @staticmethod + def TCOD_random_get_gaussian_double_range(mersenne: Any, min: float, max: float, /) -> float: + """double TCOD_random_get_gaussian_double_range(TCOD_random_t mersenne, double min, double max)""" + + @staticmethod + def TCOD_random_get_gaussian_double_range_custom(mersenne: Any, min: float, max: float, mean: float, /) -> float: + """double TCOD_random_get_gaussian_double_range_custom(TCOD_random_t mersenne, double min, double max, double mean)""" + + @staticmethod + def TCOD_random_get_gaussian_double_range_custom_inv( + mersenne: Any, min: float, max: float, mean: float, / + ) -> float: + """double TCOD_random_get_gaussian_double_range_custom_inv(TCOD_random_t mersenne, double min, double max, double mean)""" + + @staticmethod + def TCOD_random_get_gaussian_double_range_inv(mersenne: Any, min: float, max: float, /) -> float: + """double TCOD_random_get_gaussian_double_range_inv(TCOD_random_t mersenne, double min, double max)""" + + @staticmethod + def TCOD_random_get_i(mersenne: Any, min: int, max: int, /) -> int: + """int TCOD_random_get_i(TCOD_random_t mersenne, int min, int max)""" + + @staticmethod + def TCOD_random_get_instance() -> Any: + """TCOD_Random *TCOD_random_get_instance(void)""" + + @staticmethod + def TCOD_random_get_int(mersenne: Any, min: int, max: int, /) -> int: + """int TCOD_random_get_int(TCOD_Random *mersenne, int min, int max)""" + + @staticmethod + def TCOD_random_get_int_mean(mersenne: Any, min: int, max: int, mean: int, /) -> int: + """int TCOD_random_get_int_mean(TCOD_Random *mersenne, int min, int max, int mean)""" + + @staticmethod + def TCOD_random_new(algo: Any, /) -> Any: + """TCOD_Random *TCOD_random_new(TCOD_random_algo_t algo)""" + + @staticmethod + def TCOD_random_new_from_seed(algo: Any, seed: Any, /) -> Any: + """TCOD_Random *TCOD_random_new_from_seed(TCOD_random_algo_t algo, uint32_t seed)""" + + @staticmethod + def TCOD_random_restore(mersenne: Any, backup: Any, /) -> None: + """void TCOD_random_restore(TCOD_Random *mersenne, TCOD_Random *backup)""" + + @staticmethod + def TCOD_random_save(mersenne: Any, /) -> Any: + """TCOD_Random *TCOD_random_save(TCOD_Random *mersenne)""" + + @staticmethod + def TCOD_random_set_distribution(mersenne: Any, distribution: Any, /) -> None: + """void TCOD_random_set_distribution(TCOD_Random *mersenne, TCOD_distribution_t distribution)""" + + @staticmethod + def TCOD_renderer_init_sdl2( + x: int, y: int, width: int, height: int, title: Any, window_flags: int, vsync: int, tileset: Any, / + ) -> Any: + """struct TCOD_Context *TCOD_renderer_init_sdl2(int x, int y, int width, int height, const char *title, int window_flags, int vsync, struct TCOD_Tileset *tileset)""" + + @staticmethod + def TCOD_renderer_init_sdl3(window_props: Any, renderer_props: Any, tileset: Any, /) -> Any: + """TCOD_Context *TCOD_renderer_init_sdl3(SDL_PropertiesID window_props, SDL_PropertiesID renderer_props, struct TCOD_Tileset *tileset)""" + + @staticmethod + def TCOD_renderer_init_xterm( + window_x: int, window_y: int, pixel_width: int, pixel_height: int, columns: int, rows: int, window_title: Any, / + ) -> Any: + """TCOD_Context *TCOD_renderer_init_xterm(int window_x, int window_y, int pixel_width, int pixel_height, int columns, int rows, const char *window_title)""" + + @staticmethod + def TCOD_rng_splitmix64_next(state: Any, /) -> Any: + """inline static uint64_t TCOD_rng_splitmix64_next(uint64_t *state)""" + + @staticmethod + def TCOD_save_xp(n: int, consoles: Any, path: Any, compress_level: int, /) -> Any: + """TCOD_Error TCOD_save_xp(int n, const TCOD_Console * const *consoles, const char *path, int compress_level)""" + + @staticmethod + def TCOD_save_xp_to_memory(n_consoles: int, consoles: Any, n_out: int, out: Any, compression_level: int, /) -> int: + """int TCOD_save_xp_to_memory(int n_consoles, const TCOD_Console * const *consoles, int n_out, unsigned char *out, int compression_level)""" + + @staticmethod + def TCOD_sdl2_atlas_delete(atlas: Any, /) -> None: + """void TCOD_sdl2_atlas_delete(struct TCOD_TilesetAtlasSDL2 *atlas)""" + + @staticmethod + def TCOD_sdl2_atlas_new(renderer: Any, tileset: Any, /) -> Any: + """struct TCOD_TilesetAtlasSDL2 *TCOD_sdl2_atlas_new(struct SDL_Renderer *renderer, struct TCOD_Tileset *tileset)""" + + @staticmethod + def TCOD_sdl2_render_texture(atlas: Any, console: Any, cache: Any, target: Any, /) -> Any: + """TCOD_Error TCOD_sdl2_render_texture(const struct TCOD_TilesetAtlasSDL2 *atlas, const struct TCOD_Console *console, struct TCOD_Console *cache, struct SDL_Texture *target)""" + + @staticmethod + def TCOD_sdl2_render_texture_setup(atlas: Any, console: Any, cache: Any, target: Any, /) -> Any: + """TCOD_Error TCOD_sdl2_render_texture_setup(const struct TCOD_TilesetAtlasSDL2 *atlas, const struct TCOD_Console *console, struct TCOD_Console **cache, struct SDL_Texture **target)""" + + @staticmethod + def TCOD_semaphore_delete(sem: Any, /) -> None: + """void TCOD_semaphore_delete(TCOD_semaphore_t sem)""" + + @staticmethod + def TCOD_semaphore_lock(sem: Any, /) -> None: + """void TCOD_semaphore_lock(TCOD_semaphore_t sem)""" + + @staticmethod + def TCOD_semaphore_new(initVal: int, /) -> Any: + """TCOD_semaphore_t TCOD_semaphore_new(int initVal)""" + + @staticmethod + def TCOD_semaphore_unlock(sem: Any, /) -> None: + """void TCOD_semaphore_unlock(TCOD_semaphore_t sem)""" + + @staticmethod + def TCOD_set_default_tileset(tileset: Any, /) -> None: + """void TCOD_set_default_tileset(TCOD_Tileset *tileset)""" + + @staticmethod + def TCOD_set_error(msg: Any, /) -> Any: + """TCOD_Error TCOD_set_error(const char *msg)""" + + @staticmethod + def TCOD_set_errorf(fmt: Any, /, *__args: Any) -> Any: + """TCOD_Error TCOD_set_errorf(const char *fmt, ...)""" + + @staticmethod + def TCOD_set_log_callback(callback: Any, userdata: Any, /) -> None: + """void TCOD_set_log_callback(TCOD_LoggingCallback callback, void *userdata)""" + + @staticmethod + def TCOD_set_log_level(level: int, /) -> None: + """void TCOD_set_log_level(int level)""" + + @staticmethod + def TCOD_strcasecmp(s1: Any, s2: Any, /) -> int: + """int TCOD_strcasecmp(const char *s1, const char *s2)""" + + @staticmethod + def TCOD_strdup(s: Any, /) -> Any: + """char *TCOD_strdup(const char *s)""" + + @staticmethod + def TCOD_strncasecmp(s1: Any, s2: Any, n: int, /) -> int: + """int TCOD_strncasecmp(const char *s1, const char *s2, size_t n)""" + + @staticmethod + def TCOD_struct_add_flag(def_: Any, propname: Any, /) -> None: + """void TCOD_struct_add_flag(TCOD_ParserStruct *def, const char *propname)""" + + @staticmethod + def TCOD_struct_add_list_property(def_: Any, name: Any, type: Any, mandatory: bool, /) -> None: + """void TCOD_struct_add_list_property(TCOD_ParserStruct *def, const char *name, TCOD_value_type_t type, bool mandatory)""" + + @staticmethod + def TCOD_struct_add_property(def_: Any, name: Any, type: Any, mandatory: bool, /) -> None: + """void TCOD_struct_add_property(TCOD_ParserStruct *def, const char *name, TCOD_value_type_t type, bool mandatory)""" + + @staticmethod + def TCOD_struct_add_structure(def_: Any, sub_structure: Any, /) -> None: + """void TCOD_struct_add_structure(TCOD_ParserStruct *def, const TCOD_ParserStruct *sub_structure)""" + + @staticmethod + def TCOD_struct_add_value_list(def_: Any, name: Any, value_list: Any, mandatory: bool, /) -> None: + """void TCOD_struct_add_value_list(TCOD_ParserStruct *def, const char *name, const char * const *value_list, bool mandatory)""" + + @staticmethod + def TCOD_struct_add_value_list_sized(def_: Any, name: Any, value_list: Any, size: int, mandatory: bool, /) -> None: + """void TCOD_struct_add_value_list_sized(TCOD_ParserStruct *def, const char *name, const char * const *value_list, int size, bool mandatory)""" + + @staticmethod + def TCOD_struct_get_name(def_: Any, /) -> Any: + """const char *TCOD_struct_get_name(const TCOD_ParserStruct *def)""" + + @staticmethod + def TCOD_struct_get_type(def_: Any, propname: Any, /) -> Any: + """TCOD_value_type_t TCOD_struct_get_type(const TCOD_ParserStruct *def, const char *propname)""" + + @staticmethod + def TCOD_struct_is_mandatory(def_: Any, propname: Any, /) -> bool: + """bool TCOD_struct_is_mandatory(TCOD_ParserStruct *def, const char *propname)""" + + @staticmethod + def TCOD_sys_accumulate_console(console: Any, /) -> int: + """int TCOD_sys_accumulate_console(const TCOD_Console *console)""" + + @staticmethod + def TCOD_sys_accumulate_console_(console: Any, viewport: Any, /) -> int: + """int TCOD_sys_accumulate_console_(const TCOD_Console *console, const struct SDL_Rect *viewport)""" + + @staticmethod + def TCOD_sys_check_for_event(eventMask: int, key: Any, mouse: Any, /) -> Any: + """TCOD_event_t TCOD_sys_check_for_event(int eventMask, TCOD_key_t *key, TCOD_mouse_t *mouse)""" + + @staticmethod + def TCOD_sys_check_for_keypress(flags: int, /) -> Any: + """TCOD_key_t TCOD_sys_check_for_keypress(int flags)""" + + @staticmethod + def TCOD_sys_check_magic_number(filename: Any, size: int, data: Any, /) -> bool: + """bool TCOD_sys_check_magic_number(const char *filename, size_t size, uint8_t *data)""" + + @staticmethod + def TCOD_sys_clipboard_get() -> Any: + """char *TCOD_sys_clipboard_get(void)""" + + @staticmethod + def TCOD_sys_clipboard_set(value: Any, /) -> bool: + """bool TCOD_sys_clipboard_set(const char *value)""" + + @staticmethod + def TCOD_sys_create_directory(path: Any, /) -> bool: + """bool TCOD_sys_create_directory(const char *path)""" + + @staticmethod + def TCOD_sys_decode_font_() -> None: + """void TCOD_sys_decode_font_(void)""" + + @staticmethod + def TCOD_sys_delete_directory(path: Any, /) -> bool: + """bool TCOD_sys_delete_directory(const char *path)""" + + @staticmethod + def TCOD_sys_delete_file(path: Any, /) -> bool: + """bool TCOD_sys_delete_file(const char *path)""" + + @staticmethod + def TCOD_sys_elapsed_milli() -> Any: + """uint32_t TCOD_sys_elapsed_milli(void)""" + + @staticmethod + def TCOD_sys_elapsed_seconds() -> float: + """float TCOD_sys_elapsed_seconds(void)""" + + @staticmethod + def TCOD_sys_file_exists(filename: Any, /, *__args: Any) -> bool: + """bool TCOD_sys_file_exists(const char *filename, ...)""" + + @staticmethod + def TCOD_sys_force_fullscreen_resolution(width: int, height: int, /) -> None: + """void TCOD_sys_force_fullscreen_resolution(int width, int height)""" + + @staticmethod + def TCOD_sys_get_SDL_renderer() -> Any: + """struct SDL_Renderer *TCOD_sys_get_SDL_renderer(void)""" + + @staticmethod + def TCOD_sys_get_SDL_window() -> Any: + """struct SDL_Window *TCOD_sys_get_SDL_window(void)""" + + @staticmethod + def TCOD_sys_get_char_size(w: Any, h: Any, /) -> None: + """void TCOD_sys_get_char_size(int *w, int *h)""" + + @staticmethod + def TCOD_sys_get_current_resolution(w: Any, h: Any, /) -> Any: + """TCOD_Error TCOD_sys_get_current_resolution(int *w, int *h)""" + + @staticmethod + def TCOD_sys_get_current_resolution_x() -> int: + """int TCOD_sys_get_current_resolution_x(void)""" + + @staticmethod + def TCOD_sys_get_current_resolution_y() -> int: + """int TCOD_sys_get_current_resolution_y(void)""" + + @staticmethod + def TCOD_sys_get_directory_content(path: Any, pattern: Any, /) -> Any: + """TCOD_list_t TCOD_sys_get_directory_content(const char *path, const char *pattern)""" + + @staticmethod + def TCOD_sys_get_fps() -> int: + """int TCOD_sys_get_fps(void)""" + + @staticmethod + def TCOD_sys_get_fullscreen_offsets(offset_x: Any, offset_y: Any, /) -> None: + """void TCOD_sys_get_fullscreen_offsets(int *offset_x, int *offset_y)""" + + @staticmethod + def TCOD_sys_get_image_alpha(image: Any, x: int, y: int, /) -> int: + """int TCOD_sys_get_image_alpha(const struct SDL_Surface *image, int x, int y)""" + + @staticmethod + def TCOD_sys_get_image_pixel(image: Any, x: int, y: int, /) -> Any: + """TCOD_color_t TCOD_sys_get_image_pixel(const struct SDL_Surface *image, int x, int y)""" + + @staticmethod + def TCOD_sys_get_image_size(image: Any, w: Any, h: Any, /) -> None: + """void TCOD_sys_get_image_size(const struct SDL_Surface *image, int *w, int *h)""" + + @staticmethod + def TCOD_sys_get_internal_console() -> Any: + """TCOD_Console *TCOD_sys_get_internal_console(void)""" + + @staticmethod + def TCOD_sys_get_internal_context() -> Any: + """TCOD_Context *TCOD_sys_get_internal_context(void)""" + + @staticmethod + def TCOD_sys_get_last_frame_length() -> float: + """float TCOD_sys_get_last_frame_length(void)""" + + @staticmethod + def TCOD_sys_get_num_cores() -> int: + """int TCOD_sys_get_num_cores(void)""" + + @staticmethod + def TCOD_sys_get_renderer() -> Any: + """TCOD_renderer_t TCOD_sys_get_renderer(void)""" + + @staticmethod + def TCOD_sys_get_sdl_renderer() -> Any: + """struct SDL_Renderer *TCOD_sys_get_sdl_renderer(void)""" + + @staticmethod + def TCOD_sys_get_sdl_window() -> Any: + """struct SDL_Window *TCOD_sys_get_sdl_window(void)""" + + @staticmethod + def TCOD_sys_handle_key_event(ev: Any, key: Any, /) -> Any: + """TCOD_event_t TCOD_sys_handle_key_event(const union SDL_Event *ev, TCOD_key_t *key)""" + + @staticmethod + def TCOD_sys_handle_mouse_event(ev: Any, mouse: Any, /) -> Any: + """TCOD_event_t TCOD_sys_handle_mouse_event(const union SDL_Event *ev, TCOD_mouse_t *mouse)""" + + @staticmethod + def TCOD_sys_is_directory(path: Any, /) -> bool: + """bool TCOD_sys_is_directory(const char *path)""" + + @staticmethod + def TCOD_sys_is_key_pressed(key: Any, /) -> bool: + """bool TCOD_sys_is_key_pressed(TCOD_keycode_t key)""" + + @staticmethod + def TCOD_sys_load_image(filename: Any, /) -> Any: + """struct SDL_Surface *TCOD_sys_load_image(const char *filename)""" + + @staticmethod + def TCOD_sys_load_player_config() -> Any: + """TCOD_Error TCOD_sys_load_player_config(void)""" + + @staticmethod + def TCOD_sys_map_ascii_to_font(asciiCode: int, fontCharX: int, fontCharY: int, /) -> None: + """void TCOD_sys_map_ascii_to_font(int asciiCode, int fontCharX, int fontCharY)""" + + @staticmethod + def TCOD_sys_pixel_to_tile(x: Any, y: Any, /) -> None: + """void TCOD_sys_pixel_to_tile(double *x, double *y)""" + + @staticmethod + def TCOD_sys_read_file(filename: Any, buf: Any, size: Any, /) -> bool: + """bool TCOD_sys_read_file(const char *filename, unsigned char **buf, size_t *size)""" + + @staticmethod + def TCOD_sys_register_SDL_renderer(renderer: Any, /) -> None: + """void TCOD_sys_register_SDL_renderer(SDL_renderer_t renderer)""" + + @staticmethod + def TCOD_sys_restore_fps() -> None: + """void TCOD_sys_restore_fps(void)""" + + @staticmethod + def TCOD_sys_save_bitmap(bitmap: Any, filename: Any, /) -> Any: + """TCOD_Error TCOD_sys_save_bitmap(struct SDL_Surface *bitmap, const char *filename)""" + + @staticmethod + def TCOD_sys_save_fps() -> None: + """void TCOD_sys_save_fps(void)""" + + @staticmethod + def TCOD_sys_save_screenshot(filename: Any, /) -> None: + """void TCOD_sys_save_screenshot(const char *filename)""" + + @staticmethod + def TCOD_sys_set_fps(val: int, /) -> None: + """void TCOD_sys_set_fps(int val)""" + + @staticmethod + def TCOD_sys_set_renderer(renderer: Any, /) -> int: + """int TCOD_sys_set_renderer(TCOD_renderer_t renderer)""" + + @staticmethod + def TCOD_sys_shutdown() -> None: + """void TCOD_sys_shutdown(void)""" + + @staticmethod + def TCOD_sys_sleep_milli(val: Any, /) -> None: + """void TCOD_sys_sleep_milli(uint32_t val)""" + + @staticmethod + def TCOD_sys_startup() -> None: + """void TCOD_sys_startup(void)""" + + @staticmethod + def TCOD_sys_update_char(asciiCode: int, font_x: int, font_y: int, img: Any, x: int, y: int, /) -> None: + """void TCOD_sys_update_char(int asciiCode, int font_x, int font_y, const TCOD_Image *img, int x, int y)""" + + @staticmethod + def TCOD_sys_wait_for_event(eventMask: int, key: Any, mouse: Any, flush: bool, /) -> Any: + """TCOD_event_t TCOD_sys_wait_for_event(int eventMask, TCOD_key_t *key, TCOD_mouse_t *mouse, bool flush)""" + + @staticmethod + def TCOD_sys_wait_for_keypress(flush: bool, /) -> Any: + """TCOD_key_t TCOD_sys_wait_for_keypress(bool flush)""" + + @staticmethod + def TCOD_sys_write_file(filename: Any, buf: Any, size: Any, /) -> bool: + """bool TCOD_sys_write_file(const char *filename, unsigned char *buf, uint32_t size)""" + + @staticmethod + def TCOD_text_delete(txt: Any, /) -> None: + """void TCOD_text_delete(TCOD_text_t txt)""" + + @staticmethod + def TCOD_text_get(txt: Any, /) -> Any: + """const char *TCOD_text_get(TCOD_text_t txt)""" + + @staticmethod + def TCOD_text_init(x: int, y: int, w: int, h: int, max_chars: int, /) -> Any: + """TCOD_text_t TCOD_text_init(int x, int y, int w, int h, int max_chars)""" + + @staticmethod + def TCOD_text_init2(w: int, h: int, max_chars: int, /) -> Any: + """TCOD_text_t TCOD_text_init2(int w, int h, int max_chars)""" + + @staticmethod + def TCOD_text_render(txt: Any, con: Any, /) -> None: + """void TCOD_text_render(TCOD_text_t txt, TCOD_console_t con)""" + + @staticmethod + def TCOD_text_reset(txt: Any, /) -> None: + """void TCOD_text_reset(TCOD_text_t txt)""" + + @staticmethod + def TCOD_text_set_colors(txt: Any, fore: Any, back: Any, back_transparency: float, /) -> None: + """void TCOD_text_set_colors(TCOD_text_t txt, TCOD_color_t fore, TCOD_color_t back, float back_transparency)""" + + @staticmethod + def TCOD_text_set_pos(txt: Any, x: int, y: int, /) -> None: + """void TCOD_text_set_pos(TCOD_text_t txt, int x, int y)""" + + @staticmethod + def TCOD_text_set_properties( + txt: Any, cursor_char: int, blink_interval: int, prompt: Any, tab_size: int, / + ) -> None: + """void TCOD_text_set_properties(TCOD_text_t txt, int cursor_char, int blink_interval, const char *prompt, int tab_size)""" + + @staticmethod + def TCOD_text_update(txt: Any, key: Any, /) -> bool: + """bool TCOD_text_update(TCOD_text_t txt, TCOD_key_t key)""" + + @staticmethod + def TCOD_thread_delete(th: Any, /) -> None: + """void TCOD_thread_delete(TCOD_thread_t th)""" + + @staticmethod + def TCOD_thread_new(func: Any, data: Any, /) -> Any: + """TCOD_thread_t TCOD_thread_new(int (*func)(void *), void *data)""" + + @staticmethod + def TCOD_thread_wait(th: Any, /) -> None: + """void TCOD_thread_wait(TCOD_thread_t th)""" + + @staticmethod + def TCOD_tileset_assign_tile(tileset: Any, tile_id: int, codepoint: int, /) -> int: + """int TCOD_tileset_assign_tile(struct TCOD_Tileset *tileset, int tile_id, int codepoint)""" + + @staticmethod + def TCOD_tileset_delete(tileset: Any, /) -> None: + """void TCOD_tileset_delete(TCOD_Tileset *tileset)""" + + @staticmethod + def TCOD_tileset_get_tile(tileset: Any, codepoint: int, /) -> Any: + """const struct TCOD_ColorRGBA *TCOD_tileset_get_tile(const TCOD_Tileset *tileset, int codepoint)""" + + @staticmethod + def TCOD_tileset_get_tile_(tileset: Any, codepoint: int, buffer: Any, /) -> Any: + """TCOD_Error TCOD_tileset_get_tile_(const TCOD_Tileset *tileset, int codepoint, struct TCOD_ColorRGBA *buffer)""" + + @staticmethod + def TCOD_tileset_get_tile_height_(tileset: Any, /) -> int: + """int TCOD_tileset_get_tile_height_(const TCOD_Tileset *tileset)""" + + @staticmethod + def TCOD_tileset_get_tile_width_(tileset: Any, /) -> int: + """int TCOD_tileset_get_tile_width_(const TCOD_Tileset *tileset)""" + + @staticmethod + def TCOD_tileset_load(filename: Any, columns: int, rows: int, n: int, charmap: Any, /) -> Any: + """TCOD_Tileset *TCOD_tileset_load(const char *filename, int columns, int rows, int n, const int *charmap)""" + + @staticmethod + def TCOD_tileset_load_fallback_font_(tile_width: int, tile_height: int, /) -> Any: + """TCOD_Tileset *TCOD_tileset_load_fallback_font_(int tile_width, int tile_height)""" + + @staticmethod + def TCOD_tileset_load_mem(buffer_length: int, buffer: Any, columns: int, rows: int, n: int, charmap: Any, /) -> Any: + """TCOD_Tileset *TCOD_tileset_load_mem(size_t buffer_length, const unsigned char *buffer, int columns, int rows, int n, const int *charmap)""" + + @staticmethod + def TCOD_tileset_load_raw( + width: int, height: int, pixels: Any, columns: int, rows: int, n: int, charmap: Any, / + ) -> Any: + """TCOD_Tileset *TCOD_tileset_load_raw(int width, int height, const struct TCOD_ColorRGBA *pixels, int columns, int rows, int n, const int *charmap)""" + + @staticmethod + def TCOD_tileset_load_truetype_(path: Any, tile_width: int, tile_height: int, /) -> Any: + """TCOD_Error TCOD_tileset_load_truetype_(const char *path, int tile_width, int tile_height)""" + + @staticmethod + def TCOD_tileset_new(tile_width: int, tile_height: int, /) -> Any: + """TCOD_Tileset *TCOD_tileset_new(int tile_width, int tile_height)""" + + @staticmethod + def TCOD_tileset_notify_tile_changed(tileset: Any, tile_id: int, /) -> None: + """void TCOD_tileset_notify_tile_changed(TCOD_Tileset *tileset, int tile_id)""" + + @staticmethod + def TCOD_tileset_observer_delete(observer: Any, /) -> None: + """void TCOD_tileset_observer_delete(struct TCOD_TilesetObserver *observer)""" + + @staticmethod + def TCOD_tileset_observer_new(tileset: Any, /) -> Any: + """struct TCOD_TilesetObserver *TCOD_tileset_observer_new(struct TCOD_Tileset *tileset)""" + + @staticmethod + def TCOD_tileset_render_to_surface(tileset: Any, console: Any, cache: Any, surface_out: Any, /) -> Any: + """TCOD_Error TCOD_tileset_render_to_surface(const TCOD_Tileset *tileset, const TCOD_Console *console, TCOD_Console **cache, struct SDL_Surface **surface_out)""" + + @staticmethod + def TCOD_tileset_reserve(tileset: Any, desired: int, /) -> Any: + """TCOD_Error TCOD_tileset_reserve(TCOD_Tileset *tileset, int desired)""" + + @staticmethod + def TCOD_tileset_set_tile_(tileset: Any, codepoint: int, buffer: Any, /) -> Any: + """TCOD_Error TCOD_tileset_set_tile_(TCOD_Tileset *tileset, int codepoint, const struct TCOD_ColorRGBA *buffer)""" + + @staticmethod + def TCOD_tree_add_son(node: Any, son: Any, /) -> None: + """void TCOD_tree_add_son(TCOD_tree_t *node, TCOD_tree_t *son)""" + + @staticmethod + def TCOD_tree_new() -> Any: + """TCOD_tree_t *TCOD_tree_new(void)""" + + @staticmethod + def TCOD_viewport_delete(viewport: Any, /) -> None: + """void TCOD_viewport_delete(TCOD_ViewportOptions *viewport)""" + + @staticmethod + def TCOD_viewport_new() -> Any: + """TCOD_ViewportOptions *TCOD_viewport_new(void)""" + + @staticmethod + def TCOD_zip_delete(zip: Any, /) -> None: + """void TCOD_zip_delete(TCOD_zip_t zip)""" + + @staticmethod + def TCOD_zip_get_char(zip: Any, /) -> Any: + """char TCOD_zip_get_char(TCOD_zip_t zip)""" + + @staticmethod + def TCOD_zip_get_color(zip: Any, /) -> Any: + """TCOD_color_t TCOD_zip_get_color(TCOD_zip_t zip)""" + + @staticmethod + def TCOD_zip_get_console(zip: Any, /) -> Any: + """TCOD_console_t TCOD_zip_get_console(TCOD_zip_t zip)""" + + @staticmethod + def TCOD_zip_get_current_bytes(zip: Any, /) -> Any: + """uint32_t TCOD_zip_get_current_bytes(TCOD_zip_t zip)""" + + @staticmethod + def TCOD_zip_get_data(zip: Any, nbBytes: int, data: Any, /) -> int: + """int TCOD_zip_get_data(TCOD_zip_t zip, int nbBytes, void *data)""" + + @staticmethod + def TCOD_zip_get_float(zip: Any, /) -> float: + """float TCOD_zip_get_float(TCOD_zip_t zip)""" + + @staticmethod + def TCOD_zip_get_image(zip: Any, /) -> Any: + """TCOD_Image *TCOD_zip_get_image(TCOD_zip_t zip)""" + + @staticmethod + def TCOD_zip_get_int(zip: Any, /) -> int: + """int TCOD_zip_get_int(TCOD_zip_t zip)""" + + @staticmethod + def TCOD_zip_get_random(zip: Any, /) -> Any: + """TCOD_Random *TCOD_zip_get_random(TCOD_zip_t zip)""" + + @staticmethod + def TCOD_zip_get_remaining_bytes(zip: Any, /) -> Any: + """uint32_t TCOD_zip_get_remaining_bytes(TCOD_zip_t zip)""" + + @staticmethod + def TCOD_zip_get_string(zip: Any, /) -> Any: + """const char *TCOD_zip_get_string(TCOD_zip_t zip)""" + + @staticmethod + def TCOD_zip_load_from_file(zip: Any, filename: Any, /) -> int: + """int TCOD_zip_load_from_file(TCOD_zip_t zip, const char *filename)""" + + @staticmethod + def TCOD_zip_new() -> Any: + """TCOD_zip_t TCOD_zip_new(void)""" + + @staticmethod + def TCOD_zip_put_char(zip: Any, val: Any, /) -> None: + """void TCOD_zip_put_char(TCOD_zip_t zip, char val)""" + + @staticmethod + def TCOD_zip_put_color(zip: Any, val: Any, /) -> None: + """void TCOD_zip_put_color(TCOD_zip_t zip, const TCOD_color_t val)""" + + @staticmethod + def TCOD_zip_put_console(zip: Any, val: Any, /) -> None: + """void TCOD_zip_put_console(TCOD_zip_t zip, const TCOD_Console *val)""" + + @staticmethod + def TCOD_zip_put_data(zip: Any, nbBytes: int, data: Any, /) -> None: + """void TCOD_zip_put_data(TCOD_zip_t zip, int nbBytes, const void *data)""" + + @staticmethod + def TCOD_zip_put_float(zip: Any, val: float, /) -> None: + """void TCOD_zip_put_float(TCOD_zip_t zip, float val)""" + + @staticmethod + def TCOD_zip_put_image(zip: Any, val: Any, /) -> None: + """void TCOD_zip_put_image(TCOD_zip_t zip, const TCOD_Image *val)""" + + @staticmethod + def TCOD_zip_put_int(zip: Any, val: int, /) -> None: + """void TCOD_zip_put_int(TCOD_zip_t zip, int val)""" + + @staticmethod + def TCOD_zip_put_random(zip: Any, val: Any, /) -> None: + """void TCOD_zip_put_random(TCOD_zip_t zip, const TCOD_Random *val)""" + + @staticmethod + def TCOD_zip_put_string(zip: Any, val: Any, /) -> None: + """void TCOD_zip_put_string(TCOD_zip_t zip, const char *val)""" + + @staticmethod + def TCOD_zip_save_to_file(zip: Any, filename: Any, /) -> int: + """int TCOD_zip_save_to_file(TCOD_zip_t zip, const char *filename)""" + + @staticmethod + def TCOD_zip_skip_bytes(zip: Any, nbBytes: Any, /) -> None: + """void TCOD_zip_skip_bytes(TCOD_zip_t zip, uint32_t nbBytes)""" + + @staticmethod + def _libtcod_log_watcher(message: Any, userdata: Any, /) -> None: + """void _libtcod_log_watcher(const TCOD_LogMessage *message, void *userdata)""" + + @staticmethod + def _pycall_bsp_callback(node: Any, userData: Any, /) -> bool: + """bool _pycall_bsp_callback(TCOD_bsp_t *node, void *userData)""" + + @staticmethod + def _pycall_cli_output(userdata: Any, output: Any, /) -> None: + """void _pycall_cli_output(void *userdata, const char *output)""" + + @staticmethod + def _pycall_parser_end_struct(str: Any, name: Any, /) -> bool: + """bool _pycall_parser_end_struct(TCOD_parser_struct_t str, const char *name)""" + + @staticmethod + def _pycall_parser_error(msg: Any, /) -> None: + """void _pycall_parser_error(const char *msg)""" + + @staticmethod + def _pycall_parser_new_flag(name: Any, /) -> bool: + """bool _pycall_parser_new_flag(const char *name)""" + + @staticmethod + def _pycall_parser_new_property(propname: Any, type: Any, value: Any, /) -> bool: + """bool _pycall_parser_new_property(const char *propname, TCOD_value_type_t type, TCOD_value_t value)""" + + @staticmethod + def _pycall_parser_new_struct(str: Any, name: Any, /) -> bool: + """bool _pycall_parser_new_struct(TCOD_parser_struct_t str, const char *name)""" + + @staticmethod + def _pycall_path_dest_only(x1: int, y1: int, x2: int, y2: int, user_data: Any, /) -> float: + """float _pycall_path_dest_only(int x1, int y1, int x2, int y2, void *user_data)""" + + @staticmethod + def _pycall_path_old(x: int, y: int, xDest: int, yDest: int, user_data: Any, /) -> float: + """float _pycall_path_old(int x, int y, int xDest, int yDest, void *user_data)""" + + @staticmethod + def _pycall_path_simple(x: int, y: int, xDest: int, yDest: int, user_data: Any, /) -> float: + """float _pycall_path_simple(int x, int y, int xDest, int yDest, void *user_data)""" + + @staticmethod + def _pycall_path_swap_src_dest(x1: int, y1: int, x2: int, y2: int, user_data: Any, /) -> float: + """float _pycall_path_swap_src_dest(int x1, int y1, int x2, int y2, void *user_data)""" + + @staticmethod + def _pycall_sdl_hook(arg0: Any, /) -> None: + """void _pycall_sdl_hook(struct SDL_Surface *)""" + + @staticmethod + def _sdl_audio_stream_callback(userdata: Any, stream: Any, additional_amount: int, total_amount: int, /) -> None: + """void _sdl_audio_stream_callback(void *userdata, SDL_AudioStream *stream, int additional_amount, int total_amount)""" + + @staticmethod + def _sdl_event_watcher(userdata: Any, event: Any, /) -> int: + """int _sdl_event_watcher(void *userdata, SDL_Event *event)""" + + @staticmethod + def _sdl_log_output_function(userdata: Any, category: int, priority: Any, message: Any, /) -> None: + """void _sdl_log_output_function(void *userdata, int category, SDL_LogPriority priority, const char *message)""" + + @staticmethod + def alloca(arg0: int, /) -> Any: + """void *alloca(size_t)""" + + @staticmethod + def bresenham(x1: int, y1: int, x2: int, y2: int, n: int, out: Any, /) -> int: + """int bresenham(int x1, int y1, int x2, int y2, int n, int *out)""" + + @staticmethod + def compute_heuristic(heuristic: Any, ndim: int, index: Any, /) -> int: + """int compute_heuristic(const struct PathfinderHeuristic *heuristic, int ndim, const int *index)""" + + @staticmethod + def dijkstra2d(dist: Any, cost: Any, edges_2d_n: int, edges_2d: Any, /) -> int: + """int dijkstra2d(struct NArray *dist, const struct NArray *cost, int edges_2d_n, const int *edges_2d)""" + + @staticmethod + def dijkstra2d_basic(dist: Any, cost: Any, cardinal: int, diagonal: int, /) -> int: + """int dijkstra2d_basic(struct NArray *dist, const struct NArray *cost, int cardinal, int diagonal)""" + + @staticmethod + def frontier_has_index(frontier: Any, index: Any, /) -> int: + """int frontier_has_index(const struct TCOD_Frontier *frontier, const int *index)""" + + @staticmethod + def get_travel_path(ndim: Any, travel_map: Any, start: Any, out: Any, /) -> int: + """ptrdiff_t get_travel_path(int8_t ndim, const NArray *travel_map, const int *start, int *out)""" + + @staticmethod + def hillclimb2d(dist_array: Any, start_i: int, start_j: int, edges_2d_n: int, edges_2d: Any, out: Any, /) -> int: + """int hillclimb2d(const struct NArray *dist_array, int start_i, int start_j, int edges_2d_n, const int *edges_2d, int *out)""" + + @staticmethod + def hillclimb2d_basic(dist: Any, x: int, y: int, cardinal: bool, diagonal: bool, out: Any, /) -> int: + """int hillclimb2d_basic(const struct NArray *dist, int x, int y, bool cardinal, bool diagonal, int *out)""" + + @staticmethod + def path_compute(frontier: Any, dist_map: Any, travel_map: Any, n: int, rules: Any, heuristic: Any, /) -> int: + """int path_compute(struct TCOD_Frontier *frontier, struct NArray *dist_map, struct NArray *travel_map, int n, const struct PathfinderRule *rules, const struct PathfinderHeuristic *heuristic)""" + + @staticmethod + def path_compute_step(frontier: Any, dist_map: Any, travel_map: Any, n: int, rules: Any, heuristic: Any, /) -> int: + """int path_compute_step(struct TCOD_Frontier *frontier, struct NArray *dist_map, struct NArray *travel_map, int n, const struct PathfinderRule *rules, const struct PathfinderHeuristic *heuristic)""" + + @staticmethod + def rebuild_frontier_from_distance(frontier: Any, dist_map: Any, /) -> int: + """int rebuild_frontier_from_distance(struct TCOD_Frontier *frontier, const NArray *dist_map)""" + + @staticmethod + def sync_time_() -> None: + """void sync_time_(void)""" + + @staticmethod + def update_frontier_heuristic(frontier: Any, heuristic: Any, /) -> int: + """int update_frontier_heuristic(struct TCOD_Frontier *frontier, const struct PathfinderHeuristic *heuristic)""" + + FOV_BASIC: Final[int] + FOV_DIAMOND: Final[int] + FOV_PERMISSIVE_0: Final[int] + FOV_PERMISSIVE_1: Final[int] + FOV_PERMISSIVE_2: Final[int] + FOV_PERMISSIVE_3: Final[int] + FOV_PERMISSIVE_4: Final[int] + FOV_PERMISSIVE_5: Final[int] + FOV_PERMISSIVE_6: Final[int] + FOV_PERMISSIVE_7: Final[int] + FOV_PERMISSIVE_8: Final[int] + FOV_RESTRICTIVE: Final[int] + FOV_SHADOW: Final[int] + FOV_SYMMETRIC_SHADOWCAST: Final[int] + NB_FOV_ALGORITHMS: Final[int] + SDLK_0: Final[int] + SDLK_1: Final[int] + SDLK_2: Final[int] + SDLK_3: Final[int] + SDLK_4: Final[int] + SDLK_5: Final[int] + SDLK_6: Final[int] + SDLK_7: Final[int] + SDLK_8: Final[int] + SDLK_9: Final[int] + SDLK_A: Final[int] + SDLK_AC_BACK: Final[int] + SDLK_AC_BOOKMARKS: Final[int] + SDLK_AC_CLOSE: Final[int] + SDLK_AC_EXIT: Final[int] + SDLK_AC_FORWARD: Final[int] + SDLK_AC_HOME: Final[int] + SDLK_AC_NEW: Final[int] + SDLK_AC_OPEN: Final[int] + SDLK_AC_PRINT: Final[int] + SDLK_AC_PROPERTIES: Final[int] + SDLK_AC_REFRESH: Final[int] + SDLK_AC_SAVE: Final[int] + SDLK_AC_SEARCH: Final[int] + SDLK_AC_STOP: Final[int] + SDLK_AGAIN: Final[int] + SDLK_ALTERASE: Final[int] + SDLK_AMPERSAND: Final[int] + SDLK_APOSTROPHE: Final[int] + SDLK_APPLICATION: Final[int] + SDLK_ASTERISK: Final[int] + SDLK_AT: Final[int] + SDLK_B: Final[int] + SDLK_BACKSLASH: Final[int] + SDLK_BACKSPACE: Final[int] + SDLK_C: Final[int] + SDLK_CALL: Final[int] + SDLK_CANCEL: Final[int] + SDLK_CAPSLOCK: Final[int] + SDLK_CARET: Final[int] + SDLK_CHANNEL_DECREMENT: Final[int] + SDLK_CHANNEL_INCREMENT: Final[int] + SDLK_CLEAR: Final[int] + SDLK_CLEARAGAIN: Final[int] + SDLK_COLON: Final[int] + SDLK_COMMA: Final[int] + SDLK_COPY: Final[int] + SDLK_CRSEL: Final[int] + SDLK_CURRENCYSUBUNIT: Final[int] + SDLK_CURRENCYUNIT: Final[int] + SDLK_CUT: Final[int] + SDLK_D: Final[int] + SDLK_DBLAPOSTROPHE: Final[int] + SDLK_DECIMALSEPARATOR: Final[int] + SDLK_DELETE: Final[int] + SDLK_DOLLAR: Final[int] + SDLK_DOWN: Final[int] + SDLK_E: Final[int] + SDLK_END: Final[int] + SDLK_ENDCALL: Final[int] + SDLK_EQUALS: Final[int] + SDLK_ESCAPE: Final[int] + SDLK_EXCLAIM: Final[int] + SDLK_EXECUTE: Final[int] + SDLK_EXSEL: Final[int] + SDLK_EXTENDED_MASK: Final[int] + SDLK_F10: Final[int] + SDLK_F11: Final[int] + SDLK_F12: Final[int] + SDLK_F13: Final[int] + SDLK_F14: Final[int] + SDLK_F15: Final[int] + SDLK_F16: Final[int] + SDLK_F17: Final[int] + SDLK_F18: Final[int] + SDLK_F19: Final[int] + SDLK_F1: Final[int] + SDLK_F20: Final[int] + SDLK_F21: Final[int] + SDLK_F22: Final[int] + SDLK_F23: Final[int] + SDLK_F24: Final[int] + SDLK_F2: Final[int] + SDLK_F3: Final[int] + SDLK_F4: Final[int] + SDLK_F5: Final[int] + SDLK_F6: Final[int] + SDLK_F7: Final[int] + SDLK_F8: Final[int] + SDLK_F9: Final[int] + SDLK_F: Final[int] + SDLK_FIND: Final[int] + SDLK_G: Final[int] + SDLK_GRAVE: Final[int] + SDLK_GREATER: Final[int] + SDLK_H: Final[int] + SDLK_HASH: Final[int] + SDLK_HELP: Final[int] + SDLK_HOME: Final[int] + SDLK_I: Final[int] + SDLK_INSERT: Final[int] + SDLK_J: Final[int] + SDLK_K: Final[int] + SDLK_KP_000: Final[int] + SDLK_KP_00: Final[int] + SDLK_KP_0: Final[int] + SDLK_KP_1: Final[int] + SDLK_KP_2: Final[int] + SDLK_KP_3: Final[int] + SDLK_KP_4: Final[int] + SDLK_KP_5: Final[int] + SDLK_KP_6: Final[int] + SDLK_KP_7: Final[int] + SDLK_KP_8: Final[int] + SDLK_KP_9: Final[int] + SDLK_KP_A: Final[int] + SDLK_KP_AMPERSAND: Final[int] + SDLK_KP_AT: Final[int] + SDLK_KP_B: Final[int] + SDLK_KP_BACKSPACE: Final[int] + SDLK_KP_BINARY: Final[int] + SDLK_KP_C: Final[int] + SDLK_KP_CLEAR: Final[int] + SDLK_KP_CLEARENTRY: Final[int] + SDLK_KP_COLON: Final[int] + SDLK_KP_COMMA: Final[int] + SDLK_KP_D: Final[int] + SDLK_KP_DBLAMPERSAND: Final[int] + SDLK_KP_DBLVERTICALBAR: Final[int] + SDLK_KP_DECIMAL: Final[int] + SDLK_KP_DIVIDE: Final[int] + SDLK_KP_E: Final[int] + SDLK_KP_ENTER: Final[int] + SDLK_KP_EQUALS: Final[int] + SDLK_KP_EQUALSAS400: Final[int] + SDLK_KP_EXCLAM: Final[int] + SDLK_KP_F: Final[int] + SDLK_KP_GREATER: Final[int] + SDLK_KP_HASH: Final[int] + SDLK_KP_HEXADECIMAL: Final[int] + SDLK_KP_LEFTBRACE: Final[int] + SDLK_KP_LEFTPAREN: Final[int] + SDLK_KP_LESS: Final[int] + SDLK_KP_MEMADD: Final[int] + SDLK_KP_MEMCLEAR: Final[int] + SDLK_KP_MEMDIVIDE: Final[int] + SDLK_KP_MEMMULTIPLY: Final[int] + SDLK_KP_MEMRECALL: Final[int] + SDLK_KP_MEMSTORE: Final[int] + SDLK_KP_MEMSUBTRACT: Final[int] + SDLK_KP_MINUS: Final[int] + SDLK_KP_MULTIPLY: Final[int] + SDLK_KP_OCTAL: Final[int] + SDLK_KP_PERCENT: Final[int] + SDLK_KP_PERIOD: Final[int] + SDLK_KP_PLUS: Final[int] + SDLK_KP_PLUSMINUS: Final[int] + SDLK_KP_POWER: Final[int] + SDLK_KP_RIGHTBRACE: Final[int] + SDLK_KP_RIGHTPAREN: Final[int] + SDLK_KP_SPACE: Final[int] + SDLK_KP_TAB: Final[int] + SDLK_KP_VERTICALBAR: Final[int] + SDLK_KP_XOR: Final[int] + SDLK_L: Final[int] + SDLK_LALT: Final[int] + SDLK_LCTRL: Final[int] + SDLK_LEFT: Final[int] + SDLK_LEFTBRACE: Final[int] + SDLK_LEFTBRACKET: Final[int] + SDLK_LEFTPAREN: Final[int] + SDLK_LEFT_TAB: Final[int] + SDLK_LESS: Final[int] + SDLK_LEVEL5_SHIFT: Final[int] + SDLK_LGUI: Final[int] + SDLK_LHYPER: Final[int] + SDLK_LMETA: Final[int] + SDLK_LSHIFT: Final[int] + SDLK_M: Final[int] + SDLK_MEDIA_EJECT: Final[int] + SDLK_MEDIA_FAST_FORWARD: Final[int] + SDLK_MEDIA_NEXT_TRACK: Final[int] + SDLK_MEDIA_PAUSE: Final[int] + SDLK_MEDIA_PLAY: Final[int] + SDLK_MEDIA_PLAY_PAUSE: Final[int] + SDLK_MEDIA_PREVIOUS_TRACK: Final[int] + SDLK_MEDIA_RECORD: Final[int] + SDLK_MEDIA_REWIND: Final[int] + SDLK_MEDIA_SELECT: Final[int] + SDLK_MEDIA_STOP: Final[int] + SDLK_MENU: Final[int] + SDLK_MINUS: Final[int] + SDLK_MODE: Final[int] + SDLK_MULTI_KEY_COMPOSE: Final[int] + SDLK_MUTE: Final[int] + SDLK_N: Final[int] + SDLK_NUMLOCKCLEAR: Final[int] + SDLK_O: Final[int] + SDLK_OPER: Final[int] + SDLK_OUT: Final[int] + SDLK_P: Final[int] + SDLK_PAGEDOWN: Final[int] + SDLK_PAGEUP: Final[int] + SDLK_PASTE: Final[int] + SDLK_PAUSE: Final[int] + SDLK_PERCENT: Final[int] + SDLK_PERIOD: Final[int] + SDLK_PIPE: Final[int] + SDLK_PLUS: Final[int] + SDLK_PLUSMINUS: Final[int] + SDLK_POWER: Final[int] + SDLK_PRINTSCREEN: Final[int] + SDLK_PRIOR: Final[int] + SDLK_Q: Final[int] + SDLK_QUESTION: Final[int] + SDLK_R: Final[int] + SDLK_RALT: Final[int] + SDLK_RCTRL: Final[int] + SDLK_RETURN2: Final[int] + SDLK_RETURN: Final[int] + SDLK_RGUI: Final[int] + SDLK_RHYPER: Final[int] + SDLK_RIGHT: Final[int] + SDLK_RIGHTBRACE: Final[int] + SDLK_RIGHTBRACKET: Final[int] + SDLK_RIGHTPAREN: Final[int] + SDLK_RMETA: Final[int] + SDLK_RSHIFT: Final[int] + SDLK_S: Final[int] + SDLK_SCANCODE_MASK: Final[int] + SDLK_SCROLLLOCK: Final[int] + SDLK_SELECT: Final[int] + SDLK_SEMICOLON: Final[int] + SDLK_SEPARATOR: Final[int] + SDLK_SLASH: Final[int] + SDLK_SLEEP: Final[int] + SDLK_SOFTLEFT: Final[int] + SDLK_SOFTRIGHT: Final[int] + SDLK_SPACE: Final[int] + SDLK_STOP: Final[int] + SDLK_SYSREQ: Final[int] + SDLK_T: Final[int] + SDLK_TAB: Final[int] + SDLK_THOUSANDSSEPARATOR: Final[int] + SDLK_TILDE: Final[int] + SDLK_U: Final[int] + SDLK_UNDERSCORE: Final[int] + SDLK_UNDO: Final[int] + SDLK_UNKNOWN: Final[int] + SDLK_UP: Final[int] + SDLK_V: Final[int] + SDLK_VOLUMEDOWN: Final[int] + SDLK_VOLUMEUP: Final[int] + SDLK_W: Final[int] + SDLK_WAKE: Final[int] + SDLK_X: Final[int] + SDLK_Y: Final[int] + SDLK_Z: Final[int] + SDL_ADDEVENT: Final[int] + SDL_ALPHA_OPAQUE: Final[int] + SDL_ALPHA_TRANSPARENT: Final[int] + SDL_APP_CONTINUE: Final[int] + SDL_APP_FAILURE: Final[int] + SDL_APP_SUCCESS: Final[int] + SDL_ARRAYORDER_ABGR: Final[int] + SDL_ARRAYORDER_ARGB: Final[int] + SDL_ARRAYORDER_BGR: Final[int] + SDL_ARRAYORDER_BGRA: Final[int] + SDL_ARRAYORDER_NONE: Final[int] + SDL_ARRAYORDER_RGB: Final[int] + SDL_ARRAYORDER_RGBA: Final[int] + SDL_ASSERTION_ABORT: Final[int] + SDL_ASSERTION_ALWAYS_IGNORE: Final[int] + SDL_ASSERTION_BREAK: Final[int] + SDL_ASSERTION_IGNORE: Final[int] + SDL_ASSERTION_RETRY: Final[int] + SDL_ASSERT_LEVEL: Final[int] + SDL_ASYNCIO_CANCELED: Final[int] + SDL_ASYNCIO_COMPLETE: Final[int] + SDL_ASYNCIO_FAILURE: Final[int] + SDL_ASYNCIO_TASK_CLOSE: Final[int] + SDL_ASYNCIO_TASK_READ: Final[int] + SDL_ASYNCIO_TASK_WRITE: Final[int] + SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK: Final[int] + SDL_AUDIO_DEVICE_DEFAULT_RECORDING: Final[int] + SDL_AUDIO_F32: Final[int] + SDL_AUDIO_F32BE: Final[Literal[37152]] = 37152 + SDL_AUDIO_F32LE: Final[Literal[33056]] = 33056 + SDL_AUDIO_MASK_BIG_ENDIAN: Final[int] + SDL_AUDIO_MASK_BITSIZE: Final[int] + SDL_AUDIO_MASK_FLOAT: Final[int] + SDL_AUDIO_MASK_SIGNED: Final[int] + SDL_AUDIO_S16: Final[int] + SDL_AUDIO_S16BE: Final[Literal[36880]] = 36880 + SDL_AUDIO_S16LE: Final[Literal[32784]] = 32784 + SDL_AUDIO_S32: Final[int] + SDL_AUDIO_S32BE: Final[Literal[36896]] = 36896 + SDL_AUDIO_S32LE: Final[Literal[32800]] = 32800 + SDL_AUDIO_S8: Final[Literal[32776]] = 32776 + SDL_AUDIO_U8: Final[Literal[8]] = 8 + SDL_AUDIO_UNKNOWN: Final[Literal[0]] = 0 + SDL_BIG_ENDIAN: Final[int] + SDL_BITMAPORDER_1234: Final[int] + SDL_BITMAPORDER_4321: Final[int] + SDL_BITMAPORDER_NONE: Final[int] + SDL_BLENDFACTOR_DST_ALPHA: Final[Literal[9]] = 9 + SDL_BLENDFACTOR_DST_COLOR: Final[Literal[7]] = 7 + SDL_BLENDFACTOR_ONE: Final[Literal[2]] = 2 + SDL_BLENDFACTOR_ONE_MINUS_DST_ALPHA: Final[Literal[10]] = 10 + SDL_BLENDFACTOR_ONE_MINUS_DST_COLOR: Final[Literal[8]] = 8 + SDL_BLENDFACTOR_ONE_MINUS_SRC_ALPHA: Final[Literal[6]] = 6 + SDL_BLENDFACTOR_ONE_MINUS_SRC_COLOR: Final[Literal[4]] = 4 + SDL_BLENDFACTOR_SRC_ALPHA: Final[Literal[5]] = 5 + SDL_BLENDFACTOR_SRC_COLOR: Final[Literal[3]] = 3 + SDL_BLENDFACTOR_ZERO: Final[Literal[1]] = 1 + SDL_BLENDMODE_ADD: Final[int] + SDL_BLENDMODE_ADD_PREMULTIPLIED: Final[int] + SDL_BLENDMODE_BLEND: Final[int] + SDL_BLENDMODE_BLEND_PREMULTIPLIED: Final[int] + SDL_BLENDMODE_INVALID: Final[int] + SDL_BLENDMODE_MOD: Final[int] + SDL_BLENDMODE_MUL: Final[int] + SDL_BLENDMODE_NONE: Final[int] + SDL_BLENDOPERATION_ADD: Final[Literal[1]] = 1 + SDL_BLENDOPERATION_MAXIMUM: Final[Literal[5]] = 5 + SDL_BLENDOPERATION_MINIMUM: Final[Literal[4]] = 4 + SDL_BLENDOPERATION_REV_SUBTRACT: Final[Literal[3]] = 3 + SDL_BLENDOPERATION_SUBTRACT: Final[Literal[2]] = 2 + SDL_BUTTON_LEFT: Final[int] + SDL_BUTTON_LMASK: Final[int] + SDL_BUTTON_MIDDLE: Final[int] + SDL_BUTTON_MMASK: Final[int] + SDL_BUTTON_RIGHT: Final[int] + SDL_BUTTON_RMASK: Final[int] + SDL_BUTTON_X1: Final[int] + SDL_BUTTON_X1MASK: Final[int] + SDL_BUTTON_X2: Final[int] + SDL_BUTTON_X2MASK: Final[int] + SDL_BYTEORDER: Final[int] + SDL_CACHELINE_SIZE: Final[int] + SDL_CAMERA_POSITION_BACK_FACING: Final[int] + SDL_CAMERA_POSITION_FRONT_FACING: Final[int] + SDL_CAMERA_POSITION_UNKNOWN: Final[int] + SDL_CAPITALIZE_LETTERS: Final[int] + SDL_CAPITALIZE_NONE: Final[int] + SDL_CAPITALIZE_SENTENCES: Final[int] + SDL_CAPITALIZE_WORDS: Final[int] + SDL_CHROMA_LOCATION_CENTER: Final[Literal[2]] = 2 + SDL_CHROMA_LOCATION_LEFT: Final[Literal[1]] = 1 + SDL_CHROMA_LOCATION_NONE: Final[Literal[0]] = 0 + SDL_CHROMA_LOCATION_TOPLEFT: Final[Literal[3]] = 3 + SDL_COLORSPACE_BT2020_FULL: Final[Literal[571483657]] = 571483657 + SDL_COLORSPACE_BT2020_LIMITED: Final[Literal[554706441]] = 554706441 + SDL_COLORSPACE_BT601_FULL: Final[Literal[571480262]] = 571480262 + SDL_COLORSPACE_BT601_LIMITED: Final[Literal[554703046]] = 554703046 + SDL_COLORSPACE_BT709_FULL: Final[Literal[571474977]] = 571474977 + SDL_COLORSPACE_BT709_LIMITED: Final[Literal[554697761]] = 554697761 + SDL_COLORSPACE_HDR10: Final[Literal[301999616]] = 301999616 + SDL_COLORSPACE_JPEG: Final[Literal[570426566]] = 570426566 + SDL_COLORSPACE_RGB_DEFAULT: Final[int] + SDL_COLORSPACE_SRGB: Final[Literal[301991328]] = 301991328 + SDL_COLORSPACE_SRGB_LINEAR: Final[Literal[301991168]] = 301991168 + SDL_COLORSPACE_UNKNOWN: Final[Literal[0]] = 0 + SDL_COLORSPACE_YUV_DEFAULT: Final[int] + SDL_COLOR_PRIMARIES_BT2020: Final[Literal[9]] = 9 + SDL_COLOR_PRIMARIES_BT470BG: Final[Literal[5]] = 5 + SDL_COLOR_PRIMARIES_BT470M: Final[Literal[4]] = 4 + SDL_COLOR_PRIMARIES_BT601: Final[Literal[6]] = 6 + SDL_COLOR_PRIMARIES_BT709: Final[Literal[1]] = 1 + SDL_COLOR_PRIMARIES_CUSTOM: Final[Literal[31]] = 31 + SDL_COLOR_PRIMARIES_EBU3213: Final[Literal[22]] = 22 + SDL_COLOR_PRIMARIES_GENERIC_FILM: Final[Literal[8]] = 8 + SDL_COLOR_PRIMARIES_SMPTE240: Final[Literal[7]] = 7 + SDL_COLOR_PRIMARIES_SMPTE431: Final[Literal[11]] = 11 + SDL_COLOR_PRIMARIES_SMPTE432: Final[Literal[12]] = 12 + SDL_COLOR_PRIMARIES_UNKNOWN: Final[Literal[0]] = 0 + SDL_COLOR_PRIMARIES_UNSPECIFIED: Final[Literal[2]] = 2 + SDL_COLOR_PRIMARIES_XYZ: Final[Literal[10]] = 10 + SDL_COLOR_RANGE_FULL: Final[Literal[2]] = 2 + SDL_COLOR_RANGE_LIMITED: Final[Literal[1]] = 1 + SDL_COLOR_RANGE_UNKNOWN: Final[Literal[0]] = 0 + SDL_COLOR_TYPE_RGB: Final[Literal[1]] = 1 + SDL_COLOR_TYPE_UNKNOWN: Final[Literal[0]] = 0 + SDL_COLOR_TYPE_YCBCR: Final[Literal[2]] = 2 + SDL_DATE_FORMAT_DDMMYYYY: Final[Literal[1]] = 1 + SDL_DATE_FORMAT_MMDDYYYY: Final[Literal[2]] = 2 + SDL_DATE_FORMAT_YYYYMMDD: Final[Literal[0]] = 0 + SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE: Final[int] + SDL_ENUM_CONTINUE: Final[int] + SDL_ENUM_FAILURE: Final[int] + SDL_ENUM_SUCCESS: Final[int] + SDL_EVENT_AUDIO_DEVICE_ADDED: Final[Literal[4352]] = 4352 + SDL_EVENT_AUDIO_DEVICE_FORMAT_CHANGED: Final[int] + SDL_EVENT_AUDIO_DEVICE_REMOVED: Final[int] + SDL_EVENT_CAMERA_DEVICE_ADDED: Final[Literal[5120]] = 5120 + SDL_EVENT_CAMERA_DEVICE_APPROVED: Final[int] + SDL_EVENT_CAMERA_DEVICE_DENIED: Final[int] + SDL_EVENT_CAMERA_DEVICE_REMOVED: Final[int] + SDL_EVENT_CLIPBOARD_UPDATE: Final[Literal[2304]] = 2304 + SDL_EVENT_DID_ENTER_BACKGROUND: Final[int] + SDL_EVENT_DID_ENTER_FOREGROUND: Final[int] + SDL_EVENT_DISPLAY_ADDED: Final[int] + SDL_EVENT_DISPLAY_CONTENT_SCALE_CHANGED: Final[int] + SDL_EVENT_DISPLAY_CURRENT_MODE_CHANGED: Final[int] + SDL_EVENT_DISPLAY_DESKTOP_MODE_CHANGED: Final[int] + SDL_EVENT_DISPLAY_FIRST: Final[int] + SDL_EVENT_DISPLAY_LAST: Final[int] + SDL_EVENT_DISPLAY_MOVED: Final[int] + SDL_EVENT_DISPLAY_ORIENTATION: Final[Literal[337]] = 337 + SDL_EVENT_DISPLAY_REMOVED: Final[int] + SDL_EVENT_DROP_BEGIN: Final[int] + SDL_EVENT_DROP_COMPLETE: Final[int] + SDL_EVENT_DROP_FILE: Final[Literal[4096]] = 4096 + SDL_EVENT_DROP_POSITION: Final[int] + SDL_EVENT_DROP_TEXT: Final[int] + SDL_EVENT_ENUM_PADDING: Final[Literal[2147483647]] = 2147483647 + SDL_EVENT_FINGER_CANCELED: Final[int] + SDL_EVENT_FINGER_DOWN: Final[Literal[1792]] = 1792 + SDL_EVENT_FINGER_MOTION: Final[int] + SDL_EVENT_FINGER_UP: Final[int] + SDL_EVENT_FIRST: Final[Literal[0]] = 0 + SDL_EVENT_GAMEPAD_ADDED: Final[int] + SDL_EVENT_GAMEPAD_AXIS_MOTION: Final[Literal[1616]] = 1616 + SDL_EVENT_GAMEPAD_BUTTON_DOWN: Final[int] + SDL_EVENT_GAMEPAD_BUTTON_UP: Final[int] + SDL_EVENT_GAMEPAD_REMAPPED: Final[int] + SDL_EVENT_GAMEPAD_REMOVED: Final[int] + SDL_EVENT_GAMEPAD_SENSOR_UPDATE: Final[int] + SDL_EVENT_GAMEPAD_STEAM_HANDLE_UPDATED: Final[int] + SDL_EVENT_GAMEPAD_TOUCHPAD_DOWN: Final[int] + SDL_EVENT_GAMEPAD_TOUCHPAD_MOTION: Final[int] + SDL_EVENT_GAMEPAD_TOUCHPAD_UP: Final[int] + SDL_EVENT_GAMEPAD_UPDATE_COMPLETE: Final[int] + SDL_EVENT_JOYSTICK_ADDED: Final[int] + SDL_EVENT_JOYSTICK_AXIS_MOTION: Final[Literal[1536]] = 1536 + SDL_EVENT_JOYSTICK_BALL_MOTION: Final[int] + SDL_EVENT_JOYSTICK_BATTERY_UPDATED: Final[int] + SDL_EVENT_JOYSTICK_BUTTON_DOWN: Final[int] + SDL_EVENT_JOYSTICK_BUTTON_UP: Final[int] + SDL_EVENT_JOYSTICK_HAT_MOTION: Final[int] + SDL_EVENT_JOYSTICK_REMOVED: Final[int] + SDL_EVENT_JOYSTICK_UPDATE_COMPLETE: Final[int] + SDL_EVENT_KEYBOARD_ADDED: Final[int] + SDL_EVENT_KEYBOARD_REMOVED: Final[int] + SDL_EVENT_KEYMAP_CHANGED: Final[int] + SDL_EVENT_KEY_DOWN: Final[Literal[768]] = 768 + SDL_EVENT_KEY_UP: Final[int] + SDL_EVENT_LAST: Final[Literal[65535]] = 65535 + SDL_EVENT_LOCALE_CHANGED: Final[int] + SDL_EVENT_LOW_MEMORY: Final[int] + SDL_EVENT_MOUSE_ADDED: Final[int] + SDL_EVENT_MOUSE_BUTTON_DOWN: Final[int] + SDL_EVENT_MOUSE_BUTTON_UP: Final[int] + SDL_EVENT_MOUSE_MOTION: Final[Literal[1024]] = 1024 + SDL_EVENT_MOUSE_REMOVED: Final[int] + SDL_EVENT_MOUSE_WHEEL: Final[int] + SDL_EVENT_PEN_AXIS: Final[int] + SDL_EVENT_PEN_BUTTON_DOWN: Final[int] + SDL_EVENT_PEN_BUTTON_UP: Final[int] + SDL_EVENT_PEN_DOWN: Final[int] + SDL_EVENT_PEN_MOTION: Final[int] + SDL_EVENT_PEN_PROXIMITY_IN: Final[Literal[4864]] = 4864 + SDL_EVENT_PEN_PROXIMITY_OUT: Final[int] + SDL_EVENT_PEN_UP: Final[int] + SDL_EVENT_POLL_SENTINEL: Final[Literal[32512]] = 32512 + SDL_EVENT_PRIVATE0: Final[Literal[16384]] = 16384 + SDL_EVENT_PRIVATE1: Final[int] + SDL_EVENT_PRIVATE2: Final[int] + SDL_EVENT_PRIVATE3: Final[int] + SDL_EVENT_QUIT: Final[Literal[256]] = 256 + SDL_EVENT_RENDER_DEVICE_LOST: Final[int] + SDL_EVENT_RENDER_DEVICE_RESET: Final[int] + SDL_EVENT_RENDER_TARGETS_RESET: Final[Literal[8192]] = 8192 + SDL_EVENT_SENSOR_UPDATE: Final[Literal[4608]] = 4608 + SDL_EVENT_SYSTEM_THEME_CHANGED: Final[int] + SDL_EVENT_TERMINATING: Final[int] + SDL_EVENT_TEXT_EDITING: Final[int] + SDL_EVENT_TEXT_EDITING_CANDIDATES: Final[int] + SDL_EVENT_TEXT_INPUT: Final[int] + SDL_EVENT_USER: Final[Literal[32768]] = 32768 + SDL_EVENT_WILL_ENTER_BACKGROUND: Final[int] + SDL_EVENT_WILL_ENTER_FOREGROUND: Final[int] + SDL_EVENT_WINDOW_CLOSE_REQUESTED: Final[int] + SDL_EVENT_WINDOW_DESTROYED: Final[int] + SDL_EVENT_WINDOW_DISPLAY_CHANGED: Final[int] + SDL_EVENT_WINDOW_DISPLAY_SCALE_CHANGED: Final[int] + SDL_EVENT_WINDOW_ENTER_FULLSCREEN: Final[int] + SDL_EVENT_WINDOW_EXPOSED: Final[int] + SDL_EVENT_WINDOW_FIRST: Final[int] + SDL_EVENT_WINDOW_FOCUS_GAINED: Final[int] + SDL_EVENT_WINDOW_FOCUS_LOST: Final[int] + SDL_EVENT_WINDOW_HDR_STATE_CHANGED: Final[int] + SDL_EVENT_WINDOW_HIDDEN: Final[int] + SDL_EVENT_WINDOW_HIT_TEST: Final[int] + SDL_EVENT_WINDOW_ICCPROF_CHANGED: Final[int] + SDL_EVENT_WINDOW_LAST: Final[int] + SDL_EVENT_WINDOW_LEAVE_FULLSCREEN: Final[int] + SDL_EVENT_WINDOW_MAXIMIZED: Final[int] + SDL_EVENT_WINDOW_METAL_VIEW_RESIZED: Final[int] + SDL_EVENT_WINDOW_MINIMIZED: Final[int] + SDL_EVENT_WINDOW_MOUSE_ENTER: Final[int] + SDL_EVENT_WINDOW_MOUSE_LEAVE: Final[int] + SDL_EVENT_WINDOW_MOVED: Final[int] + SDL_EVENT_WINDOW_OCCLUDED: Final[int] + SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED: Final[int] + SDL_EVENT_WINDOW_RESIZED: Final[int] + SDL_EVENT_WINDOW_RESTORED: Final[int] + SDL_EVENT_WINDOW_SAFE_AREA_CHANGED: Final[int] + SDL_EVENT_WINDOW_SHOWN: Final[Literal[514]] = 514 + SDL_FILEDIALOG_OPENFILE: Final[int] + SDL_FILEDIALOG_OPENFOLDER: Final[int] + SDL_FILEDIALOG_SAVEFILE: Final[int] + SDL_FLASH_BRIEFLY: Final[int] + SDL_FLASH_CANCEL: Final[int] + SDL_FLASH_UNTIL_FOCUSED: Final[int] + SDL_FLIP_HORIZONTAL: Final[int] + SDL_FLIP_NONE: Final[int] + SDL_FLIP_VERTICAL: Final[int] + SDL_FLOATWORDORDER: Final[int] + SDL_FOLDER_COUNT: Final[int] + SDL_FOLDER_DESKTOP: Final[int] + SDL_FOLDER_DOCUMENTS: Final[int] + SDL_FOLDER_DOWNLOADS: Final[int] + SDL_FOLDER_HOME: Final[int] + SDL_FOLDER_MUSIC: Final[int] + SDL_FOLDER_PICTURES: Final[int] + SDL_FOLDER_PUBLICSHARE: Final[int] + SDL_FOLDER_SAVEDGAMES: Final[int] + SDL_FOLDER_SCREENSHOTS: Final[int] + SDL_FOLDER_TEMPLATES: Final[int] + SDL_FOLDER_VIDEOS: Final[int] + SDL_GAMEPAD_AXIS_COUNT: Final[int] + SDL_GAMEPAD_AXIS_INVALID: Final[Literal[-1]] = -1 + SDL_GAMEPAD_AXIS_LEFTX: Final[int] + SDL_GAMEPAD_AXIS_LEFTY: Final[int] + SDL_GAMEPAD_AXIS_LEFT_TRIGGER: Final[int] + SDL_GAMEPAD_AXIS_RIGHTX: Final[int] + SDL_GAMEPAD_AXIS_RIGHTY: Final[int] + SDL_GAMEPAD_AXIS_RIGHT_TRIGGER: Final[int] + SDL_GAMEPAD_BINDTYPE_AXIS: Final[int] + SDL_GAMEPAD_BINDTYPE_BUTTON: Final[int] + SDL_GAMEPAD_BINDTYPE_HAT: Final[int] + SDL_GAMEPAD_BINDTYPE_NONE: Final[Literal[0]] = 0 + SDL_GAMEPAD_BUTTON_BACK: Final[int] + SDL_GAMEPAD_BUTTON_COUNT: Final[int] + SDL_GAMEPAD_BUTTON_DPAD_DOWN: Final[int] + SDL_GAMEPAD_BUTTON_DPAD_LEFT: Final[int] + SDL_GAMEPAD_BUTTON_DPAD_RIGHT: Final[int] + SDL_GAMEPAD_BUTTON_DPAD_UP: Final[int] + SDL_GAMEPAD_BUTTON_EAST: Final[int] + SDL_GAMEPAD_BUTTON_GUIDE: Final[int] + SDL_GAMEPAD_BUTTON_INVALID: Final[Literal[-1]] = -1 + SDL_GAMEPAD_BUTTON_LABEL_A: Final[int] + SDL_GAMEPAD_BUTTON_LABEL_B: Final[int] + SDL_GAMEPAD_BUTTON_LABEL_CIRCLE: Final[int] + SDL_GAMEPAD_BUTTON_LABEL_CROSS: Final[int] + SDL_GAMEPAD_BUTTON_LABEL_SQUARE: Final[int] + SDL_GAMEPAD_BUTTON_LABEL_TRIANGLE: Final[int] + SDL_GAMEPAD_BUTTON_LABEL_UNKNOWN: Final[int] + SDL_GAMEPAD_BUTTON_LABEL_X: Final[int] + SDL_GAMEPAD_BUTTON_LABEL_Y: Final[int] + SDL_GAMEPAD_BUTTON_LEFT_PADDLE1: Final[int] + SDL_GAMEPAD_BUTTON_LEFT_PADDLE2: Final[int] + SDL_GAMEPAD_BUTTON_LEFT_SHOULDER: Final[int] + SDL_GAMEPAD_BUTTON_LEFT_STICK: Final[int] + SDL_GAMEPAD_BUTTON_MISC1: Final[int] + SDL_GAMEPAD_BUTTON_MISC2: Final[int] + SDL_GAMEPAD_BUTTON_MISC3: Final[int] + SDL_GAMEPAD_BUTTON_MISC4: Final[int] + SDL_GAMEPAD_BUTTON_MISC5: Final[int] + SDL_GAMEPAD_BUTTON_MISC6: Final[int] + SDL_GAMEPAD_BUTTON_NORTH: Final[int] + SDL_GAMEPAD_BUTTON_RIGHT_PADDLE1: Final[int] + SDL_GAMEPAD_BUTTON_RIGHT_PADDLE2: Final[int] + SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER: Final[int] + SDL_GAMEPAD_BUTTON_RIGHT_STICK: Final[int] + SDL_GAMEPAD_BUTTON_SOUTH: Final[int] + SDL_GAMEPAD_BUTTON_START: Final[int] + SDL_GAMEPAD_BUTTON_TOUCHPAD: Final[int] + SDL_GAMEPAD_BUTTON_WEST: Final[int] + SDL_GAMEPAD_TYPE_COUNT: Final[int] + SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_LEFT: Final[int] + SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_PAIR: Final[int] + SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_JOYCON_RIGHT: Final[int] + SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_PRO: Final[int] + SDL_GAMEPAD_TYPE_PS3: Final[int] + SDL_GAMEPAD_TYPE_PS4: Final[int] + SDL_GAMEPAD_TYPE_PS5: Final[int] + SDL_GAMEPAD_TYPE_STANDARD: Final[int] + SDL_GAMEPAD_TYPE_UNKNOWN: Final[Literal[0]] = 0 + SDL_GAMEPAD_TYPE_XBOX360: Final[int] + SDL_GAMEPAD_TYPE_XBOXONE: Final[int] + SDL_GETEVENT: Final[int] + SDL_GLOB_CASEINSENSITIVE: Final[int] + SDL_GL_ACCELERATED_VISUAL: Final[int] + SDL_GL_ACCUM_ALPHA_SIZE: Final[int] + SDL_GL_ACCUM_BLUE_SIZE: Final[int] + SDL_GL_ACCUM_GREEN_SIZE: Final[int] + SDL_GL_ACCUM_RED_SIZE: Final[int] + SDL_GL_ALPHA_SIZE: Final[int] + SDL_GL_BLUE_SIZE: Final[int] + SDL_GL_BUFFER_SIZE: Final[int] + SDL_GL_CONTEXT_DEBUG_FLAG: Final[int] + SDL_GL_CONTEXT_FLAGS: Final[int] + SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG: Final[int] + SDL_GL_CONTEXT_MAJOR_VERSION: Final[int] + SDL_GL_CONTEXT_MINOR_VERSION: Final[int] + SDL_GL_CONTEXT_NO_ERROR: Final[int] + SDL_GL_CONTEXT_PROFILE_COMPATIBILITY: Final[int] + SDL_GL_CONTEXT_PROFILE_CORE: Final[int] + SDL_GL_CONTEXT_PROFILE_ES: Final[int] + SDL_GL_CONTEXT_PROFILE_MASK: Final[int] + SDL_GL_CONTEXT_RELEASE_BEHAVIOR: Final[int] + SDL_GL_CONTEXT_RELEASE_BEHAVIOR_FLUSH: Final[int] + SDL_GL_CONTEXT_RELEASE_BEHAVIOR_NONE: Final[int] + SDL_GL_CONTEXT_RESET_ISOLATION_FLAG: Final[int] + SDL_GL_CONTEXT_RESET_LOSE_CONTEXT: Final[int] + SDL_GL_CONTEXT_RESET_NOTIFICATION: Final[int] + SDL_GL_CONTEXT_RESET_NO_NOTIFICATION: Final[int] + SDL_GL_CONTEXT_ROBUST_ACCESS_FLAG: Final[int] + SDL_GL_DEPTH_SIZE: Final[int] + SDL_GL_DOUBLEBUFFER: Final[int] + SDL_GL_EGL_PLATFORM: Final[int] + SDL_GL_FLOATBUFFERS: Final[int] + SDL_GL_FRAMEBUFFER_SRGB_CAPABLE: Final[int] + SDL_GL_GREEN_SIZE: Final[int] + SDL_GL_MULTISAMPLEBUFFERS: Final[int] + SDL_GL_MULTISAMPLESAMPLES: Final[int] + SDL_GL_RED_SIZE: Final[int] + SDL_GL_RETAINED_BACKING: Final[int] + SDL_GL_SHARE_WITH_CURRENT_CONTEXT: Final[int] + SDL_GL_STENCIL_SIZE: Final[int] + SDL_GL_STEREO: Final[int] + SDL_GPU_BLENDFACTOR_CONSTANT_COLOR: Final[int] + SDL_GPU_BLENDFACTOR_DST_ALPHA: Final[int] + SDL_GPU_BLENDFACTOR_DST_COLOR: Final[int] + SDL_GPU_BLENDFACTOR_INVALID: Final[int] + SDL_GPU_BLENDFACTOR_ONE: Final[int] + SDL_GPU_BLENDFACTOR_ONE_MINUS_CONSTANT_COLOR: Final[int] + SDL_GPU_BLENDFACTOR_ONE_MINUS_DST_ALPHA: Final[int] + SDL_GPU_BLENDFACTOR_ONE_MINUS_DST_COLOR: Final[int] + SDL_GPU_BLENDFACTOR_ONE_MINUS_SRC_ALPHA: Final[int] + SDL_GPU_BLENDFACTOR_ONE_MINUS_SRC_COLOR: Final[int] + SDL_GPU_BLENDFACTOR_SRC_ALPHA: Final[int] + SDL_GPU_BLENDFACTOR_SRC_ALPHA_SATURATE: Final[int] + SDL_GPU_BLENDFACTOR_SRC_COLOR: Final[int] + SDL_GPU_BLENDFACTOR_ZERO: Final[int] + SDL_GPU_BLENDOP_ADD: Final[int] + SDL_GPU_BLENDOP_INVALID: Final[int] + SDL_GPU_BLENDOP_MAX: Final[int] + SDL_GPU_BLENDOP_MIN: Final[int] + SDL_GPU_BLENDOP_REVERSE_SUBTRACT: Final[int] + SDL_GPU_BLENDOP_SUBTRACT: Final[int] + SDL_GPU_BUFFERUSAGE_COMPUTE_STORAGE_READ: Final[int] + SDL_GPU_BUFFERUSAGE_COMPUTE_STORAGE_WRITE: Final[int] + SDL_GPU_BUFFERUSAGE_GRAPHICS_STORAGE_READ: Final[int] + SDL_GPU_BUFFERUSAGE_INDEX: Final[int] + SDL_GPU_BUFFERUSAGE_INDIRECT: Final[int] + SDL_GPU_BUFFERUSAGE_VERTEX: Final[int] + SDL_GPU_COLORCOMPONENT_A: Final[int] + SDL_GPU_COLORCOMPONENT_B: Final[int] + SDL_GPU_COLORCOMPONENT_G: Final[int] + SDL_GPU_COLORCOMPONENT_R: Final[int] + SDL_GPU_COMPAREOP_ALWAYS: Final[int] + SDL_GPU_COMPAREOP_EQUAL: Final[int] + SDL_GPU_COMPAREOP_GREATER: Final[int] + SDL_GPU_COMPAREOP_GREATER_OR_EQUAL: Final[int] + SDL_GPU_COMPAREOP_INVALID: Final[int] + SDL_GPU_COMPAREOP_LESS: Final[int] + SDL_GPU_COMPAREOP_LESS_OR_EQUAL: Final[int] + SDL_GPU_COMPAREOP_NEVER: Final[int] + SDL_GPU_COMPAREOP_NOT_EQUAL: Final[int] + SDL_GPU_CUBEMAPFACE_NEGATIVEX: Final[int] + SDL_GPU_CUBEMAPFACE_NEGATIVEY: Final[int] + SDL_GPU_CUBEMAPFACE_NEGATIVEZ: Final[int] + SDL_GPU_CUBEMAPFACE_POSITIVEX: Final[int] + SDL_GPU_CUBEMAPFACE_POSITIVEY: Final[int] + SDL_GPU_CUBEMAPFACE_POSITIVEZ: Final[int] + SDL_GPU_CULLMODE_BACK: Final[int] + SDL_GPU_CULLMODE_FRONT: Final[int] + SDL_GPU_CULLMODE_NONE: Final[int] + SDL_GPU_FILLMODE_FILL: Final[int] + SDL_GPU_FILLMODE_LINE: Final[int] + SDL_GPU_FILTER_LINEAR: Final[int] + SDL_GPU_FILTER_NEAREST: Final[int] + SDL_GPU_FRONTFACE_CLOCKWISE: Final[int] + SDL_GPU_FRONTFACE_COUNTER_CLOCKWISE: Final[int] + SDL_GPU_INDEXELEMENTSIZE_16BIT: Final[int] + SDL_GPU_INDEXELEMENTSIZE_32BIT: Final[int] + SDL_GPU_LOADOP_CLEAR: Final[int] + SDL_GPU_LOADOP_DONT_CARE: Final[int] + SDL_GPU_LOADOP_LOAD: Final[int] + SDL_GPU_PRESENTMODE_IMMEDIATE: Final[int] + SDL_GPU_PRESENTMODE_MAILBOX: Final[int] + SDL_GPU_PRESENTMODE_VSYNC: Final[int] + SDL_GPU_PRIMITIVETYPE_LINELIST: Final[int] + SDL_GPU_PRIMITIVETYPE_LINESTRIP: Final[int] + SDL_GPU_PRIMITIVETYPE_POINTLIST: Final[int] + SDL_GPU_PRIMITIVETYPE_TRIANGLELIST: Final[int] + SDL_GPU_PRIMITIVETYPE_TRIANGLESTRIP: Final[int] + SDL_GPU_SAMPLECOUNT_1: Final[int] + SDL_GPU_SAMPLECOUNT_2: Final[int] + SDL_GPU_SAMPLECOUNT_4: Final[int] + SDL_GPU_SAMPLECOUNT_8: Final[int] + SDL_GPU_SAMPLERADDRESSMODE_CLAMP_TO_EDGE: Final[int] + SDL_GPU_SAMPLERADDRESSMODE_MIRRORED_REPEAT: Final[int] + SDL_GPU_SAMPLERADDRESSMODE_REPEAT: Final[int] + SDL_GPU_SAMPLERMIPMAPMODE_LINEAR: Final[int] + SDL_GPU_SAMPLERMIPMAPMODE_NEAREST: Final[int] + SDL_GPU_SHADERFORMAT_DXBC: Final[int] + SDL_GPU_SHADERFORMAT_DXIL: Final[int] + SDL_GPU_SHADERFORMAT_INVALID: Final[int] + SDL_GPU_SHADERFORMAT_METALLIB: Final[int] + SDL_GPU_SHADERFORMAT_MSL: Final[int] + SDL_GPU_SHADERFORMAT_PRIVATE: Final[int] + SDL_GPU_SHADERFORMAT_SPIRV: Final[int] + SDL_GPU_SHADERSTAGE_FRAGMENT: Final[int] + SDL_GPU_SHADERSTAGE_VERTEX: Final[int] + SDL_GPU_STENCILOP_DECREMENT_AND_CLAMP: Final[int] + SDL_GPU_STENCILOP_DECREMENT_AND_WRAP: Final[int] + SDL_GPU_STENCILOP_INCREMENT_AND_CLAMP: Final[int] + SDL_GPU_STENCILOP_INCREMENT_AND_WRAP: Final[int] + SDL_GPU_STENCILOP_INVALID: Final[int] + SDL_GPU_STENCILOP_INVERT: Final[int] + SDL_GPU_STENCILOP_KEEP: Final[int] + SDL_GPU_STENCILOP_REPLACE: Final[int] + SDL_GPU_STENCILOP_ZERO: Final[int] + SDL_GPU_STOREOP_DONT_CARE: Final[int] + SDL_GPU_STOREOP_RESOLVE: Final[int] + SDL_GPU_STOREOP_RESOLVE_AND_STORE: Final[int] + SDL_GPU_STOREOP_STORE: Final[int] + SDL_GPU_SWAPCHAINCOMPOSITION_HDR10_ST2084: Final[int] + SDL_GPU_SWAPCHAINCOMPOSITION_HDR_EXTENDED_LINEAR: Final[int] + SDL_GPU_SWAPCHAINCOMPOSITION_SDR: Final[int] + SDL_GPU_SWAPCHAINCOMPOSITION_SDR_LINEAR: Final[int] + SDL_GPU_TEXTUREFORMAT_A8_UNORM: Final[int] + SDL_GPU_TEXTUREFORMAT_ASTC_10x10_FLOAT: Final[int] + SDL_GPU_TEXTUREFORMAT_ASTC_10x10_UNORM: Final[int] + SDL_GPU_TEXTUREFORMAT_ASTC_10x10_UNORM_SRGB: Final[int] + SDL_GPU_TEXTUREFORMAT_ASTC_10x5_FLOAT: Final[int] + SDL_GPU_TEXTUREFORMAT_ASTC_10x5_UNORM: Final[int] + SDL_GPU_TEXTUREFORMAT_ASTC_10x5_UNORM_SRGB: Final[int] + SDL_GPU_TEXTUREFORMAT_ASTC_10x6_FLOAT: Final[int] + SDL_GPU_TEXTUREFORMAT_ASTC_10x6_UNORM: Final[int] + SDL_GPU_TEXTUREFORMAT_ASTC_10x6_UNORM_SRGB: Final[int] + SDL_GPU_TEXTUREFORMAT_ASTC_10x8_FLOAT: Final[int] + SDL_GPU_TEXTUREFORMAT_ASTC_10x8_UNORM: Final[int] + SDL_GPU_TEXTUREFORMAT_ASTC_10x8_UNORM_SRGB: Final[int] + SDL_GPU_TEXTUREFORMAT_ASTC_12x10_FLOAT: Final[int] + SDL_GPU_TEXTUREFORMAT_ASTC_12x10_UNORM: Final[int] + SDL_GPU_TEXTUREFORMAT_ASTC_12x10_UNORM_SRGB: Final[int] + SDL_GPU_TEXTUREFORMAT_ASTC_12x12_FLOAT: Final[int] + SDL_GPU_TEXTUREFORMAT_ASTC_12x12_UNORM: Final[int] + SDL_GPU_TEXTUREFORMAT_ASTC_12x12_UNORM_SRGB: Final[int] + SDL_GPU_TEXTUREFORMAT_ASTC_4x4_FLOAT: Final[int] + SDL_GPU_TEXTUREFORMAT_ASTC_4x4_UNORM: Final[int] + SDL_GPU_TEXTUREFORMAT_ASTC_4x4_UNORM_SRGB: Final[int] + SDL_GPU_TEXTUREFORMAT_ASTC_5x4_FLOAT: Final[int] + SDL_GPU_TEXTUREFORMAT_ASTC_5x4_UNORM: Final[int] + SDL_GPU_TEXTUREFORMAT_ASTC_5x4_UNORM_SRGB: Final[int] + SDL_GPU_TEXTUREFORMAT_ASTC_5x5_FLOAT: Final[int] + SDL_GPU_TEXTUREFORMAT_ASTC_5x5_UNORM: Final[int] + SDL_GPU_TEXTUREFORMAT_ASTC_5x5_UNORM_SRGB: Final[int] + SDL_GPU_TEXTUREFORMAT_ASTC_6x5_FLOAT: Final[int] + SDL_GPU_TEXTUREFORMAT_ASTC_6x5_UNORM: Final[int] + SDL_GPU_TEXTUREFORMAT_ASTC_6x5_UNORM_SRGB: Final[int] + SDL_GPU_TEXTUREFORMAT_ASTC_6x6_FLOAT: Final[int] + SDL_GPU_TEXTUREFORMAT_ASTC_6x6_UNORM: Final[int] + SDL_GPU_TEXTUREFORMAT_ASTC_6x6_UNORM_SRGB: Final[int] + SDL_GPU_TEXTUREFORMAT_ASTC_8x5_FLOAT: Final[int] + SDL_GPU_TEXTUREFORMAT_ASTC_8x5_UNORM: Final[int] + SDL_GPU_TEXTUREFORMAT_ASTC_8x5_UNORM_SRGB: Final[int] + SDL_GPU_TEXTUREFORMAT_ASTC_8x6_FLOAT: Final[int] + SDL_GPU_TEXTUREFORMAT_ASTC_8x6_UNORM: Final[int] + SDL_GPU_TEXTUREFORMAT_ASTC_8x6_UNORM_SRGB: Final[int] + SDL_GPU_TEXTUREFORMAT_ASTC_8x8_FLOAT: Final[int] + SDL_GPU_TEXTUREFORMAT_ASTC_8x8_UNORM: Final[int] + SDL_GPU_TEXTUREFORMAT_ASTC_8x8_UNORM_SRGB: Final[int] + SDL_GPU_TEXTUREFORMAT_B4G4R4A4_UNORM: Final[int] + SDL_GPU_TEXTUREFORMAT_B5G5R5A1_UNORM: Final[int] + SDL_GPU_TEXTUREFORMAT_B5G6R5_UNORM: Final[int] + SDL_GPU_TEXTUREFORMAT_B8G8R8A8_UNORM: Final[int] + SDL_GPU_TEXTUREFORMAT_B8G8R8A8_UNORM_SRGB: Final[int] + SDL_GPU_TEXTUREFORMAT_BC1_RGBA_UNORM: Final[int] + SDL_GPU_TEXTUREFORMAT_BC1_RGBA_UNORM_SRGB: Final[int] + SDL_GPU_TEXTUREFORMAT_BC2_RGBA_UNORM: Final[int] + SDL_GPU_TEXTUREFORMAT_BC2_RGBA_UNORM_SRGB: Final[int] + SDL_GPU_TEXTUREFORMAT_BC3_RGBA_UNORM: Final[int] + SDL_GPU_TEXTUREFORMAT_BC3_RGBA_UNORM_SRGB: Final[int] + SDL_GPU_TEXTUREFORMAT_BC4_R_UNORM: Final[int] + SDL_GPU_TEXTUREFORMAT_BC5_RG_UNORM: Final[int] + SDL_GPU_TEXTUREFORMAT_BC6H_RGB_FLOAT: Final[int] + SDL_GPU_TEXTUREFORMAT_BC6H_RGB_UFLOAT: Final[int] + SDL_GPU_TEXTUREFORMAT_BC7_RGBA_UNORM: Final[int] + SDL_GPU_TEXTUREFORMAT_BC7_RGBA_UNORM_SRGB: Final[int] + SDL_GPU_TEXTUREFORMAT_D16_UNORM: Final[int] + SDL_GPU_TEXTUREFORMAT_D24_UNORM: Final[int] + SDL_GPU_TEXTUREFORMAT_D24_UNORM_S8_UINT: Final[int] + SDL_GPU_TEXTUREFORMAT_D32_FLOAT: Final[int] + SDL_GPU_TEXTUREFORMAT_D32_FLOAT_S8_UINT: Final[int] + SDL_GPU_TEXTUREFORMAT_INVALID: Final[int] + SDL_GPU_TEXTUREFORMAT_R10G10B10A2_UNORM: Final[int] + SDL_GPU_TEXTUREFORMAT_R11G11B10_UFLOAT: Final[int] + SDL_GPU_TEXTUREFORMAT_R16G16B16A16_FLOAT: Final[int] + SDL_GPU_TEXTUREFORMAT_R16G16B16A16_INT: Final[int] + SDL_GPU_TEXTUREFORMAT_R16G16B16A16_SNORM: Final[int] + SDL_GPU_TEXTUREFORMAT_R16G16B16A16_UINT: Final[int] + SDL_GPU_TEXTUREFORMAT_R16G16B16A16_UNORM: Final[int] + SDL_GPU_TEXTUREFORMAT_R16G16_FLOAT: Final[int] + SDL_GPU_TEXTUREFORMAT_R16G16_INT: Final[int] + SDL_GPU_TEXTUREFORMAT_R16G16_SNORM: Final[int] + SDL_GPU_TEXTUREFORMAT_R16G16_UINT: Final[int] + SDL_GPU_TEXTUREFORMAT_R16G16_UNORM: Final[int] + SDL_GPU_TEXTUREFORMAT_R16_FLOAT: Final[int] + SDL_GPU_TEXTUREFORMAT_R16_INT: Final[int] + SDL_GPU_TEXTUREFORMAT_R16_SNORM: Final[int] + SDL_GPU_TEXTUREFORMAT_R16_UINT: Final[int] + SDL_GPU_TEXTUREFORMAT_R16_UNORM: Final[int] + SDL_GPU_TEXTUREFORMAT_R32G32B32A32_FLOAT: Final[int] + SDL_GPU_TEXTUREFORMAT_R32G32B32A32_INT: Final[int] + SDL_GPU_TEXTUREFORMAT_R32G32B32A32_UINT: Final[int] + SDL_GPU_TEXTUREFORMAT_R32G32_FLOAT: Final[int] + SDL_GPU_TEXTUREFORMAT_R32G32_INT: Final[int] + SDL_GPU_TEXTUREFORMAT_R32G32_UINT: Final[int] + SDL_GPU_TEXTUREFORMAT_R32_FLOAT: Final[int] + SDL_GPU_TEXTUREFORMAT_R32_INT: Final[int] + SDL_GPU_TEXTUREFORMAT_R32_UINT: Final[int] + SDL_GPU_TEXTUREFORMAT_R8G8B8A8_INT: Final[int] + SDL_GPU_TEXTUREFORMAT_R8G8B8A8_SNORM: Final[int] + SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UINT: Final[int] + SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM: Final[int] + SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM_SRGB: Final[int] + SDL_GPU_TEXTUREFORMAT_R8G8_INT: Final[int] + SDL_GPU_TEXTUREFORMAT_R8G8_SNORM: Final[int] + SDL_GPU_TEXTUREFORMAT_R8G8_UINT: Final[int] + SDL_GPU_TEXTUREFORMAT_R8G8_UNORM: Final[int] + SDL_GPU_TEXTUREFORMAT_R8_INT: Final[int] + SDL_GPU_TEXTUREFORMAT_R8_SNORM: Final[int] + SDL_GPU_TEXTUREFORMAT_R8_UINT: Final[int] + SDL_GPU_TEXTUREFORMAT_R8_UNORM: Final[int] + SDL_GPU_TEXTURETYPE_2D: Final[int] + SDL_GPU_TEXTURETYPE_2D_ARRAY: Final[int] + SDL_GPU_TEXTURETYPE_3D: Final[int] + SDL_GPU_TEXTURETYPE_CUBE: Final[int] + SDL_GPU_TEXTURETYPE_CUBE_ARRAY: Final[int] + SDL_GPU_TEXTUREUSAGE_COLOR_TARGET: Final[int] + SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_READ: Final[int] + SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_SIMULTANEOUS_READ_WRITE: Final[int] + SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_WRITE: Final[int] + SDL_GPU_TEXTUREUSAGE_DEPTH_STENCIL_TARGET: Final[int] + SDL_GPU_TEXTUREUSAGE_GRAPHICS_STORAGE_READ: Final[int] + SDL_GPU_TEXTUREUSAGE_SAMPLER: Final[int] + SDL_GPU_TRANSFERBUFFERUSAGE_DOWNLOAD: Final[int] + SDL_GPU_TRANSFERBUFFERUSAGE_UPLOAD: Final[int] + SDL_GPU_VERTEXELEMENTFORMAT_BYTE2: Final[int] + SDL_GPU_VERTEXELEMENTFORMAT_BYTE2_NORM: Final[int] + SDL_GPU_VERTEXELEMENTFORMAT_BYTE4: Final[int] + SDL_GPU_VERTEXELEMENTFORMAT_BYTE4_NORM: Final[int] + SDL_GPU_VERTEXELEMENTFORMAT_FLOAT2: Final[int] + SDL_GPU_VERTEXELEMENTFORMAT_FLOAT3: Final[int] + SDL_GPU_VERTEXELEMENTFORMAT_FLOAT4: Final[int] + SDL_GPU_VERTEXELEMENTFORMAT_FLOAT: Final[int] + SDL_GPU_VERTEXELEMENTFORMAT_HALF2: Final[int] + SDL_GPU_VERTEXELEMENTFORMAT_HALF4: Final[int] + SDL_GPU_VERTEXELEMENTFORMAT_INT2: Final[int] + SDL_GPU_VERTEXELEMENTFORMAT_INT3: Final[int] + SDL_GPU_VERTEXELEMENTFORMAT_INT4: Final[int] + SDL_GPU_VERTEXELEMENTFORMAT_INT: Final[int] + SDL_GPU_VERTEXELEMENTFORMAT_INVALID: Final[int] + SDL_GPU_VERTEXELEMENTFORMAT_SHORT2: Final[int] + SDL_GPU_VERTEXELEMENTFORMAT_SHORT2_NORM: Final[int] + SDL_GPU_VERTEXELEMENTFORMAT_SHORT4: Final[int] + SDL_GPU_VERTEXELEMENTFORMAT_SHORT4_NORM: Final[int] + SDL_GPU_VERTEXELEMENTFORMAT_UBYTE2: Final[int] + SDL_GPU_VERTEXELEMENTFORMAT_UBYTE2_NORM: Final[int] + SDL_GPU_VERTEXELEMENTFORMAT_UBYTE4: Final[int] + SDL_GPU_VERTEXELEMENTFORMAT_UBYTE4_NORM: Final[int] + SDL_GPU_VERTEXELEMENTFORMAT_UINT2: Final[int] + SDL_GPU_VERTEXELEMENTFORMAT_UINT3: Final[int] + SDL_GPU_VERTEXELEMENTFORMAT_UINT4: Final[int] + SDL_GPU_VERTEXELEMENTFORMAT_UINT: Final[int] + SDL_GPU_VERTEXELEMENTFORMAT_USHORT2: Final[int] + SDL_GPU_VERTEXELEMENTFORMAT_USHORT2_NORM: Final[int] + SDL_GPU_VERTEXELEMENTFORMAT_USHORT4: Final[int] + SDL_GPU_VERTEXELEMENTFORMAT_USHORT4_NORM: Final[int] + SDL_GPU_VERTEXINPUTRATE_INSTANCE: Final[int] + SDL_GPU_VERTEXINPUTRATE_VERTEX: Final[int] + SDL_HAPTIC_AUTOCENTER: Final[int] + SDL_HAPTIC_CARTESIAN: Final[int] + SDL_HAPTIC_CONSTANT: Final[int] + SDL_HAPTIC_CUSTOM: Final[int] + SDL_HAPTIC_DAMPER: Final[int] + SDL_HAPTIC_FRICTION: Final[int] + SDL_HAPTIC_GAIN: Final[int] + SDL_HAPTIC_INERTIA: Final[int] + SDL_HAPTIC_INFINITY: Final[int] + SDL_HAPTIC_LEFTRIGHT: Final[int] + SDL_HAPTIC_PAUSE: Final[int] + SDL_HAPTIC_POLAR: Final[int] + SDL_HAPTIC_RAMP: Final[int] + SDL_HAPTIC_RESERVED1: Final[int] + SDL_HAPTIC_RESERVED2: Final[int] + SDL_HAPTIC_RESERVED3: Final[int] + SDL_HAPTIC_SAWTOOTHDOWN: Final[int] + SDL_HAPTIC_SAWTOOTHUP: Final[int] + SDL_HAPTIC_SINE: Final[int] + SDL_HAPTIC_SPHERICAL: Final[int] + SDL_HAPTIC_SPRING: Final[int] + SDL_HAPTIC_SQUARE: Final[int] + SDL_HAPTIC_STATUS: Final[int] + SDL_HAPTIC_STEERING_AXIS: Final[int] + SDL_HAPTIC_TRIANGLE: Final[int] + SDL_HAT_CENTERED: Final[int] + SDL_HAT_DOWN: Final[int] + SDL_HAT_LEFT: Final[int] + SDL_HAT_LEFTDOWN: Final[int] + SDL_HAT_LEFTUP: Final[int] + SDL_HAT_RIGHT: Final[int] + SDL_HAT_RIGHTDOWN: Final[int] + SDL_HAT_RIGHTUP: Final[int] + SDL_HAT_UP: Final[int] + SDL_HID_API_BUS_BLUETOOTH: Final[Literal[2]] = 2 + SDL_HID_API_BUS_I2C: Final[Literal[3]] = 3 + SDL_HID_API_BUS_SPI: Final[Literal[4]] = 4 + SDL_HID_API_BUS_UNKNOWN: Final[Literal[0]] = 0 + SDL_HID_API_BUS_USB: Final[Literal[1]] = 1 + SDL_HINT_DEFAULT: Final[int] + SDL_HINT_NORMAL: Final[int] + SDL_HINT_OVERRIDE: Final[int] + SDL_HITTEST_DRAGGABLE: Final[int] + SDL_HITTEST_NORMAL: Final[int] + SDL_HITTEST_RESIZE_BOTTOM: Final[int] + SDL_HITTEST_RESIZE_BOTTOMLEFT: Final[int] + SDL_HITTEST_RESIZE_BOTTOMRIGHT: Final[int] + SDL_HITTEST_RESIZE_LEFT: Final[int] + SDL_HITTEST_RESIZE_RIGHT: Final[int] + SDL_HITTEST_RESIZE_TOP: Final[int] + SDL_HITTEST_RESIZE_TOPLEFT: Final[int] + SDL_HITTEST_RESIZE_TOPRIGHT: Final[int] + SDL_ICONV_E2BIG: Final[int] + SDL_ICONV_EILSEQ: Final[int] + SDL_ICONV_EINVAL: Final[int] + SDL_ICONV_ERROR: Final[int] + SDL_INIT_AUDIO: Final[int] + SDL_INIT_CAMERA: Final[int] + SDL_INIT_EVENTS: Final[int] + SDL_INIT_GAMEPAD: Final[int] + SDL_INIT_HAPTIC: Final[int] + SDL_INIT_JOYSTICK: Final[int] + SDL_INIT_SENSOR: Final[int] + SDL_INIT_STATUS_INITIALIZED: Final[int] + SDL_INIT_STATUS_INITIALIZING: Final[int] + SDL_INIT_STATUS_UNINITIALIZED: Final[int] + SDL_INIT_STATUS_UNINITIALIZING: Final[int] + SDL_INIT_VIDEO: Final[int] + SDL_INVALID_UNICODE_CODEPOINT: Final[int] + SDL_IO_SEEK_CUR: Final[int] + SDL_IO_SEEK_END: Final[int] + SDL_IO_SEEK_SET: Final[int] + SDL_IO_STATUS_EOF: Final[int] + SDL_IO_STATUS_ERROR: Final[int] + SDL_IO_STATUS_NOT_READY: Final[int] + SDL_IO_STATUS_READONLY: Final[int] + SDL_IO_STATUS_READY: Final[int] + SDL_IO_STATUS_WRITEONLY: Final[int] + SDL_JOYSTICK_AXIS_MAX: Final[int] + SDL_JOYSTICK_AXIS_MIN: Final[int] + SDL_JOYSTICK_CONNECTION_INVALID: Final[Literal[-1]] = -1 + SDL_JOYSTICK_CONNECTION_UNKNOWN: Final[int] + SDL_JOYSTICK_CONNECTION_WIRED: Final[int] + SDL_JOYSTICK_CONNECTION_WIRELESS: Final[int] + SDL_JOYSTICK_TYPE_ARCADE_PAD: Final[int] + SDL_JOYSTICK_TYPE_ARCADE_STICK: Final[int] + SDL_JOYSTICK_TYPE_COUNT: Final[int] + SDL_JOYSTICK_TYPE_DANCE_PAD: Final[int] + SDL_JOYSTICK_TYPE_DRUM_KIT: Final[int] + SDL_JOYSTICK_TYPE_FLIGHT_STICK: Final[int] + SDL_JOYSTICK_TYPE_GAMEPAD: Final[int] + SDL_JOYSTICK_TYPE_GUITAR: Final[int] + SDL_JOYSTICK_TYPE_THROTTLE: Final[int] + SDL_JOYSTICK_TYPE_UNKNOWN: Final[int] + SDL_JOYSTICK_TYPE_WHEEL: Final[int] + SDL_KMOD_ALT: Final[int] + SDL_KMOD_CAPS: Final[int] + SDL_KMOD_CTRL: Final[int] + SDL_KMOD_GUI: Final[int] + SDL_KMOD_LALT: Final[int] + SDL_KMOD_LCTRL: Final[int] + SDL_KMOD_LEVEL5: Final[int] + SDL_KMOD_LGUI: Final[int] + SDL_KMOD_LSHIFT: Final[int] + SDL_KMOD_MODE: Final[int] + SDL_KMOD_NONE: Final[int] + SDL_KMOD_NUM: Final[int] + SDL_KMOD_RALT: Final[int] + SDL_KMOD_RCTRL: Final[int] + SDL_KMOD_RGUI: Final[int] + SDL_KMOD_RSHIFT: Final[int] + SDL_KMOD_SCROLL: Final[int] + SDL_KMOD_SHIFT: Final[int] + SDL_LIL_ENDIAN: Final[int] + SDL_LOGICAL_PRESENTATION_DISABLED: Final[int] + SDL_LOGICAL_PRESENTATION_INTEGER_SCALE: Final[int] + SDL_LOGICAL_PRESENTATION_LETTERBOX: Final[int] + SDL_LOGICAL_PRESENTATION_OVERSCAN: Final[int] + SDL_LOGICAL_PRESENTATION_STRETCH: Final[int] + SDL_LOG_CATEGORY_APPLICATION: Final[int] + SDL_LOG_CATEGORY_ASSERT: Final[int] + SDL_LOG_CATEGORY_AUDIO: Final[int] + SDL_LOG_CATEGORY_CUSTOM: Final[int] + SDL_LOG_CATEGORY_ERROR: Final[int] + SDL_LOG_CATEGORY_GPU: Final[int] + SDL_LOG_CATEGORY_INPUT: Final[int] + SDL_LOG_CATEGORY_RENDER: Final[int] + SDL_LOG_CATEGORY_RESERVED10: Final[int] + SDL_LOG_CATEGORY_RESERVED2: Final[int] + SDL_LOG_CATEGORY_RESERVED3: Final[int] + SDL_LOG_CATEGORY_RESERVED4: Final[int] + SDL_LOG_CATEGORY_RESERVED5: Final[int] + SDL_LOG_CATEGORY_RESERVED6: Final[int] + SDL_LOG_CATEGORY_RESERVED7: Final[int] + SDL_LOG_CATEGORY_RESERVED8: Final[int] + SDL_LOG_CATEGORY_RESERVED9: Final[int] + SDL_LOG_CATEGORY_SYSTEM: Final[int] + SDL_LOG_CATEGORY_TEST: Final[int] + SDL_LOG_CATEGORY_VIDEO: Final[int] + SDL_LOG_PRIORITY_COUNT: Final[int] + SDL_LOG_PRIORITY_CRITICAL: Final[int] + SDL_LOG_PRIORITY_DEBUG: Final[int] + SDL_LOG_PRIORITY_ERROR: Final[int] + SDL_LOG_PRIORITY_INFO: Final[int] + SDL_LOG_PRIORITY_INVALID: Final[int] + SDL_LOG_PRIORITY_TRACE: Final[int] + SDL_LOG_PRIORITY_VERBOSE: Final[int] + SDL_LOG_PRIORITY_WARN: Final[int] + SDL_MAJOR_VERSION: Final[int] + SDL_MATRIX_COEFFICIENTS_BT2020_CL: Final[Literal[10]] = 10 + SDL_MATRIX_COEFFICIENTS_BT2020_NCL: Final[Literal[9]] = 9 + SDL_MATRIX_COEFFICIENTS_BT470BG: Final[Literal[5]] = 5 + SDL_MATRIX_COEFFICIENTS_BT601: Final[Literal[6]] = 6 + SDL_MATRIX_COEFFICIENTS_BT709: Final[Literal[1]] = 1 + SDL_MATRIX_COEFFICIENTS_CHROMA_DERIVED_CL: Final[Literal[13]] = 13 + SDL_MATRIX_COEFFICIENTS_CHROMA_DERIVED_NCL: Final[Literal[12]] = 12 + SDL_MATRIX_COEFFICIENTS_CUSTOM: Final[Literal[31]] = 31 + SDL_MATRIX_COEFFICIENTS_FCC: Final[Literal[4]] = 4 + SDL_MATRIX_COEFFICIENTS_ICTCP: Final[Literal[14]] = 14 + SDL_MATRIX_COEFFICIENTS_IDENTITY: Final[Literal[0]] = 0 + SDL_MATRIX_COEFFICIENTS_SMPTE2085: Final[Literal[11]] = 11 + SDL_MATRIX_COEFFICIENTS_SMPTE240: Final[Literal[7]] = 7 + SDL_MATRIX_COEFFICIENTS_UNSPECIFIED: Final[Literal[2]] = 2 + SDL_MATRIX_COEFFICIENTS_YCGCO: Final[Literal[8]] = 8 + SDL_MAX_SINT16: Final[int] + SDL_MAX_SINT32: Final[int] + SDL_MAX_SINT64: Final[int] + SDL_MAX_SINT8: Final[int] + SDL_MAX_TIME: Final[int] + SDL_MAX_UINT16: Final[int] + SDL_MAX_UINT32: Final[int] + SDL_MAX_UINT64: Final[int] + SDL_MAX_UINT8: Final[int] + SDL_MESSAGEBOX_BUTTONS_LEFT_TO_RIGHT: Final[int] + SDL_MESSAGEBOX_BUTTONS_RIGHT_TO_LEFT: Final[int] + SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT: Final[int] + SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT: Final[int] + SDL_MESSAGEBOX_COLOR_BACKGROUND: Final[int] + SDL_MESSAGEBOX_COLOR_BUTTON_BACKGROUND: Final[int] + SDL_MESSAGEBOX_COLOR_BUTTON_BORDER: Final[int] + SDL_MESSAGEBOX_COLOR_BUTTON_SELECTED: Final[int] + SDL_MESSAGEBOX_COLOR_COUNT: Final[int] + SDL_MESSAGEBOX_COLOR_TEXT: Final[int] + SDL_MESSAGEBOX_ERROR: Final[int] + SDL_MESSAGEBOX_INFORMATION: Final[int] + SDL_MESSAGEBOX_WARNING: Final[int] + SDL_MICRO_VERSION: Final[int] + SDL_MINOR_VERSION: Final[int] + SDL_MIN_SINT16: Final[int] + SDL_MIN_SINT32: Final[int] + SDL_MIN_SINT64: Final[int] + SDL_MIN_SINT8: Final[int] + SDL_MIN_TIME: Final[int] + SDL_MIN_UINT16: Final[int] + SDL_MIN_UINT32: Final[int] + SDL_MIN_UINT64: Final[int] + SDL_MIN_UINT8: Final[int] + SDL_MOUSEWHEEL_FLIPPED: Final[int] + SDL_MOUSEWHEEL_NORMAL: Final[int] + SDL_MOUSE_TOUCHID: Final[int] + SDL_MS_PER_SECOND: Final[int] + SDL_NS_PER_MS: Final[int] + SDL_NS_PER_SECOND: Final[int] + SDL_NS_PER_US: Final[int] + SDL_NULL_WHILE_LOOP_CONDITION: Final[int] + SDL_ORIENTATION_LANDSCAPE: Final[int] + SDL_ORIENTATION_LANDSCAPE_FLIPPED: Final[int] + SDL_ORIENTATION_PORTRAIT: Final[int] + SDL_ORIENTATION_PORTRAIT_FLIPPED: Final[int] + SDL_ORIENTATION_UNKNOWN: Final[int] + SDL_PACKEDLAYOUT_1010102: Final[int] + SDL_PACKEDLAYOUT_1555: Final[int] + SDL_PACKEDLAYOUT_2101010: Final[int] + SDL_PACKEDLAYOUT_332: Final[int] + SDL_PACKEDLAYOUT_4444: Final[int] + SDL_PACKEDLAYOUT_5551: Final[int] + SDL_PACKEDLAYOUT_565: Final[int] + SDL_PACKEDLAYOUT_8888: Final[int] + SDL_PACKEDLAYOUT_NONE: Final[int] + SDL_PACKEDORDER_ABGR: Final[int] + SDL_PACKEDORDER_ARGB: Final[int] + SDL_PACKEDORDER_BGRA: Final[int] + SDL_PACKEDORDER_BGRX: Final[int] + SDL_PACKEDORDER_NONE: Final[int] + SDL_PACKEDORDER_RGBA: Final[int] + SDL_PACKEDORDER_RGBX: Final[int] + SDL_PACKEDORDER_XBGR: Final[int] + SDL_PACKEDORDER_XRGB: Final[int] + SDL_PATHTYPE_DIRECTORY: Final[int] + SDL_PATHTYPE_FILE: Final[int] + SDL_PATHTYPE_NONE: Final[int] + SDL_PATHTYPE_OTHER: Final[int] + SDL_PEEKEVENT: Final[int] + SDL_PEN_AXIS_COUNT: Final[int] + SDL_PEN_AXIS_DISTANCE: Final[int] + SDL_PEN_AXIS_PRESSURE: Final[int] + SDL_PEN_AXIS_ROTATION: Final[int] + SDL_PEN_AXIS_SLIDER: Final[int] + SDL_PEN_AXIS_TANGENTIAL_PRESSURE: Final[int] + SDL_PEN_AXIS_XTILT: Final[int] + SDL_PEN_AXIS_YTILT: Final[int] + SDL_PEN_INPUT_BUTTON_1: Final[int] + SDL_PEN_INPUT_BUTTON_2: Final[int] + SDL_PEN_INPUT_BUTTON_3: Final[int] + SDL_PEN_INPUT_BUTTON_4: Final[int] + SDL_PEN_INPUT_BUTTON_5: Final[int] + SDL_PEN_INPUT_DOWN: Final[int] + SDL_PEN_INPUT_ERASER_TIP: Final[int] + SDL_PEN_MOUSEID: Final[int] + SDL_PEN_TOUCHID: Final[int] + SDL_PIXELFORMAT_ABGR128_FLOAT: int + SDL_PIXELFORMAT_ABGR1555: int + SDL_PIXELFORMAT_ABGR2101010: int + SDL_PIXELFORMAT_ABGR32: int + SDL_PIXELFORMAT_ABGR4444: int + SDL_PIXELFORMAT_ABGR64: int + SDL_PIXELFORMAT_ABGR64_FLOAT: int + SDL_PIXELFORMAT_ABGR8888: int + SDL_PIXELFORMAT_ARGB128_FLOAT: int + SDL_PIXELFORMAT_ARGB1555: int + SDL_PIXELFORMAT_ARGB2101010: int + SDL_PIXELFORMAT_ARGB32: int + SDL_PIXELFORMAT_ARGB4444: int + SDL_PIXELFORMAT_ARGB64: int + SDL_PIXELFORMAT_ARGB64_FLOAT: int + SDL_PIXELFORMAT_ARGB8888: int + SDL_PIXELFORMAT_BGR24: int + SDL_PIXELFORMAT_BGR48: int + SDL_PIXELFORMAT_BGR48_FLOAT: int + SDL_PIXELFORMAT_BGR565: int + SDL_PIXELFORMAT_BGR96_FLOAT: int + SDL_PIXELFORMAT_BGRA128_FLOAT: int + SDL_PIXELFORMAT_BGRA32: int + SDL_PIXELFORMAT_BGRA4444: int + SDL_PIXELFORMAT_BGRA5551: int + SDL_PIXELFORMAT_BGRA64: int + SDL_PIXELFORMAT_BGRA64_FLOAT: int + SDL_PIXELFORMAT_BGRA8888: int + SDL_PIXELFORMAT_BGRX32: int + SDL_PIXELFORMAT_BGRX8888: int + SDL_PIXELFORMAT_EXTERNAL_OES: int + SDL_PIXELFORMAT_INDEX1LSB: int + SDL_PIXELFORMAT_INDEX1MSB: int + SDL_PIXELFORMAT_INDEX2LSB: int + SDL_PIXELFORMAT_INDEX2MSB: int + SDL_PIXELFORMAT_INDEX4LSB: int + SDL_PIXELFORMAT_INDEX4MSB: int + SDL_PIXELFORMAT_INDEX8: int + SDL_PIXELFORMAT_IYUV: int + SDL_PIXELFORMAT_MJPG: int + SDL_PIXELFORMAT_NV12: int + SDL_PIXELFORMAT_NV21: int + SDL_PIXELFORMAT_P010: int + SDL_PIXELFORMAT_RGB24: int + SDL_PIXELFORMAT_RGB332: int + SDL_PIXELFORMAT_RGB48: int + SDL_PIXELFORMAT_RGB48_FLOAT: int + SDL_PIXELFORMAT_RGB565: int + SDL_PIXELFORMAT_RGB96_FLOAT: int + SDL_PIXELFORMAT_RGBA128_FLOAT: int + SDL_PIXELFORMAT_RGBA32: int + SDL_PIXELFORMAT_RGBA4444: int + SDL_PIXELFORMAT_RGBA5551: int + SDL_PIXELFORMAT_RGBA64: int + SDL_PIXELFORMAT_RGBA64_FLOAT: int + SDL_PIXELFORMAT_RGBA8888: int + SDL_PIXELFORMAT_RGBX32: int + SDL_PIXELFORMAT_RGBX8888: int + SDL_PIXELFORMAT_UNKNOWN: int + SDL_PIXELFORMAT_UYVY: int + SDL_PIXELFORMAT_XBGR1555: int + SDL_PIXELFORMAT_XBGR2101010: int + SDL_PIXELFORMAT_XBGR32: int + SDL_PIXELFORMAT_XBGR4444: int + SDL_PIXELFORMAT_XBGR8888: int + SDL_PIXELFORMAT_XRGB1555: int + SDL_PIXELFORMAT_XRGB2101010: int + SDL_PIXELFORMAT_XRGB32: int + SDL_PIXELFORMAT_XRGB4444: int + SDL_PIXELFORMAT_XRGB8888: int + SDL_PIXELFORMAT_YUY2: int + SDL_PIXELFORMAT_YV12: int + SDL_PIXELFORMAT_YVYU: int + SDL_PIXELTYPE_ARRAYF16: Final[int] + SDL_PIXELTYPE_ARRAYF32: Final[int] + SDL_PIXELTYPE_ARRAYU16: Final[int] + SDL_PIXELTYPE_ARRAYU32: Final[int] + SDL_PIXELTYPE_ARRAYU8: Final[int] + SDL_PIXELTYPE_INDEX1: Final[int] + SDL_PIXELTYPE_INDEX2: Final[int] + SDL_PIXELTYPE_INDEX4: Final[int] + SDL_PIXELTYPE_INDEX8: Final[int] + SDL_PIXELTYPE_PACKED16: Final[int] + SDL_PIXELTYPE_PACKED32: Final[int] + SDL_PIXELTYPE_PACKED8: Final[int] + SDL_PIXELTYPE_UNKNOWN: Final[int] + SDL_POWERSTATE_CHARGED: Final[int] + SDL_POWERSTATE_CHARGING: Final[int] + SDL_POWERSTATE_ERROR: Final[Literal[-1]] = -1 + SDL_POWERSTATE_NO_BATTERY: Final[int] + SDL_POWERSTATE_ON_BATTERY: Final[int] + SDL_POWERSTATE_UNKNOWN: Final[int] + SDL_PROCESS_STDIO_APP: Final[int] + SDL_PROCESS_STDIO_INHERITED: Final[int] + SDL_PROCESS_STDIO_NULL: Final[int] + SDL_PROCESS_STDIO_REDIRECT: Final[int] + SDL_PROPERTY_TYPE_BOOLEAN: Final[int] + SDL_PROPERTY_TYPE_FLOAT: Final[int] + SDL_PROPERTY_TYPE_INVALID: Final[int] + SDL_PROPERTY_TYPE_NUMBER: Final[int] + SDL_PROPERTY_TYPE_POINTER: Final[int] + SDL_PROPERTY_TYPE_STRING: Final[int] + SDL_RENDERER_VSYNC_ADAPTIVE: Final[int] + SDL_RENDERER_VSYNC_DISABLED: Final[int] + SDL_SANDBOX_FLATPAK: Final[int] + SDL_SANDBOX_MACOS: Final[int] + SDL_SANDBOX_NONE: Final[Literal[0]] = 0 + SDL_SANDBOX_SNAP: Final[int] + SDL_SANDBOX_UNKNOWN_CONTAINER: Final[int] + SDL_SCALEMODE_INVALID: Final[Literal[-1]] = -1 + SDL_SCALEMODE_LINEAR: Final[int] + SDL_SCALEMODE_NEAREST: Final[int] + SDL_SCANCODE_0: Final[Literal[39]] = 39 + SDL_SCANCODE_1: Final[Literal[30]] = 30 + SDL_SCANCODE_2: Final[Literal[31]] = 31 + SDL_SCANCODE_3: Final[Literal[32]] = 32 + SDL_SCANCODE_4: Final[Literal[33]] = 33 + SDL_SCANCODE_5: Final[Literal[34]] = 34 + SDL_SCANCODE_6: Final[Literal[35]] = 35 + SDL_SCANCODE_7: Final[Literal[36]] = 36 + SDL_SCANCODE_8: Final[Literal[37]] = 37 + SDL_SCANCODE_9: Final[Literal[38]] = 38 + SDL_SCANCODE_A: Final[Literal[4]] = 4 + SDL_SCANCODE_AC_BACK: Final[Literal[282]] = 282 + SDL_SCANCODE_AC_BOOKMARKS: Final[Literal[286]] = 286 + SDL_SCANCODE_AC_CLOSE: Final[Literal[275]] = 275 + SDL_SCANCODE_AC_EXIT: Final[Literal[276]] = 276 + SDL_SCANCODE_AC_FORWARD: Final[Literal[283]] = 283 + SDL_SCANCODE_AC_HOME: Final[Literal[281]] = 281 + SDL_SCANCODE_AC_NEW: Final[Literal[273]] = 273 + SDL_SCANCODE_AC_OPEN: Final[Literal[274]] = 274 + SDL_SCANCODE_AC_PRINT: Final[Literal[278]] = 278 + SDL_SCANCODE_AC_PROPERTIES: Final[Literal[279]] = 279 + SDL_SCANCODE_AC_REFRESH: Final[Literal[285]] = 285 + SDL_SCANCODE_AC_SAVE: Final[Literal[277]] = 277 + SDL_SCANCODE_AC_SEARCH: Final[Literal[280]] = 280 + SDL_SCANCODE_AC_STOP: Final[Literal[284]] = 284 + SDL_SCANCODE_AGAIN: Final[Literal[121]] = 121 + SDL_SCANCODE_ALTERASE: Final[Literal[153]] = 153 + SDL_SCANCODE_APOSTROPHE: Final[Literal[52]] = 52 + SDL_SCANCODE_APPLICATION: Final[Literal[101]] = 101 + SDL_SCANCODE_B: Final[Literal[5]] = 5 + SDL_SCANCODE_BACKSLASH: Final[Literal[49]] = 49 + SDL_SCANCODE_BACKSPACE: Final[Literal[42]] = 42 + SDL_SCANCODE_C: Final[Literal[6]] = 6 + SDL_SCANCODE_CALL: Final[Literal[289]] = 289 + SDL_SCANCODE_CANCEL: Final[Literal[155]] = 155 + SDL_SCANCODE_CAPSLOCK: Final[Literal[57]] = 57 + SDL_SCANCODE_CHANNEL_DECREMENT: Final[Literal[261]] = 261 + SDL_SCANCODE_CHANNEL_INCREMENT: Final[Literal[260]] = 260 + SDL_SCANCODE_CLEAR: Final[Literal[156]] = 156 + SDL_SCANCODE_CLEARAGAIN: Final[Literal[162]] = 162 + SDL_SCANCODE_COMMA: Final[Literal[54]] = 54 + SDL_SCANCODE_COPY: Final[Literal[124]] = 124 + SDL_SCANCODE_COUNT: Final[Literal[512]] = 512 + SDL_SCANCODE_CRSEL: Final[Literal[163]] = 163 + SDL_SCANCODE_CURRENCYSUBUNIT: Final[Literal[181]] = 181 + SDL_SCANCODE_CURRENCYUNIT: Final[Literal[180]] = 180 + SDL_SCANCODE_CUT: Final[Literal[123]] = 123 + SDL_SCANCODE_D: Final[Literal[7]] = 7 + SDL_SCANCODE_DECIMALSEPARATOR: Final[Literal[179]] = 179 + SDL_SCANCODE_DELETE: Final[Literal[76]] = 76 + SDL_SCANCODE_DOWN: Final[Literal[81]] = 81 + SDL_SCANCODE_E: Final[Literal[8]] = 8 + SDL_SCANCODE_END: Final[Literal[77]] = 77 + SDL_SCANCODE_ENDCALL: Final[Literal[290]] = 290 + SDL_SCANCODE_EQUALS: Final[Literal[46]] = 46 + SDL_SCANCODE_ESCAPE: Final[Literal[41]] = 41 + SDL_SCANCODE_EXECUTE: Final[Literal[116]] = 116 + SDL_SCANCODE_EXSEL: Final[Literal[164]] = 164 + SDL_SCANCODE_F10: Final[Literal[67]] = 67 + SDL_SCANCODE_F11: Final[Literal[68]] = 68 + SDL_SCANCODE_F12: Final[Literal[69]] = 69 + SDL_SCANCODE_F13: Final[Literal[104]] = 104 + SDL_SCANCODE_F14: Final[Literal[105]] = 105 + SDL_SCANCODE_F15: Final[Literal[106]] = 106 + SDL_SCANCODE_F16: Final[Literal[107]] = 107 + SDL_SCANCODE_F17: Final[Literal[108]] = 108 + SDL_SCANCODE_F18: Final[Literal[109]] = 109 + SDL_SCANCODE_F19: Final[Literal[110]] = 110 + SDL_SCANCODE_F1: Final[Literal[58]] = 58 + SDL_SCANCODE_F20: Final[Literal[111]] = 111 + SDL_SCANCODE_F21: Final[Literal[112]] = 112 + SDL_SCANCODE_F22: Final[Literal[113]] = 113 + SDL_SCANCODE_F23: Final[Literal[114]] = 114 + SDL_SCANCODE_F24: Final[Literal[115]] = 115 + SDL_SCANCODE_F2: Final[Literal[59]] = 59 + SDL_SCANCODE_F3: Final[Literal[60]] = 60 + SDL_SCANCODE_F4: Final[Literal[61]] = 61 + SDL_SCANCODE_F5: Final[Literal[62]] = 62 + SDL_SCANCODE_F6: Final[Literal[63]] = 63 + SDL_SCANCODE_F7: Final[Literal[64]] = 64 + SDL_SCANCODE_F8: Final[Literal[65]] = 65 + SDL_SCANCODE_F9: Final[Literal[66]] = 66 + SDL_SCANCODE_F: Final[Literal[9]] = 9 + SDL_SCANCODE_FIND: Final[Literal[126]] = 126 + SDL_SCANCODE_G: Final[Literal[10]] = 10 + SDL_SCANCODE_GRAVE: Final[Literal[53]] = 53 + SDL_SCANCODE_H: Final[Literal[11]] = 11 + SDL_SCANCODE_HELP: Final[Literal[117]] = 117 + SDL_SCANCODE_HOME: Final[Literal[74]] = 74 + SDL_SCANCODE_I: Final[Literal[12]] = 12 + SDL_SCANCODE_INSERT: Final[Literal[73]] = 73 + SDL_SCANCODE_INTERNATIONAL1: Final[Literal[135]] = 135 + SDL_SCANCODE_INTERNATIONAL2: Final[Literal[136]] = 136 + SDL_SCANCODE_INTERNATIONAL3: Final[Literal[137]] = 137 + SDL_SCANCODE_INTERNATIONAL4: Final[Literal[138]] = 138 + SDL_SCANCODE_INTERNATIONAL5: Final[Literal[139]] = 139 + SDL_SCANCODE_INTERNATIONAL6: Final[Literal[140]] = 140 + SDL_SCANCODE_INTERNATIONAL7: Final[Literal[141]] = 141 + SDL_SCANCODE_INTERNATIONAL8: Final[Literal[142]] = 142 + SDL_SCANCODE_INTERNATIONAL9: Final[Literal[143]] = 143 + SDL_SCANCODE_J: Final[Literal[13]] = 13 + SDL_SCANCODE_K: Final[Literal[14]] = 14 + SDL_SCANCODE_KP_000: Final[Literal[177]] = 177 + SDL_SCANCODE_KP_00: Final[Literal[176]] = 176 + SDL_SCANCODE_KP_0: Final[Literal[98]] = 98 + SDL_SCANCODE_KP_1: Final[Literal[89]] = 89 + SDL_SCANCODE_KP_2: Final[Literal[90]] = 90 + SDL_SCANCODE_KP_3: Final[Literal[91]] = 91 + SDL_SCANCODE_KP_4: Final[Literal[92]] = 92 + SDL_SCANCODE_KP_5: Final[Literal[93]] = 93 + SDL_SCANCODE_KP_6: Final[Literal[94]] = 94 + SDL_SCANCODE_KP_7: Final[Literal[95]] = 95 + SDL_SCANCODE_KP_8: Final[Literal[96]] = 96 + SDL_SCANCODE_KP_9: Final[Literal[97]] = 97 + SDL_SCANCODE_KP_A: Final[Literal[188]] = 188 + SDL_SCANCODE_KP_AMPERSAND: Final[Literal[199]] = 199 + SDL_SCANCODE_KP_AT: Final[Literal[206]] = 206 + SDL_SCANCODE_KP_B: Final[Literal[189]] = 189 + SDL_SCANCODE_KP_BACKSPACE: Final[Literal[187]] = 187 + SDL_SCANCODE_KP_BINARY: Final[Literal[218]] = 218 + SDL_SCANCODE_KP_C: Final[Literal[190]] = 190 + SDL_SCANCODE_KP_CLEAR: Final[Literal[216]] = 216 + SDL_SCANCODE_KP_CLEARENTRY: Final[Literal[217]] = 217 + SDL_SCANCODE_KP_COLON: Final[Literal[203]] = 203 + SDL_SCANCODE_KP_COMMA: Final[Literal[133]] = 133 + SDL_SCANCODE_KP_D: Final[Literal[191]] = 191 + SDL_SCANCODE_KP_DBLAMPERSAND: Final[Literal[200]] = 200 + SDL_SCANCODE_KP_DBLVERTICALBAR: Final[Literal[202]] = 202 + SDL_SCANCODE_KP_DECIMAL: Final[Literal[220]] = 220 + SDL_SCANCODE_KP_DIVIDE: Final[Literal[84]] = 84 + SDL_SCANCODE_KP_E: Final[Literal[192]] = 192 + SDL_SCANCODE_KP_ENTER: Final[Literal[88]] = 88 + SDL_SCANCODE_KP_EQUALS: Final[Literal[103]] = 103 + SDL_SCANCODE_KP_EQUALSAS400: Final[Literal[134]] = 134 + SDL_SCANCODE_KP_EXCLAM: Final[Literal[207]] = 207 + SDL_SCANCODE_KP_F: Final[Literal[193]] = 193 + SDL_SCANCODE_KP_GREATER: Final[Literal[198]] = 198 + SDL_SCANCODE_KP_HASH: Final[Literal[204]] = 204 + SDL_SCANCODE_KP_HEXADECIMAL: Final[Literal[221]] = 221 + SDL_SCANCODE_KP_LEFTBRACE: Final[Literal[184]] = 184 + SDL_SCANCODE_KP_LEFTPAREN: Final[Literal[182]] = 182 + SDL_SCANCODE_KP_LESS: Final[Literal[197]] = 197 + SDL_SCANCODE_KP_MEMADD: Final[Literal[211]] = 211 + SDL_SCANCODE_KP_MEMCLEAR: Final[Literal[210]] = 210 + SDL_SCANCODE_KP_MEMDIVIDE: Final[Literal[214]] = 214 + SDL_SCANCODE_KP_MEMMULTIPLY: Final[Literal[213]] = 213 + SDL_SCANCODE_KP_MEMRECALL: Final[Literal[209]] = 209 + SDL_SCANCODE_KP_MEMSTORE: Final[Literal[208]] = 208 + SDL_SCANCODE_KP_MEMSUBTRACT: Final[Literal[212]] = 212 + SDL_SCANCODE_KP_MINUS: Final[Literal[86]] = 86 + SDL_SCANCODE_KP_MULTIPLY: Final[Literal[85]] = 85 + SDL_SCANCODE_KP_OCTAL: Final[Literal[219]] = 219 + SDL_SCANCODE_KP_PERCENT: Final[Literal[196]] = 196 + SDL_SCANCODE_KP_PERIOD: Final[Literal[99]] = 99 + SDL_SCANCODE_KP_PLUS: Final[Literal[87]] = 87 + SDL_SCANCODE_KP_PLUSMINUS: Final[Literal[215]] = 215 + SDL_SCANCODE_KP_POWER: Final[Literal[195]] = 195 + SDL_SCANCODE_KP_RIGHTBRACE: Final[Literal[185]] = 185 + SDL_SCANCODE_KP_RIGHTPAREN: Final[Literal[183]] = 183 + SDL_SCANCODE_KP_SPACE: Final[Literal[205]] = 205 + SDL_SCANCODE_KP_TAB: Final[Literal[186]] = 186 + SDL_SCANCODE_KP_VERTICALBAR: Final[Literal[201]] = 201 + SDL_SCANCODE_KP_XOR: Final[Literal[194]] = 194 + SDL_SCANCODE_L: Final[Literal[15]] = 15 + SDL_SCANCODE_LALT: Final[Literal[226]] = 226 + SDL_SCANCODE_LANG1: Final[Literal[144]] = 144 + SDL_SCANCODE_LANG2: Final[Literal[145]] = 145 + SDL_SCANCODE_LANG3: Final[Literal[146]] = 146 + SDL_SCANCODE_LANG4: Final[Literal[147]] = 147 + SDL_SCANCODE_LANG5: Final[Literal[148]] = 148 + SDL_SCANCODE_LANG6: Final[Literal[149]] = 149 + SDL_SCANCODE_LANG7: Final[Literal[150]] = 150 + SDL_SCANCODE_LANG8: Final[Literal[151]] = 151 + SDL_SCANCODE_LANG9: Final[Literal[152]] = 152 + SDL_SCANCODE_LCTRL: Final[Literal[224]] = 224 + SDL_SCANCODE_LEFT: Final[Literal[80]] = 80 + SDL_SCANCODE_LEFTBRACKET: Final[Literal[47]] = 47 + SDL_SCANCODE_LGUI: Final[Literal[227]] = 227 + SDL_SCANCODE_LSHIFT: Final[Literal[225]] = 225 + SDL_SCANCODE_M: Final[Literal[16]] = 16 + SDL_SCANCODE_MEDIA_EJECT: Final[Literal[270]] = 270 + SDL_SCANCODE_MEDIA_FAST_FORWARD: Final[Literal[265]] = 265 + SDL_SCANCODE_MEDIA_NEXT_TRACK: Final[Literal[267]] = 267 + SDL_SCANCODE_MEDIA_PAUSE: Final[Literal[263]] = 263 + SDL_SCANCODE_MEDIA_PLAY: Final[Literal[262]] = 262 + SDL_SCANCODE_MEDIA_PLAY_PAUSE: Final[Literal[271]] = 271 + SDL_SCANCODE_MEDIA_PREVIOUS_TRACK: Final[Literal[268]] = 268 + SDL_SCANCODE_MEDIA_RECORD: Final[Literal[264]] = 264 + SDL_SCANCODE_MEDIA_REWIND: Final[Literal[266]] = 266 + SDL_SCANCODE_MEDIA_SELECT: Final[Literal[272]] = 272 + SDL_SCANCODE_MEDIA_STOP: Final[Literal[269]] = 269 + SDL_SCANCODE_MENU: Final[Literal[118]] = 118 + SDL_SCANCODE_MINUS: Final[Literal[45]] = 45 + SDL_SCANCODE_MODE: Final[Literal[257]] = 257 + SDL_SCANCODE_MUTE: Final[Literal[127]] = 127 + SDL_SCANCODE_N: Final[Literal[17]] = 17 + SDL_SCANCODE_NONUSBACKSLASH: Final[Literal[100]] = 100 + SDL_SCANCODE_NONUSHASH: Final[Literal[50]] = 50 + SDL_SCANCODE_NUMLOCKCLEAR: Final[Literal[83]] = 83 + SDL_SCANCODE_O: Final[Literal[18]] = 18 + SDL_SCANCODE_OPER: Final[Literal[161]] = 161 + SDL_SCANCODE_OUT: Final[Literal[160]] = 160 + SDL_SCANCODE_P: Final[Literal[19]] = 19 + SDL_SCANCODE_PAGEDOWN: Final[Literal[78]] = 78 + SDL_SCANCODE_PAGEUP: Final[Literal[75]] = 75 + SDL_SCANCODE_PASTE: Final[Literal[125]] = 125 + SDL_SCANCODE_PAUSE: Final[Literal[72]] = 72 + SDL_SCANCODE_PERIOD: Final[Literal[55]] = 55 + SDL_SCANCODE_POWER: Final[Literal[102]] = 102 + SDL_SCANCODE_PRINTSCREEN: Final[Literal[70]] = 70 + SDL_SCANCODE_PRIOR: Final[Literal[157]] = 157 + SDL_SCANCODE_Q: Final[Literal[20]] = 20 + SDL_SCANCODE_R: Final[Literal[21]] = 21 + SDL_SCANCODE_RALT: Final[Literal[230]] = 230 + SDL_SCANCODE_RCTRL: Final[Literal[228]] = 228 + SDL_SCANCODE_RESERVED: Final[Literal[400]] = 400 + SDL_SCANCODE_RETURN2: Final[Literal[158]] = 158 + SDL_SCANCODE_RETURN: Final[Literal[40]] = 40 + SDL_SCANCODE_RGUI: Final[Literal[231]] = 231 + SDL_SCANCODE_RIGHT: Final[Literal[79]] = 79 + SDL_SCANCODE_RIGHTBRACKET: Final[Literal[48]] = 48 + SDL_SCANCODE_RSHIFT: Final[Literal[229]] = 229 + SDL_SCANCODE_S: Final[Literal[22]] = 22 + SDL_SCANCODE_SCROLLLOCK: Final[Literal[71]] = 71 + SDL_SCANCODE_SELECT: Final[Literal[119]] = 119 + SDL_SCANCODE_SEMICOLON: Final[Literal[51]] = 51 + SDL_SCANCODE_SEPARATOR: Final[Literal[159]] = 159 + SDL_SCANCODE_SLASH: Final[Literal[56]] = 56 + SDL_SCANCODE_SLEEP: Final[Literal[258]] = 258 + SDL_SCANCODE_SOFTLEFT: Final[Literal[287]] = 287 + SDL_SCANCODE_SOFTRIGHT: Final[Literal[288]] = 288 + SDL_SCANCODE_SPACE: Final[Literal[44]] = 44 + SDL_SCANCODE_STOP: Final[Literal[120]] = 120 + SDL_SCANCODE_SYSREQ: Final[Literal[154]] = 154 + SDL_SCANCODE_T: Final[Literal[23]] = 23 + SDL_SCANCODE_TAB: Final[Literal[43]] = 43 + SDL_SCANCODE_THOUSANDSSEPARATOR: Final[Literal[178]] = 178 + SDL_SCANCODE_U: Final[Literal[24]] = 24 + SDL_SCANCODE_UNDO: Final[Literal[122]] = 122 + SDL_SCANCODE_UNKNOWN: Final[Literal[0]] = 0 + SDL_SCANCODE_UP: Final[Literal[82]] = 82 + SDL_SCANCODE_V: Final[Literal[25]] = 25 + SDL_SCANCODE_VOLUMEDOWN: Final[Literal[129]] = 129 + SDL_SCANCODE_VOLUMEUP: Final[Literal[128]] = 128 + SDL_SCANCODE_W: Final[Literal[26]] = 26 + SDL_SCANCODE_WAKE: Final[Literal[259]] = 259 + SDL_SCANCODE_X: Final[Literal[27]] = 27 + SDL_SCANCODE_Y: Final[Literal[28]] = 28 + SDL_SCANCODE_Z: Final[Literal[29]] = 29 + SDL_SENSOR_ACCEL: Final[int] + SDL_SENSOR_ACCEL_L: Final[int] + SDL_SENSOR_ACCEL_R: Final[int] + SDL_SENSOR_GYRO: Final[int] + SDL_SENSOR_GYRO_L: Final[int] + SDL_SENSOR_GYRO_R: Final[int] + SDL_SENSOR_INVALID: Final[Literal[-1]] = -1 + SDL_SENSOR_UNKNOWN: Final[int] + SDL_SIZE_MAX: Final[int] + SDL_SURFACE_LOCKED: Final[int] + SDL_SURFACE_LOCK_NEEDED: Final[int] + SDL_SURFACE_PREALLOCATED: Final[int] + SDL_SURFACE_SIMD_ALIGNED: Final[int] + SDL_SYSTEM_CURSOR_COUNT: Final[int] + SDL_SYSTEM_CURSOR_CROSSHAIR: Final[int] + SDL_SYSTEM_CURSOR_DEFAULT: Final[int] + SDL_SYSTEM_CURSOR_EW_RESIZE: Final[int] + SDL_SYSTEM_CURSOR_E_RESIZE: Final[int] + SDL_SYSTEM_CURSOR_MOVE: Final[int] + SDL_SYSTEM_CURSOR_NESW_RESIZE: Final[int] + SDL_SYSTEM_CURSOR_NE_RESIZE: Final[int] + SDL_SYSTEM_CURSOR_NOT_ALLOWED: Final[int] + SDL_SYSTEM_CURSOR_NS_RESIZE: Final[int] + SDL_SYSTEM_CURSOR_NWSE_RESIZE: Final[int] + SDL_SYSTEM_CURSOR_NW_RESIZE: Final[int] + SDL_SYSTEM_CURSOR_N_RESIZE: Final[int] + SDL_SYSTEM_CURSOR_POINTER: Final[int] + SDL_SYSTEM_CURSOR_PROGRESS: Final[int] + SDL_SYSTEM_CURSOR_SE_RESIZE: Final[int] + SDL_SYSTEM_CURSOR_SW_RESIZE: Final[int] + SDL_SYSTEM_CURSOR_S_RESIZE: Final[int] + SDL_SYSTEM_CURSOR_TEXT: Final[int] + SDL_SYSTEM_CURSOR_WAIT: Final[int] + SDL_SYSTEM_CURSOR_W_RESIZE: Final[int] + SDL_SYSTEM_THEME_DARK: Final[int] + SDL_SYSTEM_THEME_LIGHT: Final[int] + SDL_SYSTEM_THEME_UNKNOWN: Final[int] + SDL_TEXTINPUT_TYPE_NUMBER: Final[int] + SDL_TEXTINPUT_TYPE_NUMBER_PASSWORD_HIDDEN: Final[int] + SDL_TEXTINPUT_TYPE_NUMBER_PASSWORD_VISIBLE: Final[int] + SDL_TEXTINPUT_TYPE_TEXT: Final[int] + SDL_TEXTINPUT_TYPE_TEXT_EMAIL: Final[int] + SDL_TEXTINPUT_TYPE_TEXT_NAME: Final[int] + SDL_TEXTINPUT_TYPE_TEXT_PASSWORD_HIDDEN: Final[int] + SDL_TEXTINPUT_TYPE_TEXT_PASSWORD_VISIBLE: Final[int] + SDL_TEXTINPUT_TYPE_TEXT_USERNAME: Final[int] + SDL_TEXTUREACCESS_STATIC: Final[int] + SDL_TEXTUREACCESS_STREAMING: Final[int] + SDL_TEXTUREACCESS_TARGET: Final[int] + SDL_THREAD_ALIVE: Final[int] + SDL_THREAD_COMPLETE: Final[int] + SDL_THREAD_DETACHED: Final[int] + SDL_THREAD_PRIORITY_HIGH: Final[int] + SDL_THREAD_PRIORITY_LOW: Final[int] + SDL_THREAD_PRIORITY_NORMAL: Final[int] + SDL_THREAD_PRIORITY_TIME_CRITICAL: Final[int] + SDL_THREAD_UNKNOWN: Final[int] + SDL_TIME_FORMAT_12HR: Final[Literal[1]] = 1 + SDL_TIME_FORMAT_24HR: Final[Literal[0]] = 0 + SDL_TOUCH_DEVICE_DIRECT: Final[int] + SDL_TOUCH_DEVICE_INDIRECT_ABSOLUTE: Final[int] + SDL_TOUCH_DEVICE_INDIRECT_RELATIVE: Final[int] + SDL_TOUCH_DEVICE_INVALID: Final[Literal[-1]] = -1 + SDL_TOUCH_MOUSEID: Final[int] + SDL_TRANSFER_CHARACTERISTICS_BT1361: Final[Literal[12]] = 12 + SDL_TRANSFER_CHARACTERISTICS_BT2020_10BIT: Final[Literal[14]] = 14 + SDL_TRANSFER_CHARACTERISTICS_BT2020_12BIT: Final[Literal[15]] = 15 + SDL_TRANSFER_CHARACTERISTICS_BT601: Final[Literal[6]] = 6 + SDL_TRANSFER_CHARACTERISTICS_BT709: Final[Literal[1]] = 1 + SDL_TRANSFER_CHARACTERISTICS_CUSTOM: Final[Literal[31]] = 31 + SDL_TRANSFER_CHARACTERISTICS_GAMMA22: Final[Literal[4]] = 4 + SDL_TRANSFER_CHARACTERISTICS_GAMMA28: Final[Literal[5]] = 5 + SDL_TRANSFER_CHARACTERISTICS_HLG: Final[Literal[18]] = 18 + SDL_TRANSFER_CHARACTERISTICS_IEC61966: Final[Literal[11]] = 11 + SDL_TRANSFER_CHARACTERISTICS_LINEAR: Final[Literal[8]] = 8 + SDL_TRANSFER_CHARACTERISTICS_LOG100: Final[Literal[9]] = 9 + SDL_TRANSFER_CHARACTERISTICS_LOG100_SQRT10: Final[Literal[10]] = 10 + SDL_TRANSFER_CHARACTERISTICS_PQ: Final[Literal[16]] = 16 + SDL_TRANSFER_CHARACTERISTICS_SMPTE240: Final[Literal[7]] = 7 + SDL_TRANSFER_CHARACTERISTICS_SMPTE428: Final[Literal[17]] = 17 + SDL_TRANSFER_CHARACTERISTICS_SRGB: Final[Literal[13]] = 13 + SDL_TRANSFER_CHARACTERISTICS_UNKNOWN: Final[Literal[0]] = 0 + SDL_TRANSFER_CHARACTERISTICS_UNSPECIFIED: Final[Literal[2]] = 2 + SDL_TRAYENTRY_BUTTON: Final[int] + SDL_TRAYENTRY_CHECKBOX: Final[int] + SDL_TRAYENTRY_CHECKED: Final[int] + SDL_TRAYENTRY_DISABLED: Final[int] + SDL_TRAYENTRY_SUBMENU: Final[int] + SDL_US_PER_SECOND: Final[int] + SDL_VERSION: Final[int] + SDL_WINDOWPOS_CENTERED: Final[int] + SDL_WINDOWPOS_CENTERED_MASK: Final[int] + SDL_WINDOWPOS_UNDEFINED: Final[int] + SDL_WINDOWPOS_UNDEFINED_MASK: Final[int] + SDL_WINDOW_ALWAYS_ON_TOP: Final[int] + SDL_WINDOW_BORDERLESS: Final[int] + SDL_WINDOW_EXTERNAL: Final[int] + SDL_WINDOW_FULLSCREEN: Final[int] + SDL_WINDOW_HIDDEN: Final[int] + SDL_WINDOW_HIGH_PIXEL_DENSITY: Final[int] + SDL_WINDOW_INPUT_FOCUS: Final[int] + SDL_WINDOW_KEYBOARD_GRABBED: Final[int] + SDL_WINDOW_MAXIMIZED: Final[int] + SDL_WINDOW_METAL: Final[int] + SDL_WINDOW_MINIMIZED: Final[int] + SDL_WINDOW_MODAL: Final[int] + SDL_WINDOW_MOUSE_CAPTURE: Final[int] + SDL_WINDOW_MOUSE_FOCUS: Final[int] + SDL_WINDOW_MOUSE_GRABBED: Final[int] + SDL_WINDOW_MOUSE_RELATIVE_MODE: Final[int] + SDL_WINDOW_NOT_FOCUSABLE: Final[int] + SDL_WINDOW_OCCLUDED: Final[int] + SDL_WINDOW_OPENGL: Final[int] + SDL_WINDOW_POPUP_MENU: Final[int] + SDL_WINDOW_RESIZABLE: Final[int] + SDL_WINDOW_SURFACE_VSYNC_ADAPTIVE: Final[int] + SDL_WINDOW_SURFACE_VSYNC_DISABLED: Final[int] + SDL_WINDOW_TOOLTIP: Final[int] + SDL_WINDOW_TRANSPARENT: Final[int] + SDL_WINDOW_UTILITY: Final[int] + SDL_WINDOW_VULKAN: Final[int] + TCODK_0: Final[int] + TCODK_1: Final[int] + TCODK_2: Final[int] + TCODK_3: Final[int] + TCODK_4: Final[int] + TCODK_5: Final[int] + TCODK_6: Final[int] + TCODK_7: Final[int] + TCODK_8: Final[int] + TCODK_9: Final[int] + TCODK_ALT: Final[int] + TCODK_APPS: Final[int] + TCODK_BACKSPACE: Final[int] + TCODK_CAPSLOCK: Final[int] + TCODK_CHAR: Final[int] + TCODK_CONTROL: Final[int] + TCODK_DELETE: Final[int] + TCODK_DOWN: Final[int] + TCODK_END: Final[int] + TCODK_ENTER: Final[int] + TCODK_ESCAPE: Final[int] + TCODK_F10: Final[int] + TCODK_F11: Final[int] + TCODK_F12: Final[int] + TCODK_F1: Final[int] + TCODK_F2: Final[int] + TCODK_F3: Final[int] + TCODK_F4: Final[int] + TCODK_F5: Final[int] + TCODK_F6: Final[int] + TCODK_F7: Final[int] + TCODK_F8: Final[int] + TCODK_F9: Final[int] + TCODK_HOME: Final[int] + TCODK_INSERT: Final[int] + TCODK_KP0: Final[int] + TCODK_KP1: Final[int] + TCODK_KP2: Final[int] + TCODK_KP3: Final[int] + TCODK_KP4: Final[int] + TCODK_KP5: Final[int] + TCODK_KP6: Final[int] + TCODK_KP7: Final[int] + TCODK_KP8: Final[int] + TCODK_KP9: Final[int] + TCODK_KPADD: Final[int] + TCODK_KPDEC: Final[int] + TCODK_KPDIV: Final[int] + TCODK_KPENTER: Final[int] + TCODK_KPMUL: Final[int] + TCODK_KPSUB: Final[int] + TCODK_LEFT: Final[int] + TCODK_LWIN: Final[int] + TCODK_NONE: Final[int] + TCODK_NUMLOCK: Final[int] + TCODK_PAGEDOWN: Final[int] + TCODK_PAGEUP: Final[int] + TCODK_PAUSE: Final[int] + TCODK_PRINTSCREEN: Final[int] + TCODK_RIGHT: Final[int] + TCODK_RWIN: Final[int] + TCODK_SCROLLLOCK: Final[int] + TCODK_SHIFT: Final[int] + TCODK_SPACE: Final[int] + TCODK_TAB: Final[int] + TCODK_TEXT: Final[int] + TCODK_UP: Final[int] + TCOD_BKGND_ADD: Final[int] + TCOD_BKGND_ADDA: Final[int] + TCOD_BKGND_ALPH: Final[int] + TCOD_BKGND_BURN: Final[int] + TCOD_BKGND_COLOR_BURN: Final[int] + TCOD_BKGND_COLOR_DODGE: Final[int] + TCOD_BKGND_DARKEN: Final[int] + TCOD_BKGND_DEFAULT: Final[int] + TCOD_BKGND_LIGHTEN: Final[int] + TCOD_BKGND_MULTIPLY: Final[int] + TCOD_BKGND_NONE: Final[int] + TCOD_BKGND_OVERLAY: Final[int] + TCOD_BKGND_SCREEN: Final[int] + TCOD_BKGND_SET: Final[int] + TCOD_CENTER: Final[int] + TCOD_CHAR_ARROW2_E: Final[Literal[16]] = 16 + TCOD_CHAR_ARROW2_N: Final[Literal[30]] = 30 + TCOD_CHAR_ARROW2_S: Final[Literal[31]] = 31 + TCOD_CHAR_ARROW2_W: Final[Literal[17]] = 17 + TCOD_CHAR_ARROW_E: Final[Literal[26]] = 26 + TCOD_CHAR_ARROW_N: Final[Literal[24]] = 24 + TCOD_CHAR_ARROW_S: Final[Literal[25]] = 25 + TCOD_CHAR_ARROW_W: Final[Literal[27]] = 27 + TCOD_CHAR_BLOCK1: Final[Literal[176]] = 176 + TCOD_CHAR_BLOCK2: Final[Literal[177]] = 177 + TCOD_CHAR_BLOCK3: Final[Literal[178]] = 178 + TCOD_CHAR_BULLET: Final[Literal[7]] = 7 + TCOD_CHAR_BULLET_INV: Final[Literal[8]] = 8 + TCOD_CHAR_BULLET_SQUARE: Final[Literal[254]] = 254 + TCOD_CHAR_CENT: Final[Literal[189]] = 189 + TCOD_CHAR_CHECKBOX_SET: Final[Literal[225]] = 225 + TCOD_CHAR_CHECKBOX_UNSET: Final[Literal[224]] = 224 + TCOD_CHAR_CLUB: Final[Literal[5]] = 5 + TCOD_CHAR_COPYRIGHT: Final[Literal[184]] = 184 + TCOD_CHAR_CROSS: Final[Literal[197]] = 197 + TCOD_CHAR_CURRENCY: Final[Literal[207]] = 207 + TCOD_CHAR_DARROW_H: Final[Literal[29]] = 29 + TCOD_CHAR_DARROW_V: Final[Literal[18]] = 18 + TCOD_CHAR_DCROSS: Final[Literal[206]] = 206 + TCOD_CHAR_DHLINE: Final[Literal[205]] = 205 + TCOD_CHAR_DIAMOND: Final[Literal[4]] = 4 + TCOD_CHAR_DIVISION: Final[Literal[246]] = 246 + TCOD_CHAR_DNE: Final[Literal[187]] = 187 + TCOD_CHAR_DNW: Final[Literal[201]] = 201 + TCOD_CHAR_DSE: Final[Literal[188]] = 188 + TCOD_CHAR_DSW: Final[Literal[200]] = 200 + TCOD_CHAR_DTEEE: Final[Literal[204]] = 204 + TCOD_CHAR_DTEEN: Final[Literal[202]] = 202 + TCOD_CHAR_DTEES: Final[Literal[203]] = 203 + TCOD_CHAR_DTEEW: Final[Literal[185]] = 185 + TCOD_CHAR_DVLINE: Final[Literal[186]] = 186 + TCOD_CHAR_EXCLAM_DOUBLE: Final[Literal[19]] = 19 + TCOD_CHAR_FEMALE: Final[Literal[12]] = 12 + TCOD_CHAR_FUNCTION: Final[Literal[159]] = 159 + TCOD_CHAR_GRADE: Final[Literal[248]] = 248 + TCOD_CHAR_HALF: Final[Literal[171]] = 171 + TCOD_CHAR_HEART: Final[Literal[3]] = 3 + TCOD_CHAR_HLINE: Final[Literal[196]] = 196 + TCOD_CHAR_LIGHT: Final[Literal[15]] = 15 + TCOD_CHAR_MALE: Final[Literal[11]] = 11 + TCOD_CHAR_MULTIPLICATION: Final[Literal[158]] = 158 + TCOD_CHAR_NE: Final[Literal[191]] = 191 + TCOD_CHAR_NOTE: Final[Literal[13]] = 13 + TCOD_CHAR_NOTE_DOUBLE: Final[Literal[14]] = 14 + TCOD_CHAR_NW: Final[Literal[218]] = 218 + TCOD_CHAR_ONE_QUARTER: Final[Literal[172]] = 172 + TCOD_CHAR_PILCROW: Final[Literal[20]] = 20 + TCOD_CHAR_POUND: Final[Literal[156]] = 156 + TCOD_CHAR_POW1: Final[Literal[251]] = 251 + TCOD_CHAR_POW2: Final[Literal[253]] = 253 + TCOD_CHAR_POW3: Final[Literal[252]] = 252 + TCOD_CHAR_RADIO_SET: Final[Literal[10]] = 10 + TCOD_CHAR_RADIO_UNSET: Final[Literal[9]] = 9 + TCOD_CHAR_RESERVED: Final[Literal[169]] = 169 + TCOD_CHAR_SE: Final[Literal[217]] = 217 + TCOD_CHAR_SECTION: Final[Literal[21]] = 21 + TCOD_CHAR_SMILIE: Final[Literal[1]] = 1 + TCOD_CHAR_SMILIE_INV: Final[Literal[2]] = 2 + TCOD_CHAR_SPADE: Final[Literal[6]] = 6 + TCOD_CHAR_SUBP_DIAG: Final[Literal[230]] = 230 + TCOD_CHAR_SUBP_E: Final[Literal[231]] = 231 + TCOD_CHAR_SUBP_N: Final[Literal[228]] = 228 + TCOD_CHAR_SUBP_NE: Final[Literal[227]] = 227 + TCOD_CHAR_SUBP_NW: Final[Literal[226]] = 226 + TCOD_CHAR_SUBP_SE: Final[Literal[229]] = 229 + TCOD_CHAR_SUBP_SW: Final[Literal[232]] = 232 + TCOD_CHAR_SW: Final[Literal[192]] = 192 + TCOD_CHAR_TEEE: Final[Literal[195]] = 195 + TCOD_CHAR_TEEN: Final[Literal[193]] = 193 + TCOD_CHAR_TEES: Final[Literal[194]] = 194 + TCOD_CHAR_TEEW: Final[Literal[180]] = 180 + TCOD_CHAR_THREE_QUARTERS: Final[Literal[243]] = 243 + TCOD_CHAR_UMLAUT: Final[Literal[249]] = 249 + TCOD_CHAR_VLINE: Final[Literal[179]] = 179 + TCOD_CHAR_YEN: Final[Literal[190]] = 190 + TCOD_COLCTRL_1: Final[Literal[1]] = 1 + TCOD_COLCTRL_2: Final[int] + TCOD_COLCTRL_3: Final[int] + TCOD_COLCTRL_4: Final[int] + TCOD_COLCTRL_5: Final[int] + TCOD_COLCTRL_BACK_RGB: Final[int] + TCOD_COLCTRL_FORE_RGB: Final[int] + TCOD_COLCTRL_NUMBER: Final[Literal[5]] = 5 + TCOD_COLCTRL_STOP: Final[int] + TCOD_COMPILEDVERSION: Final[int] + TCOD_DISTRIBUTION_GAUSSIAN: Final[int] + TCOD_DISTRIBUTION_GAUSSIAN_INVERSE: Final[int] + TCOD_DISTRIBUTION_GAUSSIAN_RANGE: Final[int] + TCOD_DISTRIBUTION_GAUSSIAN_RANGE_INVERSE: Final[int] + TCOD_DISTRIBUTION_LINEAR: Final[int] + TCOD_EVENT_ANY: Final[int] + TCOD_EVENT_FINGER: Final[int] + TCOD_EVENT_FINGER_MOVE: Final[Literal[32]] = 32 + TCOD_EVENT_FINGER_PRESS: Final[Literal[64]] = 64 + TCOD_EVENT_FINGER_RELEASE: Final[Literal[128]] = 128 + TCOD_EVENT_KEY: Final[int] + TCOD_EVENT_KEY_PRESS: Final[Literal[1]] = 1 + TCOD_EVENT_KEY_RELEASE: Final[Literal[2]] = 2 + TCOD_EVENT_MOUSE: Final[int] + TCOD_EVENT_MOUSE_MOVE: Final[Literal[4]] = 4 + TCOD_EVENT_MOUSE_PRESS: Final[Literal[8]] = 8 + TCOD_EVENT_MOUSE_RELEASE: Final[Literal[16]] = 16 + TCOD_EVENT_NONE: Final[Literal[0]] = 0 + TCOD_E_ERROR: Final[Literal[-1]] = -1 + TCOD_E_INVALID_ARGUMENT: Final[Literal[-2]] = -2 + TCOD_E_OK: Final[Literal[0]] = 0 + TCOD_E_OUT_OF_MEMORY: Final[Literal[-3]] = -3 + TCOD_E_REQUIRES_ATTENTION: Final[Literal[-4]] = -4 + TCOD_E_WARN: Final[Literal[1]] = 1 + TCOD_FALLBACK_FONT_SIZE: Final[Literal[16]] = 16 + TCOD_FONT_LAYOUT_ASCII_INCOL: Final[Literal[1]] = 1 + TCOD_FONT_LAYOUT_ASCII_INROW: Final[Literal[2]] = 2 + TCOD_FONT_LAYOUT_CP437: Final[Literal[16]] = 16 + TCOD_FONT_LAYOUT_TCOD: Final[Literal[8]] = 8 + TCOD_FONT_TYPE_GRAYSCALE: Final[Literal[4]] = 4 + TCOD_FONT_TYPE_GREYSCALE: Final[Literal[4]] = 4 + TCOD_KEY_PRESSED: Final[Literal[1]] = 1 + TCOD_KEY_RELEASED: Final[Literal[2]] = 2 + TCOD_KEY_TEXT_SIZE: Final[Literal[32]] = 32 + TCOD_LEFT: Final[int] + TCOD_LEX_CHAR: Final[Literal[7]] = 7 + TCOD_LEX_COMMENT: Final[Literal[9]] = 9 + TCOD_LEX_EOF: Final[Literal[8]] = 8 + TCOD_LEX_FLAG_NESTING_COMMENT: Final[Literal[2]] = 2 + TCOD_LEX_FLAG_NOCASE: Final[Literal[1]] = 1 + TCOD_LEX_FLAG_TOKENIZE_COMMENTS: Final[Literal[4]] = 4 + TCOD_LEX_FLOAT: Final[Literal[6]] = 6 + TCOD_LEX_IDEN: Final[Literal[3]] = 3 + TCOD_LEX_INTEGER: Final[Literal[5]] = 5 + TCOD_LEX_KEYWORD: Final[Literal[2]] = 2 + TCOD_LEX_KEYWORD_SIZE: Final[Literal[20]] = 20 + TCOD_LEX_MAX_KEYWORDS: Final[Literal[100]] = 100 + TCOD_LEX_MAX_SYMBOLS: Final[Literal[100]] = 100 + TCOD_LEX_STRING: Final[Literal[4]] = 4 + TCOD_LEX_SYMBOL: Final[Literal[1]] = 1 + TCOD_LEX_SYMBOL_SIZE: Final[Literal[5]] = 5 + TCOD_LEX_UNKNOWN: Final[Literal[0]] = 0 + TCOD_LOG_CRITICAL: Final[Literal[50]] = 50 + TCOD_LOG_DEBUG: Final[Literal[10]] = 10 + TCOD_LOG_ERROR: Final[Literal[40]] = 40 + TCOD_LOG_INFO: Final[Literal[20]] = 20 + TCOD_LOG_WARNING: Final[Literal[30]] = 30 + TCOD_MAJOR_VERSION: Final[Literal[2]] = 2 + TCOD_MINOR_VERSION: Final[Literal[2]] = 2 + TCOD_NB_RENDERERS: Final[int] + TCOD_NOISE_DEFAULT: Final[Literal[0]] = 0 + TCOD_NOISE_MAX_DIMENSIONS: Final[Literal[4]] = 4 + TCOD_NOISE_MAX_OCTAVES: Final[Literal[128]] = 128 + TCOD_NOISE_PERLIN: Final[Literal[1]] = 1 + TCOD_NOISE_SIMPLEX: Final[Literal[2]] = 2 + TCOD_NOISE_WAVELET: Final[Literal[4]] = 4 + TCOD_PATCHLEVEL: Final[Literal[1]] = 1 + TCOD_PATHFINDER_MAX_DIMENSIONS: Final[Literal[4]] = 4 + TCOD_RENDERER_GLSL: Final[int] + TCOD_RENDERER_OPENGL2: Final[int] + TCOD_RENDERER_OPENGL: Final[int] + TCOD_RENDERER_SDL2: Final[int] + TCOD_RENDERER_SDL: Final[int] + TCOD_RENDERER_XTERM: Final[int] + TCOD_RIGHT: Final[int] + TCOD_RNG_CMWC: Final[int] + TCOD_RNG_MT: Final[int] + TCOD_TYPE_BOOL: Final[int] + TCOD_TYPE_CHAR: Final[int] + TCOD_TYPE_COLOR: Final[int] + TCOD_TYPE_CUSTOM00: Final[int] + TCOD_TYPE_CUSTOM01: Final[int] + TCOD_TYPE_CUSTOM02: Final[int] + TCOD_TYPE_CUSTOM03: Final[int] + TCOD_TYPE_CUSTOM04: Final[int] + TCOD_TYPE_CUSTOM05: Final[int] + TCOD_TYPE_CUSTOM06: Final[int] + TCOD_TYPE_CUSTOM07: Final[int] + TCOD_TYPE_CUSTOM08: Final[int] + TCOD_TYPE_CUSTOM09: Final[int] + TCOD_TYPE_CUSTOM10: Final[int] + TCOD_TYPE_CUSTOM11: Final[int] + TCOD_TYPE_CUSTOM12: Final[int] + TCOD_TYPE_CUSTOM13: Final[int] + TCOD_TYPE_CUSTOM14: Final[int] + TCOD_TYPE_CUSTOM15: Final[int] + TCOD_TYPE_DICE: Final[int] + TCOD_TYPE_FLOAT: Final[int] + TCOD_TYPE_INT: Final[int] + TCOD_TYPE_LIST: Final[Literal[1024]] = 1024 + TCOD_TYPE_NONE: Final[int] + TCOD_TYPE_STRING: Final[int] + TCOD_TYPE_VALUELIST00: Final[int] + TCOD_TYPE_VALUELIST01: Final[int] + TCOD_TYPE_VALUELIST02: Final[int] + TCOD_TYPE_VALUELIST03: Final[int] + TCOD_TYPE_VALUELIST04: Final[int] + TCOD_TYPE_VALUELIST05: Final[int] + TCOD_TYPE_VALUELIST06: Final[int] + TCOD_TYPE_VALUELIST07: Final[int] + TCOD_TYPE_VALUELIST08: Final[int] + TCOD_TYPE_VALUELIST09: Final[int] + TCOD_TYPE_VALUELIST10: Final[int] + TCOD_TYPE_VALUELIST11: Final[int] + TCOD_TYPE_VALUELIST12: Final[int] + TCOD_TYPE_VALUELIST13: Final[int] + TCOD_TYPE_VALUELIST14: Final[int] + TCOD_TYPE_VALUELIST15: Final[int] + TCOD_ctx: Any + kNoiseImplementationFBM: Final[int] + kNoiseImplementationSimple: Final[int] + kNoiseImplementationTurbulence: Final[int] + np_float16: Final[int] + np_float32: Final[int] + np_float64: Final[int] + np_int16: Final[int] + np_int32: Final[int] + np_int64: Final[int] + np_int8: Final[int] + np_uint16: Final[int] + np_uint32: Final[int] + np_uint64: Final[int] + np_uint8: Final[int] + np_undefined: Final[Literal[0]] = 0 + +lib: _lib +ffi: Any diff --git a/tcod/bsp.py b/tcod/bsp.py index a53b8f67..21aa093d 100644 --- a/tcod/bsp.py +++ b/tcod/bsp.py @@ -24,14 +24,20 @@ else: print('Dig a room for %s.' % node) """ + from __future__ import annotations -from typing import Any, Iterator +from typing import TYPE_CHECKING, Any + +from typing_extensions import deprecated -import tcod.random -from tcod._internal import deprecate from tcod.cffi import ffi, lib +if TYPE_CHECKING: + from collections.abc import Iterator + + import tcod.random + class BSP: """A binary space partitioning tree which can be used for simple dungeon generation. @@ -71,7 +77,7 @@ def __init__(self, x: int, y: int, width: int, height: int) -> None: self.children: tuple[()] | tuple[BSP, BSP] = () @property - @deprecate("This attribute has been renamed to `width`.", FutureWarning) + @deprecated("This attribute has been renamed to `width`.", category=FutureWarning) def w(self) -> int: # noqa: D102 return self.width @@ -80,7 +86,7 @@ def w(self, value: int) -> None: self.width = value @property - @deprecate("This attribute has been renamed to `height`.", FutureWarning) + @deprecated("This attribute has been renamed to `height`.", category=FutureWarning) def h(self) -> int: # noqa: D102 return self.height @@ -88,7 +94,7 @@ def h(self) -> int: # noqa: D102 def h(self, value: int) -> None: self.height = value - def _as_cdata(self) -> Any: + def _as_cdata(self) -> Any: # noqa: ANN401 cdata = ffi.gc( lib.TCOD_bsp_new_with_size(self.x, self.y, self.width, self.height), lib.TCOD_bsp_delete, @@ -100,22 +106,11 @@ def __repr__(self) -> str: """Provide a useful readout when printed.""" status = "leaf" if self.children: - status = "split at position=%i,horizontal=%r" % ( - self.position, - self.horizontal, - ) - - return "<%s(x=%i,y=%i,width=%i,height=%i) level=%i %s>" % ( - self.__class__.__name__, - self.x, - self.y, - self.width, - self.height, - self.level, - status, - ) + status = f"split at position={self.position},horizontal={self.horizontal!r}" + + return f"<{self.__class__.__name__}(x={self.x},y={self.y},width={self.width},height={self.height}) level={self.level} {status}>" - def _unpack_bsp_tree(self, cdata: Any) -> None: + def _unpack_bsp_tree(self, cdata: Any) -> None: # noqa: ANN401 self.x = cdata.x self.y = cdata.y self.width = cdata.w @@ -131,7 +126,11 @@ def _unpack_bsp_tree(self, cdata: Any) -> None: self.children[1].parent = self self.children[1]._unpack_bsp_tree(lib.TCOD_bsp_right(cdata)) - def split_once(self, horizontal: bool, position: int) -> None: + def split_once( + self, + horizontal: bool, # noqa: FBT001 + position: int, + ) -> None: """Split this partition into 2 sub-partitions. Args: @@ -154,20 +153,17 @@ def split_recursive( # noqa: PLR0913 """Divide this partition recursively. Args: - depth (int): The maximum depth to divide this object recursively. - min_width (int): The minimum width of any individual partition. - min_height (int): The minimum height of any individual partition. - max_horizontal_ratio (float): - Prevent creating a horizontal ratio more extreme than this. - max_vertical_ratio (float): - Prevent creating a vertical ratio more extreme than this. - seed (Optional[tcod.random.Random]): - The random number generator to use. + depth: The maximum depth to divide this object recursively. + min_width: The minimum width of any individual partition. + min_height: The minimum height of any individual partition. + max_horizontal_ratio: Prevent creating a horizontal ratio more extreme than this. + max_vertical_ratio: Prevent creating a vertical ratio more extreme than this. + seed: The random number generator to use. """ cdata = self._as_cdata() lib.TCOD_bsp_split_recursive( cdata, - seed or ffi.NULL, + seed.random_c if seed is not None else ffi.NULL, depth, min_width, min_height, @@ -176,7 +172,7 @@ def split_recursive( # noqa: PLR0913 ) self._unpack_bsp_tree(cdata) - @deprecate("Use pre_order method instead of walk.") + @deprecated("Use pre_order method instead of walk.") def walk(self) -> Iterator[BSP]: """Iterate over this BSP's hierarchy in pre order. @@ -220,13 +216,13 @@ def level_order(self) -> Iterator[BSP]: .. versionadded:: 8.3 """ - next = [self] - while next: - level = next - next = [] + next_ = [self] + while next_: + level = next_ + next_ = [] yield from level for node in level: - next.extend(node.children) + next_.extend(node.children) def inverted_level_order(self) -> Iterator[BSP]: """Iterate over this BSP's hierarchy in inverse level order. @@ -234,13 +230,13 @@ def inverted_level_order(self) -> Iterator[BSP]: .. versionadded:: 8.3 """ levels: list[list[BSP]] = [] - next = [self] - while next: - levels.append(next) - level = next - next = [] + next_: list[BSP] = [self] + while next_: + levels.append(next_) + level = next_ + next_ = [] for node in level: - next.extend(node.children) + next_.extend(node.children) while levels: yield from levels.pop() diff --git a/tcod/cffi.h b/tcod/cffi.h index fc373dac..5f8fa494 100644 --- a/tcod/cffi.h +++ b/tcod/cffi.h @@ -9,4 +9,3 @@ #include "path.h" #include "random.h" #include "tcod.h" -#include "tdl.h" diff --git a/tcod/cffi.py b/tcod/cffi.py index d733abea..57f4039b 100644 --- a/tcod/cffi.py +++ b/tcod/cffi.py @@ -1,4 +1,5 @@ """This module handles loading of the libtcod cffi API.""" + from __future__ import annotations import logging @@ -6,7 +7,7 @@ import platform import sys from pathlib import Path -from typing import Any +from typing import Any, Literal import cffi @@ -14,17 +15,12 @@ __sdl_version__ = "" +REQUIRED_SDL_VERSION = (3, 2, 0) + ffi_check = cffi.FFI() ffi_check.cdef( """ -typedef struct SDL_version -{ - uint8_t major; - uint8_t minor; - uint8_t patch; -} SDL_version; - -void SDL_GetVersion(SDL_version * ver); +int SDL_GetVersion(void); """ ) @@ -32,24 +28,27 @@ def verify_dependencies() -> None: """Try to make sure dependencies exist on this system.""" if sys.platform == "win32": - lib_test: Any = ffi_check.dlopen("SDL2.dll") # Make sure SDL2.dll is here. - version: Any = ffi_check.new("struct SDL_version*") - lib_test.SDL_GetVersion(version) # Need to check this version. - version_tuple = version.major, version.minor, version.patch - if version_tuple < (2, 0, 5): + lib_test: Any = ffi_check.dlopen("SDL3.dll") # Make sure SDL3.dll is here. + int_version = lib_test.SDL_GetVersion() # Need to check this version. + major = int_version // 1000000 + minor = (int_version // 1000) % 1000 + patch = int_version % 1000 + version_tuple = major, minor, patch + if version_tuple < REQUIRED_SDL_VERSION: msg = f"Tried to load an old version of SDL {version_tuple!r}" raise RuntimeError(msg) -def get_architecture() -> str: - """Return the Windows architecture, one of "x86" or "x64".""" +def get_architecture() -> Literal["x86", "x64", "arm64"]: + """Return the Windows architecture.""" + if "(ARM64)" in sys.version: + return "arm64" return "x86" if platform.architecture()[0] == "32bit" else "x64" def get_sdl_version() -> str: - sdl_version = ffi.new("SDL_version*") - lib.SDL_GetVersion(sdl_version) - return f"{sdl_version.major}.{sdl_version.minor}.{sdl_version.patch}" + int_version = lib.SDL_GetVersion() + return f"{int_version // 1000000}.{(int_version // 1000) % 1000}.{int_version % 1000}" if sys.platform == "win32": @@ -58,13 +57,13 @@ def get_sdl_version() -> str: verify_dependencies() -from tcod._libtcod import ffi, lib # noqa +from tcod._libtcod import ffi, lib # noqa: E402 __sdl_version__ = get_sdl_version() -@ffi.def_extern() # type: ignore -def _libtcod_log_watcher(message: Any, userdata: None) -> None: # noqa: ANN401 +@ffi.def_extern() # type: ignore[untyped-decorator] +def _libtcod_log_watcher(message: Any, _userdata: None) -> None: # noqa: ANN401 text = str(ffi.string(message.message), encoding="utf-8") source = str(ffi.string(message.source), encoding="utf-8") level = int(message.level) @@ -75,4 +74,4 @@ def _libtcod_log_watcher(message: Any, userdata: None) -> None: # noqa: ANN401 lib.TCOD_set_log_callback(lib._libtcod_log_watcher, ffi.NULL) lib.TCOD_set_log_level(0) -__all__ = ["ffi", "lib", "__sdl_version__"] +__all__ = ["__sdl_version__", "ffi", "lib"] diff --git a/tcod/color.py b/tcod/color.py index dfd97b8c..bf9dabb3 100644 --- a/tcod/color.py +++ b/tcod/color.py @@ -1,14 +1,15 @@ """Old libtcod color management.""" + from __future__ import annotations import warnings -from typing import Any, List +from typing import Any from tcod._internal import deprecate from tcod.cffi import lib -class Color(List[int]): +class Color(list[int]): """Old-style libtcodpy color class. Args: @@ -17,7 +18,7 @@ class Color(List[int]): b (int): Blue value, from 0 to 255. """ - def __init__(self, r: int = 0, g: int = 0, b: int = 0) -> None: + def __init__(self, r: int = 0, g: int = 0, b: int = 0) -> None: # noqa: D107 list.__setitem__(self, slice(None), (r & 0xFF, g & 0xFF, b & 0xFF)) @property @@ -30,7 +31,7 @@ def r(self) -> int: return int(self[0]) @r.setter - @deprecate("Setting color attributes has been deprecated.", FutureWarning) + @deprecate("Setting color attributes has been deprecated.", category=FutureWarning) def r(self, value: int) -> None: self[0] = value & 0xFF @@ -44,7 +45,7 @@ def g(self) -> int: return int(self[1]) @g.setter - @deprecate("Setting color attributes has been deprecated.", FutureWarning) + @deprecate("Setting color attributes has been deprecated.", category=FutureWarning) def g(self, value: int) -> None: self[1] = value & 0xFF @@ -58,7 +59,7 @@ def b(self) -> int: return int(self[2]) @b.setter - @deprecate("Setting color attributes has been deprecated.", FutureWarning) + @deprecate("Setting color attributes has been deprecated.", category=FutureWarning) def b(self, value: int) -> None: self[2] = value & 0xFF @@ -81,14 +82,16 @@ def __getitem__(self, index: Any) -> Any: # noqa: ANN401 return super().__getitem__("rgb".index(index)) return super().__getitem__(index) - @deprecate("This class will not be mutable in the future.", FutureWarning) - def __setitem__(self, index: Any, value: Any) -> None: # noqa: ANN401 + @deprecate("This class will not be mutable in the future.", category=FutureWarning) + def __setitem__(self, index: Any, value: Any) -> None: # noqa: ANN401, D105 if isinstance(index, str): super().__setitem__("rgb".index(index), value) else: super().__setitem__(index, value) - def __eq__(self, other: Any) -> bool: + __hash__ = None + + def __eq__(self, other: object) -> bool: """Compare equality between colors. Also compares with standard sequences such as 3-item tuples or lists. @@ -98,8 +101,8 @@ def __eq__(self, other: Any) -> bool: except TypeError: return False - @deprecate("Use NumPy instead for color math operations.", FutureWarning) - def __add__(self, other: Any) -> Color: # type: ignore[override] + @deprecate("Use NumPy instead for color math operations.", category=FutureWarning) + def __add__(self, other: object) -> Color: # type: ignore[override] """Add two colors together. .. deprecated:: 9.2 @@ -107,8 +110,8 @@ def __add__(self, other: Any) -> Color: # type: ignore[override] """ return Color._new_from_cdata(lib.TCOD_color_add(self, other)) - @deprecate("Use NumPy instead for color math operations.", FutureWarning) - def __sub__(self, other: Any) -> Color: + @deprecate("Use NumPy instead for color math operations.", category=FutureWarning) + def __sub__(self, other: object) -> Color: """Subtract one color from another. .. deprecated:: 9.2 @@ -116,8 +119,8 @@ def __sub__(self, other: Any) -> Color: """ return Color._new_from_cdata(lib.TCOD_color_subtract(self, other)) - @deprecate("Use NumPy instead for color math operations.", FutureWarning) - def __mul__(self, other: Any) -> Color: + @deprecate("Use NumPy instead for color math operations.", category=FutureWarning) + def __mul__(self, other: object) -> Color: """Multiply with a scaler or another color. .. deprecated:: 9.2 @@ -125,7 +128,7 @@ def __mul__(self, other: Any) -> Color: """ if isinstance(other, (Color, list, tuple)): return Color._new_from_cdata(lib.TCOD_color_multiply(self, other)) - return Color._new_from_cdata(lib.TCOD_color_multiply_scalar(self, other)) + return Color._new_from_cdata(lib.TCOD_color_multiply_scalar(self, other)) # type: ignore[arg-type] def __repr__(self) -> str: """Return a printable representation of the current color.""" diff --git a/tcod/console.py b/tcod/console.py index 36cc0aba..bfcbce00 100644 --- a/tcod/console.py +++ b/tcod/console.py @@ -1,23 +1,31 @@ -"""Libtcod consoles are a strictly tile-based representation of text and color. +"""Libtcod tile-based Consoles and printing functions. + +Libtcod consoles are a strictly tile-based representation of colored glyphs/tiles. To render a console you need a tileset and a window to render to. See :ref:`getting-started` for info on how to set those up. """ + from __future__ import annotations import warnings -from os import PathLike from pathlib import Path -from typing import Any, Iterable +from typing import TYPE_CHECKING, Any, Literal, overload import numpy as np -from numpy.typing import NDArray -from typing_extensions import Literal +from typing_extensions import Self, deprecated import tcod._internal import tcod.constants -from tcod._internal import _check, deprecate +import tcod.image +from tcod._internal import _check, _path_encode from tcod.cffi import ffi, lib +if TYPE_CHECKING: + from collections.abc import Iterable + from os import PathLike + + from numpy.typing import ArrayLike, NDArray + def _fmt(string: str) -> bytes: """Return a string that escapes 'C printf' side effects.""" @@ -26,7 +34,7 @@ def _fmt(string: str) -> bytes: _root_console = None -rgba_graphic = np.dtype([("ch", np.intc), ("fg", "4B"), ("bg", "4B")]) +rgba_graphic: np.dtype[Any] = np.dtype([("ch", np.intc), ("fg", "4B"), ("bg", "4B")]) """A NumPy :any:`dtype` compatible with :any:`Console.rgba`. This dtype is: ``np.dtype([("ch", np.intc), ("fg", "4B"), ("bg", "4B")])`` @@ -34,7 +42,7 @@ def _fmt(string: str) -> bytes: .. versionadded:: 12.3 """ -rgb_graphic = np.dtype([("ch", np.intc), ("fg", "3B"), ("bg", "3B")]) +rgb_graphic: np.dtype[Any] = np.dtype([("ch", np.intc), ("fg", "3B"), ("bg", "3B")]) """A NumPy :any:`dtype` compatible with :any:`Console.rgb`. This dtype is: ``np.dtype([("ch", np.intc), ("fg", "3B"), ("bg", "3B")])`` @@ -101,7 +109,7 @@ class Console: DTYPE = rgba_graphic # A structured array type with the added "fg_rgb" and "bg_rgb" fields. - _DTYPE_RGB = np.dtype( + _DTYPE_RGB: np.dtype[Any] = np.dtype( { "names": ["ch", "fg", "bg"], "formats": [np.int32, "3u1", "3u1"], @@ -117,8 +125,9 @@ def __init__( order: Literal["C", "F"] = "C", buffer: NDArray[Any] | None = None, ) -> None: + """Initialize the console.""" self._key_color: tuple[int, int, int] | None = None - self._order = tcod._internal.verify_order(order) + self._order: Literal["C", "F"] = tcod._internal.verify_order(order) if buffer is not None: if self._order == "F": buffer = buffer.transpose() @@ -166,7 +175,7 @@ def _get_root(cls, order: Literal["C", "F"] | None = None) -> Console: This function will also update an already active root console. """ - global _root_console + global _root_console # noqa: PLW0603 if _root_console is None: _root_console = object.__new__(cls) self: Console = _root_console @@ -178,7 +187,7 @@ def _get_root(cls, order: Literal["C", "F"] | None = None) -> Console: def _init_setup_console_data(self, order: Literal["C", "F"] = "C") -> None: """Setup numpy arrays over libtcod data buffers.""" - global _root_console + global _root_console # noqa: PLW0603 self._key_color = None if self.console_c == ffi.NULL: _root_console = self @@ -186,7 +195,7 @@ def _init_setup_console_data(self, order: Literal["C", "F"] = "C") -> None: else: self._console_data = ffi.cast("struct TCOD_Console*", self.console_c) - self._tiles: NDArray[Any] = np.frombuffer( # type: ignore + self._tiles = np.frombuffer( ffi.buffer(self._console_data.tiles[0 : self.width * self.height]), dtype=self.DTYPE, ).reshape((self.height, self.width)) @@ -196,12 +205,12 @@ def _init_setup_console_data(self, order: Literal["C", "F"] = "C") -> None: @property def width(self) -> int: """The width of this Console.""" - return lib.TCOD_console_get_width(self.console_c) # type: ignore + return int(lib.TCOD_console_get_width(self.console_c)) @property def height(self) -> int: """The height of this Console.""" - return lib.TCOD_console_get_height(self.console_c) # type: ignore + return int(lib.TCOD_console_get_height(self.console_c)) @property def bg(self) -> NDArray[np.uint8]: @@ -244,7 +253,7 @@ def ch(self) -> NDArray[np.intc]: return self._tiles["ch"].T if self._order == "F" else self._tiles["ch"] @property - @deprecate("This attribute has been renamed to `rgba`.", category=FutureWarning) + @deprecated("This attribute has been renamed to `rgba`.", category=FutureWarning) def tiles(self) -> NDArray[Any]: """An array of this consoles raw tile data. @@ -260,7 +269,7 @@ def tiles(self) -> NDArray[Any]: return self.rgba @property - @deprecate("This attribute has been renamed to `rgba`.", category=FutureWarning) + @deprecated("This attribute has been renamed to `rgba`.", category=FutureWarning) def buffer(self) -> NDArray[Any]: """An array of this consoles raw tile data. @@ -272,7 +281,7 @@ def buffer(self) -> NDArray[Any]: return self.rgba @property - @deprecate("This attribute has been renamed to `rgb`.", category=FutureWarning) + @deprecated("This attribute has been renamed to `rgb`.", category=FutureWarning) def tiles_rgb(self) -> NDArray[Any]: """An array of this consoles data without the alpha channel. @@ -284,7 +293,7 @@ def tiles_rgb(self) -> NDArray[Any]: return self.rgb @property - @deprecate("This attribute has been renamed to `rgb`.", category=FutureWarning) + @deprecated("This attribute has been renamed to `rgb`.", category=FutureWarning) def tiles2(self) -> NDArray[Any]: """This name is deprecated in favour of :any:`rgb`. @@ -310,8 +319,8 @@ def rgba(self) -> NDArray[Any]: ... (*WHITE, 255), ... (*BLACK, 255), ... ) - >>> con.rgba[0, 0] - (88, [255, 255, 255, 255], [ 0, 0, 0, 255]) + >>> print(f"{con.rgba[0, 0]=}") + con.rgba[0, 0]=...(88, [255, 255, 255, 255], [ 0, 0, 0, 255])... .. versionadded:: 12.3 """ @@ -327,59 +336,119 @@ def rgb(self) -> NDArray[Any]: The :any:`rgb_graphic` dtype can be used to make arrays compatible with this attribute that are independent of a :any:`Console`. + Example: + >>> tile_graphics = np.array( # Tile graphics lookup table + ... [ # (Unicode, foreground, background) + ... (ord("."), (255, 255, 255), (0, 0, 0)), # Tile 0 + ... (ord("#"), (255, 255, 255), (0, 0, 0)), # Tile 1 + ... (ord("^"), (255, 255, 255), (0, 0, 0)), # Tile 2 + ... (ord("~"), (255, 255, 255), (0, 0, 0)), # Tile 3 + ... ], + ... dtype=tcod.console.rgb_graphic, + ... ) + >>> console = tcod.console.Console(6, 5) + >>> console.rgb[:] = tile_graphics[ # Convert 2D array of indexes to tile graphics + ... [ + ... [1, 1, 1, 1, 1, 1], + ... [1, 0, 2, 0, 0, 1], + ... [1, 0, 0, 3, 3, 1], + ... [1, 0, 0, 3, 3, 1], + ... [1, 1, 1, 1, 1, 1], + ... ], + ... ] + >>> print(console) + <###### + #.^..# + #..~~# + #..~~# + ######> + Example: >>> con = tcod.console.Console(10, 2) >>> BLUE, YELLOW, BLACK = (0, 0, 255), (255, 255, 0), (0, 0, 0) >>> con.rgb[0, 0] = ord("@"), YELLOW, BLACK - >>> con.rgb[0, 0] - (64, [255, 255, 0], [0, 0, 0]) + >>> print(f"{con.rgb[0, 0]=}") + con.rgb[0, 0]=...(64, [255, 255, 0], [0, 0, 0])... >>> con.rgb["bg"] = BLUE - >>> con.rgb[0, 0] - (64, [255, 255, 0], [ 0, 0, 255]) + >>> print(f"{con.rgb[0, 0]=}") + con.rgb[0, 0]=...(64, [255, 255, 0], [ 0, 0, 255])... .. versionadded:: 12.3 """ return self.rgba.view(self._DTYPE_RGB) - @property + _DEPRECATE_CONSOLE_DEFAULTS_MSG = """Console defaults have been deprecated. +Consider one of the following: + + # Set parameters once then pass them as kwargs + DEFAULT_COLOR = {"bg": (0, 0, 127), "fg": (127, 127, 255)} + console.print(x, y, string, **DEFAULT_COLOR) + + # Clear the console to a color and then skip setting colors on printing/drawing + console.clear(fg=(127, 127, 255), bg=(0, 0, 127)) + console.print(x, y, string, fg=None) +""" + + @property # Getters used internally, so only deprecate the setters. def default_bg(self) -> tuple[int, int, int]: - """Tuple[int, int, int]: The default background color.""" + """Tuple[int, int, int]: The default background color. + + .. deprecated:: 8.5 + These should not be used. Prefer passing defaults as kwargs. + + .. code-block:: + + DEFAULT_COLOR = {"bg": (0, 0, 127), "fg": (127, 127, 255)} + console.print(x, y, string, **DEFAULT_COLOR) + """ color = self._console_data.back return color.r, color.g, color.b @default_bg.setter - @deprecate("Console defaults have been deprecated.", category=FutureWarning) + @deprecated(_DEPRECATE_CONSOLE_DEFAULTS_MSG, category=FutureWarning) def default_bg(self, color: tuple[int, int, int]) -> None: self._console_data.back = color @property def default_fg(self) -> tuple[int, int, int]: - """Tuple[int, int, int]: The default foreground color.""" + """Tuple[int, int, int]: The default foreground color. + + .. deprecated:: 8.5 + These should not be used. Prefer passing defaults as kwargs. + """ color = self._console_data.fore return color.r, color.g, color.b @default_fg.setter - @deprecate("Console defaults have been deprecated.", category=FutureWarning) + @deprecated(_DEPRECATE_CONSOLE_DEFAULTS_MSG, category=FutureWarning) def default_fg(self, color: tuple[int, int, int]) -> None: self._console_data.fore = color @property def default_bg_blend(self) -> int: - """int: The default blending mode.""" - return self._console_data.bkgnd_flag # type: ignore + """int: The default blending mode. + + .. deprecated:: 8.5 + These should not be used. Prefer passing defaults as kwargs. + """ + return int(self._console_data.bkgnd_flag) @default_bg_blend.setter - @deprecate("Console defaults have been deprecated.", category=FutureWarning) + @deprecated(_DEPRECATE_CONSOLE_DEFAULTS_MSG, category=FutureWarning) def default_bg_blend(self, value: int) -> None: self._console_data.bkgnd_flag = value @property def default_alignment(self) -> int: - """int: The default text alignment.""" - return self._console_data.alignment # type: ignore + """int: The default text alignment. + + .. deprecated:: 8.5 + These should not be used. Prefer passing defaults as kwargs. + """ + return int(self._console_data.alignment) @default_alignment.setter - @deprecate("Console defaults have been deprecated.", category=FutureWarning) + @deprecated(_DEPRECATE_CONSOLE_DEFAULTS_MSG, category=FutureWarning) def default_alignment(self, value: int) -> None: self._console_data.alignment = value @@ -387,15 +456,15 @@ def __clear_warning(self, name: str, value: tuple[int, int, int]) -> None: """Raise a warning for bad default values during calls to clear.""" warnings.warn( f"Clearing with the console default values is deprecated.\nAdd {name}={value!r} to this call.", - DeprecationWarning, + FutureWarning, stacklevel=3, ) def clear( self, ch: int = 0x20, - fg: tuple[int, int, int] = ..., # type: ignore - bg: tuple[int, int, int] = ..., # type: ignore + fg: tuple[int, int, int] = ..., # type: ignore[assignment] + bg: tuple[int, int, int] = ..., # type: ignore[assignment] ) -> None: """Reset all values in this console to a single value. @@ -414,11 +483,11 @@ def clear( Added the `ch`, `fg`, and `bg` parameters. Non-white-on-black default values are deprecated. """ - if fg is ...: # type: ignore + if fg is ...: # type: ignore[comparison-overlap] fg = self.default_fg if fg != (255, 255, 255): self.__clear_warning("fg", fg) - if bg is ...: # type: ignore + if bg is ...: # type: ignore[comparison-overlap] bg = self.default_bg if bg != (0, 0, 0): self.__clear_warning("bg", bg) @@ -441,24 +510,28 @@ def put_char( """ lib.TCOD_console_put_char(self.console_c, x, y, ch, bg_blend) - __ALIGNMENT_LOOKUP = {0: "tcod.LEFT", 1: "tcod.RIGHT", 2: "tcod.CENTER"} - - __BG_BLEND_LOOKUP = { - 0: "tcod.BKGND_NONE", - 1: "tcod.BKGND_SET", - 2: "tcod.BKGND_MULTIPLY", - 3: "tcod.BKGND_LIGHTEN", - 4: "tcod.BKGND_DARKEN", - 5: "tcod.BKGND_SCREEN", - 6: "tcod.BKGND_COLOR_DODGE", - 7: "tcod.BKGND_COLOR_BURN", - 8: "tcod.BKGND_ADD", - 9: "tcod.BKGND_ADDA", - 10: "tcod.BKGND_BURN", - 11: "tcod.BKGND_OVERLAY", - 12: "tcod.BKGND_ALPH", - 13: "tcod.BKGND_DEFAULT", - } + __ALIGNMENT_LOOKUP = ( + "tcod.LEFT", + "tcod.RIGHT", + "tcod.CENTER", + ) + + __BG_BLEND_LOOKUP = ( + "tcod.BKGND_NONE", + "tcod.BKGND_SET", + "tcod.BKGND_MULTIPLY", + "tcod.BKGND_LIGHTEN", + "tcod.BKGND_DARKEN", + "tcod.BKGND_SCREEN", + "tcod.BKGND_COLOR_DODGE", + "tcod.BKGND_COLOR_BURN", + "tcod.BKGND_ADD", + "tcod.BKGND_ADDA", + "tcod.BKGND_BURN", + "tcod.BKGND_OVERLAY", + "tcod.BKGND_ALPH", + "tcod.BKGND_DEFAULT", + ) def __deprecate_defaults( # noqa: C901, PLR0912 self, @@ -511,6 +584,7 @@ def __deprecate_defaults( # noqa: C901, PLR0912 stacklevel=3, ) + @deprecated("Switch to using keywords and then replace with 'console.print(...)'") def print_( self, x: int, @@ -538,7 +612,8 @@ def print_( alignment = self.default_alignment if alignment is None else alignment lib.TCOD_console_printf_ex(self.console_c, x, y, bg_blend, alignment, _fmt(string)) - def print_rect( + @deprecated("Switch to using keywords and then replace with 'console.print(...)'") + def print_rect( # noqa: PLR0913 self, x: int, y: int, @@ -604,13 +679,14 @@ def get_height_rect(self, x: int, y: int, width: int, height: int, string: str) string_ = string.encode("utf-8") return int(lib.TCOD_console_get_height_rect_n(self.console_c, x, y, width, height, len(string_), string_)) - def rect( + @deprecated("""Replace with 'console.draw_rect(x, y, width, height, ch=..., fg=..., bg=..., bg_blend=...)'""") + def rect( # noqa: PLR0913 self, x: int, y: int, width: int, height: int, - clear: bool, + clear: bool, # noqa: FBT001 bg_blend: int = tcod.constants.BKGND_DEFAULT, ) -> None: """Draw a the background color on a rect optionally clearing the text. @@ -636,6 +712,9 @@ def rect( self.__deprecate_defaults("draw_rect", bg_blend, clear=bool(clear)) lib.TCOD_console_rect(self.console_c, x, y, width, height, clear, bg_blend) + @deprecated( + """Replace with 'console.draw_rect(x, y, width=width, height=1, ch=ord("─"), fg=..., bg=..., bg_blend=...)'""" + ) def hline( self, x: int, @@ -645,7 +724,7 @@ def hline( ) -> None: """Draw a horizontal line on the console. - This always uses ord('─'), the horizontal line character. + This always uses ord("─"), the horizontal line character. Args: x (int): The x coordinate from the left. @@ -663,6 +742,9 @@ def hline( self.__deprecate_defaults("draw_rect", bg_blend) lib.TCOD_console_hline(self.console_c, x, y, width, bg_blend) + @deprecated( + """Replace with 'console.draw_rect(x, y, width=1, height=height, ch=ord("│"), fg=..., bg=..., bg_blend=...)'""" + ) def vline( self, x: int, @@ -672,7 +754,7 @@ def vline( ) -> None: """Draw a vertical line on the console. - This always uses ord('│'), the vertical line character. + This always uses ord("│"), the vertical line character. Args: x (int): The x coordinate from the left. @@ -690,14 +772,15 @@ def vline( self.__deprecate_defaults("draw_rect", bg_blend) lib.TCOD_console_vline(self.console_c, x, y, height, bg_blend) - def print_frame( + @deprecated("Replace with 'console.draw_frame(...)', use a separate print call for frame titles") + def print_frame( # noqa: PLR0913 self, x: int, y: int, width: int, height: int, string: str = "", - clear: bool = True, + clear: bool = True, # noqa: FBT001, FBT002 bg_blend: int = tcod.constants.BKGND_DEFAULT, ) -> None: """Draw a framed rectangle with optional text. @@ -729,10 +812,10 @@ def print_frame( explicit. """ self.__deprecate_defaults("draw_frame", bg_blend) - string = _fmt(string) if string else ffi.NULL - _check(lib.TCOD_console_printf_frame(self.console_c, x, y, width, height, clear, bg_blend, string)) + string_: Any = _fmt(string) if string else ffi.NULL + _check(lib.TCOD_console_printf_frame(self.console_c, x, y, width, height, clear, bg_blend, string_)) - def blit( + def blit( # noqa: PLR0913 self, dest: Console, dest_x: int = 0, @@ -778,11 +861,11 @@ def blit( # The old syntax is easy to detect and correct. if hasattr(src_y, "console_c"): (src_x, src_y, width, height, dest, dest_x, dest_y) = ( - dest, # type: ignore + dest, # type: ignore[assignment] dest_x, dest_y, src_x, - src_y, # type: ignore + src_y, # type: ignore[assignment] width, height, ) @@ -822,7 +905,7 @@ def blit( bg_alpha, ) - @deprecate("Pass the key color to Console.blit instead of calling this function.") + @deprecated("Pass the key color to Console.blit instead of calling this function.") def set_key_color(self, color: tuple[int, int, int] | None) -> None: """Set a consoles blit transparent color. @@ -834,7 +917,7 @@ def set_key_color(self, color: tuple[int, int, int] | None) -> None: """ self._key_color = color - def __enter__(self) -> Console: + def __enter__(self) -> Self: """Return this console in a managed context. When the root console is used as a context, the graphical window will @@ -865,7 +948,7 @@ def close(self) -> None: raise NotImplementedError(msg) lib.TCOD_console_delete(self.console_c) - def __exit__(self, *args: Any) -> None: + def __exit__(self, *_: object) -> None: """Close the graphical window on exit. Some tcod functions may have undefined behavior after this point. @@ -880,6 +963,7 @@ def __bool__(self) -> bool: return bool(self.console_c != ffi.NULL) def __getstate__(self) -> dict[str, Any]: + """Support serialization via :mod:`pickle`.""" state = self.__dict__.copy() del state["console_c"] state["_console_data"] = { @@ -895,6 +979,7 @@ def __getstate__(self) -> dict[str, Any]: return state def __setstate__(self, state: dict[str, Any]) -> None: + """Support serialization via :mod:`pickle`.""" self._key_color = None if "_tiles" not in state: tiles: NDArray[Any] = np.ndarray((self.height, self.width), dtype=self.DTYPE) @@ -914,67 +999,193 @@ def __setstate__(self, state: dict[str, Any]) -> None: def __repr__(self) -> str: """Return a string representation of this console.""" - return "tcod.console.Console(width=%i, height=%i, " "order=%r,buffer=\n%r)" % ( - self.width, - self.height, - self._order, - self.rgba, - ) + return f"tcod.console.Console(width={self.width}, height={self.height}, order={self._order!r},buffer=\n{self.rgba!r})" def __str__(self) -> str: """Return a simplified representation of this consoles contents.""" - return "<%s>" % "\n ".join("".join(chr(c) for c in line) for line in self._tiles["ch"]) + return "<{}>".format("\n ".join("".join(chr(c) for c in line) for line in self._tiles["ch"])) + @overload def print( self, x: int, y: int, + text: str, + *, + width: int | None = None, + height: int | None = None, + fg: tuple[int, int, int] | None = None, + bg: tuple[int, int, int] | None = None, + bg_blend: int = tcod.constants.BKGND_SET, + alignment: int = tcod.constants.LEFT, + ) -> int: ... + + @overload + @deprecated( + "Replace text, fg, bg, bg_blend, and alignment with keyword arguments." + "\n'string' keyword should be renamed to `text`" + ) + def print( + self, + x: int, + y: int, + text: str, + fg: tuple[int, int, int] | None = None, + bg: tuple[int, int, int] | None = None, + bg_blend: int = tcod.constants.BKGND_SET, + alignment: int = tcod.constants.LEFT, + *, + string: str = "", + ) -> int: ... + + @overload + @deprecated( + "Replace text, fg, bg, bg_blend, and alignment with keyword arguments." + "\n'string' keyword should be renamed to `text`" + ) + def print( + self, + x: int, + y: int, + text: str = "", + fg: tuple[int, int, int] | None = None, + bg: tuple[int, int, int] | None = None, + bg_blend: int = tcod.constants.BKGND_SET, + alignment: int = tcod.constants.LEFT, + *, string: str, + ) -> int: ... + + def print( # noqa: PLR0913 + self, + x: int, + y: int, + text: str = "", fg: tuple[int, int, int] | None = None, bg: tuple[int, int, int] | None = None, bg_blend: int = tcod.constants.BKGND_SET, alignment: int = tcod.constants.LEFT, - ) -> None: - r"""Print a string on a console with manual line breaks. + *, + width: int | None = None, + height: int | None = None, + string: str = "", + ) -> int: + r"""Print a string of Unicode text on this console. - `x` and `y` are the starting tile, with ``0,0`` as the upper-left - corner of the console. + Prefer using keywords for this method call to avoid ambiguous parameters. - `string` is a Unicode string which may include color control - characters. Strings which are too long will be truncated until the - next newline character ``"\n"``. + Args: + x: Starting X coordinate, with the left-most tile as zero. + y: Starting Y coordinate, with the top-most tile as zero. + text: A Unicode string which may include color control characters. + width: Width in tiles to constrain the printing region. + If a `width` is given then `text` will have automatic word wrapping applied to it. + A `width` of `None` means `text` will only have manual line breaks. + height: Height in tiles to constrain the printing region. + fg: RGB tuple to use as the foreground color, or `None` to leave the foreground unchanged. + Tuple values should be 0-255. + Must be given as a keyword argument. + bg: RGB tuple to use as the background color, or `None` to leave the foreground unchanged. + Tuple values should be 0-255. + Must be given as a keyword argument. + bg_blend: Background blend type used by libtcod. + Typically starts with `libtcodpy.BKGND_*`. + Must be given as a keyword argument. + alignment: One of `libtcodpy.LEFT`, `libtcodpy.CENTER`, or `libtcodpy.RIGHT` + Must be given as a keyword argument. + string: Older deprecated name of the `text` parameter. - `fg` and `bg` are the foreground text color and background tile color - respectfully. This is a 3-item tuple with (r, g, b) color values from - 0 to 255. These parameters can also be set to `None` to leave the - colors unchanged. + Returns: + The height of `text` in lines via word wrapping and line breaks. - `bg_blend` is the blend type used by libtcod. + Example:: - `alignment` can be `tcod.LEFT`, `tcod.CENTER`, or `tcod.RIGHT`. + >>> from tcod import libtcodpy + >>> console = tcod.console.Console(20, 1) + >>> console.clear(ch=ord('·')) + >>> console.print(x=0, y=0, text="left") + 1 + >>> console.print(x=console.width, y=0, text="right", alignment=libtcodpy.RIGHT) + 1 + >>> console.print(x=10, y=0, text="center", alignment=libtcodpy.CENTER) + 1 + >>> print(console) + + + >>> console = tcod.console.Console(20, 4) + >>> console.clear(ch=ord('·')) + >>> console.print(x=1, y=1, text="words within bounds", width=8) + 3 + >>> print(console) + <···················· + ·words·············· + ·within············· + ·bounds·············> + >>> WHITE = (255, 255, 255) + >>> BLACK = (0, 0, 0) + >>> console.print(x=0, y=0, text="Black text on white background", fg=BLACK, bg=WHITE) + 1 .. versionadded:: 8.5 .. versionchanged:: 9.0 + `fg` and `bg` now default to `None` instead of white-on-black. .. versionchanged:: 13.0 + `x` and `y` are now always used as an absolute position for negative values. + + .. versionchanged:: 18.0 + + Deprecated giving `string`, `fg`, `bg`, and `bg_blend` as positional arguments. + + Added `text` parameter to replace `string`. + + Added `width` and `height` keyword parameters to bind text to a rectangle and replace other print functions. + Right-aligned text with `width=None` now treats the `x` coordinate as a past-the-end index, this will shift + the text of older calls to the left by 1 tile. + + Now returns the number of lines printed via word wrapping, + same as previous print functions bound to rectangles. """ - string_ = string.encode("utf-8") - lib.TCOD_console_printn( - self.console_c, - x, - y, - len(string_), - string_, - (fg,) if fg is not None else ffi.NULL, - (bg,) if bg is not None else ffi.NULL, - bg_blend, - alignment, + if width is not None and width <= 0: + return 0 + if width is None and alignment == tcod.constants.LEFT: # Fix alignment + width = 0x100000 + if width is None and alignment == tcod.constants.CENTER: # Fix center alignment + x -= 0x100000 + width = 0x200000 + if width is None and alignment == tcod.constants.RIGHT: # Fix right alignment + x -= 0x100000 + width = 0x100000 + rgb_fg = ffi.new("TCOD_ColorRGB*", fg) if fg is not None else ffi.NULL + rgb_bg = ffi.new("TCOD_ColorRGB*", bg) if bg is not None else ffi.NULL + utf8 = (string or text).encode("utf-8") + return _check( + int( + lib.TCOD_printn_rgb( + self.console_c, + { + "x": x, + "y": y, + "width": width or 0, + "height": height or 0, + "fg": rgb_fg, + "bg": rgb_bg, + "flag": bg_blend, + "alignment": alignment, + }, + len(utf8), + utf8, + ) + ) ) - def print_box( + @deprecated( + "Switch parameters to keywords, then replace method with 'console.print(...)', then replace 'string=' with 'text='" + ) + def print_box( # noqa: PLR0913 self, x: int, y: int, @@ -1015,6 +1226,9 @@ def print_box( .. versionchanged:: 13.0 `x` and `y` are now always used as an absolute position for negative values. + + .. deprecated:: 18.0 + This method was replaced by more functional :any:`Console.print` method. """ string_ = string.encode("utf-8") return int( @@ -1033,17 +1247,49 @@ def print_box( ) ) + @overload def draw_frame( self, x: int, y: int, width: int, height: int, - title: str = "", + *, clear: bool = True, fg: tuple[int, int, int] | None = None, bg: tuple[int, int, int] | None = None, bg_blend: int = tcod.constants.BKGND_SET, + decoration: str | tuple[int, int, int, int, int, int, int, int, int] = "┌─┐│ │└─┘", + ) -> None: ... + + @overload + @deprecated("Parameters clear, fg, bg, bg_blend should be keyword arguments. Remove title parameter") + def draw_frame( + self, + x: int, + y: int, + width: int, + height: int, + title: str = "", + clear: bool = True, # noqa: FBT001, FBT002 + fg: tuple[int, int, int] | None = None, + bg: tuple[int, int, int] | None = None, + bg_blend: int = tcod.constants.BKGND_SET, + *, + decoration: str | tuple[int, int, int, int, int, int, int, int, int] = "┌─┐│ │└─┘", + ) -> None: ... + + def draw_frame( # noqa: PLR0913 + self, + x: int, + y: int, + width: int, + height: int, + title: str = "", + clear: bool = True, # noqa: FBT001, FBT002 + fg: tuple[int, int, int] | None = None, + bg: tuple[int, int, int] | None = None, + bg_blend: int = tcod.constants.BKGND_SET, *, decoration: str | tuple[int, int, int, int, int, int, int, int, int] = "┌─┐│ │└─┘", ) -> None: @@ -1061,13 +1307,16 @@ def draw_frame( border with :any:`Console.print_box` using your own style. If `clear` is True than the region inside of the frame will be cleared. + Must be given as a keyword argument. `fg` and `bg` are the foreground and background colors for the frame border. This is a 3-item tuple with (r, g, b) color values from 0 to 255. These parameters can also be set to `None` to leave the colors unchanged. + Must be given as a keyword argument. `bg_blend` is the blend type used by libtcod. + Must be given as a keyword argument. `decoration` is a sequence of glyphs to use for rendering the borders. This a str or tuple of int's with 9 items with the items arranged in @@ -1087,17 +1336,22 @@ def draw_frame( .. versionchanged:: 13.0 `x` and `y` are now always used as an absolute position for negative values. + .. versionchanged:: 18.0 + Deprecated `clear`, `fg`, `bg`, and `bg_blend` being given as positional arguments. + These should be keyword arguments only. + Example:: + >>> from tcod import libtcodpy >>> console = tcod.console.Console(12, 6) >>> console.draw_frame(x=0, y=0, width=3, height=3) >>> console.draw_frame(x=3, y=0, width=3, height=3, decoration="╔═╗║ ║╚═╝") >>> console.draw_frame(x=6, y=0, width=3, height=3, decoration="123456789") >>> console.draw_frame(x=9, y=0, width=3, height=3, decoration="/-\\| |\\-/") >>> console.draw_frame(x=0, y=3, width=12, height=3) - >>> console.print_box(x=0, y=3, width=12, height=1, string=" Title ", alignment=tcod.CENTER) + >>> console.print(x=0, y=3, width=12, height=1, string=" Title ", alignment=libtcodpy.CENTER) 1 - >>> console.print_box(x=0, y=5, width=12, height=1, string="┤Lower├", alignment=tcod.CENTER) + >>> console.print(x=0, y=5, width=12, height=1, string="┤Lower├", alignment=libtcodpy.CENTER) 1 >>> print(console) <┌─┐╔═╗123/-\ @@ -1132,7 +1386,7 @@ def draw_frame( ) return decoration_ = [ord(c) for c in decoration] if isinstance(decoration, str) else decoration - if len(decoration_) != 9: + if len(decoration_) != 9: # noqa: PLR2004 msg = f"Decoration must have a length of 9 (len(decoration) is {len(decoration_)}.)" raise TypeError(msg) _check( @@ -1150,6 +1404,22 @@ def draw_frame( ) ) + @overload + def draw_rect( + self, + x: int, + y: int, + width: int, + height: int, + *, + ch: int, + fg: tuple[int, int, int] | None = None, + bg: tuple[int, int, int] | None = None, + bg_blend: int = tcod.constants.BKGND_SET, + ) -> None: ... + + @overload + @deprecated("Parameters ch, fg, bg, bg_blend should be keyword arguments") def draw_rect( self, x: int, @@ -1160,6 +1430,18 @@ def draw_rect( fg: tuple[int, int, int] | None = None, bg: tuple[int, int, int] | None = None, bg_blend: int = tcod.constants.BKGND_SET, + ) -> None: ... + + def draw_rect( # noqa: PLR0913 + self, + x: int, + y: int, + width: int, + height: int, + ch: int, + fg: tuple[int, int, int] | None = None, + bg: tuple[int, int, int] | None = None, + bg_blend: int = tcod.constants.BKGND_SET, ) -> None: """Draw characters and colors over a rectangular region. @@ -1168,15 +1450,16 @@ def draw_rect( `width` and `height` determine the size of the rectangle. - `ch` is a Unicode integer. You can use 0 to leave the current - characters unchanged. + `ch` is a Unicode integer. You can use 0 to leave the current characters unchanged. + Must be given as a keyword argument. - `fg` and `bg` are the foreground text color and background tile color - respectfully. This is a 3-item tuple with (r, g, b) color values from - 0 to 255. These parameters can also be set to `None` to leave the - colors unchanged. + `fg` and `bg` are the foreground text color and background tile color respectfully. + This is a 3-item tuple with (r, g, b) color values from 0 to 255. + These parameters can also be set to `None` to leave the colors unchanged. + Must be given as a keyword argument. `bg_blend` is the blend type used by libtcod. + Must be given as a keyword argument. .. versionadded:: 8.5 @@ -1185,6 +1468,10 @@ def draw_rect( .. versionchanged:: 13.0 `x` and `y` are now always used as an absolute position for negative values. + + .. versionchanged:: 18.0 + Deprecated `ch`, `fg`, `bg`, and `bg_blend` being given as positional arguments. + These should be keyword arguments only. """ lib.TCOD_console_draw_rect_rgb( self.console_c, @@ -1198,7 +1485,7 @@ def draw_rect( bg_blend, ) - def draw_semigraphics(self, pixels: Any, x: int = 0, y: int = 0) -> None: + def draw_semigraphics(self, pixels: ArrayLike | tcod.image.Image, x: int = 0, y: int = 0) -> None: """Draw a block of 2x2 semi-graphics into this console. `pixels` is an Image or an array-like object. It will be down-sampled @@ -1209,7 +1496,7 @@ def draw_semigraphics(self, pixels: Any, x: int = 0, y: int = 0) -> None: .. versionadded:: 11.4 """ - image = tcod._internal._as_image(pixels) + image = tcod.image._as_image(pixels) lib.TCOD_image_blit_2x(image.image_c, self.console_c, x, y, 0, 0, -1, -1) @@ -1226,7 +1513,7 @@ def get_height_rect(width: int, string: str) -> int: return int(lib.TCOD_console_get_height_rect_wn(width, len(string_), string_)) -@deprecate("This function does not support contexts.", category=FutureWarning) +@deprecated("This function does not support contexts.", category=FutureWarning) def recommended_size() -> tuple[int, int]: """Return the recommended size of a console for the current active window. @@ -1252,9 +1539,9 @@ def recommended_size() -> tuple[int, int]: renderer = lib.TCOD_sys_get_sdl_renderer() with ffi.new("int[2]") as xy: if renderer: - lib.SDL_GetRendererOutputSize(renderer, xy, xy + 1) + lib.SDL_GetCurrentRenderOutputSize(renderer, xy, xy + 1) else: # Assume OpenGL if a renderer does not exist. - lib.SDL_GL_GetDrawableSize(window, xy, xy + 1) + lib.SDL_GetWindowSizeInPixels(window, xy, xy + 1) w = max(1, xy[0] // lib.TCOD_ctx.tileset.tile_width) h = max(1, xy[1] // lib.TCOD_ctx.tileset.tile_height) return w, h @@ -1295,9 +1582,9 @@ def load_xp(path: str | PathLike[str], order: Literal["C", "F"] = "C") -> tuple[ console.rgba[is_transparent] = (ord(" "), (0,), (0,)) """ path = Path(path).resolve(strict=True) - layers = _check(tcod.lib.TCOD_load_xp(bytes(path), 0, ffi.NULL)) + layers = _check(tcod.lib.TCOD_load_xp(_path_encode(path), 0, ffi.NULL)) consoles = ffi.new("TCOD_Console*[]", layers) - _check(tcod.lib.TCOD_load_xp(bytes(path), layers, consoles)) + _check(tcod.lib.TCOD_load_xp(_path_encode(path), layers, consoles)) return tuple(Console._from_cdata(console_p, order=order) for console_p in consoles) @@ -1358,7 +1645,7 @@ def save_xp( tcod.lib.TCOD_save_xp( len(consoles_c), consoles_c, - bytes(path), + _path_encode(path), compress_level, ) ) diff --git a/tcod/constants.py b/tcod/constants.py index f0a596dc..ebaa4f4b 100644 --- a/tcod/constants.py +++ b/tcod/constants.py @@ -2,6 +2,7 @@ This module is auto-generated by `build_libtcod.py`. """ + from tcod.color import Color FOV_BASIC = 0 @@ -502,7 +503,7 @@ white = Color(255, 255, 255) yellow = Color(255, 255, 0) -__all__ = [ +__all__ = [ # noqa: RUF022 "FOV_BASIC", "FOV_DIAMOND", "FOV_PERMISSIVE_0", diff --git a/tcod/context.py b/tcod/context.py index 4b2d33e3..01ed61f7 100644 --- a/tcod/context.py +++ b/tcod/context.py @@ -22,16 +22,18 @@ .. versionadded:: 11.12 """ + from __future__ import annotations import copy import pickle import sys import warnings +from math import floor from pathlib import Path -from typing import Any, Iterable, NoReturn, TypeVar +from typing import TYPE_CHECKING, Any, Literal, NoReturn, TypeVar -from typing_extensions import Literal +from typing_extensions import Self, deprecated import tcod.console import tcod.event @@ -39,41 +41,39 @@ import tcod.sdl.render import tcod.sdl.video import tcod.tileset -from tcod._internal import _check, _check_warn, pending_deprecate +from tcod._internal import _check, _check_warn from tcod.cffi import ffi, lib +if TYPE_CHECKING: + from collections.abc import Iterable + __all__ = ( - "Context", - "new", - "new_window", - "new_terminal", - "SDL_WINDOW_FULLSCREEN", - "SDL_WINDOW_FULLSCREEN_DESKTOP", - "SDL_WINDOW_HIDDEN", - "SDL_WINDOW_BORDERLESS", - "SDL_WINDOW_RESIZABLE", - "SDL_WINDOW_MINIMIZED", - "SDL_WINDOW_MAXIMIZED", - "SDL_WINDOW_INPUT_GRABBED", - "SDL_WINDOW_ALLOW_HIGHDPI", "RENDERER_OPENGL", "RENDERER_OPENGL2", "RENDERER_SDL", "RENDERER_SDL2", "RENDERER_XTERM", + "SDL_WINDOW_ALLOW_HIGHDPI", + "SDL_WINDOW_BORDERLESS", + "SDL_WINDOW_FULLSCREEN", + "SDL_WINDOW_FULLSCREEN_DESKTOP", + "SDL_WINDOW_HIDDEN", + "SDL_WINDOW_INPUT_GRABBED", + "SDL_WINDOW_MAXIMIZED", + "SDL_WINDOW_MINIMIZED", + "SDL_WINDOW_RESIZABLE", + "Context", + "new", + "new_terminal", + "new_window", ) -_Event = TypeVar("_Event", bound=tcod.event.Event) +_Event = TypeVar("_Event", bound="tcod.event.Event") SDL_WINDOW_FULLSCREEN = lib.SDL_WINDOW_FULLSCREEN -"""Exclusive fullscreen mode. - -It's generally not recommended to use this flag unless you know what you're -doing. -`SDL_WINDOW_FULLSCREEN_DESKTOP` should be used instead whenever possible. -""" -SDL_WINDOW_FULLSCREEN_DESKTOP = lib.SDL_WINDOW_FULLSCREEN_DESKTOP -"""A borderless fullscreen window at the desktop resolution.""" +"""Fullscreen mode.""" +# SDL_WINDOW_FULLSCREEN_DESKTOP = lib.SDL_WINDOW_FULLSCREEN_DESKTOP +# """A borderless fullscreen window at the desktop resolution.""" SDL_WINDOW_HIDDEN = lib.SDL_WINDOW_HIDDEN """Window is hidden.""" SDL_WINDOW_BORDERLESS = lib.SDL_WINDOW_BORDERLESS @@ -84,9 +84,9 @@ """Window is minimized.""" SDL_WINDOW_MAXIMIZED = lib.SDL_WINDOW_MAXIMIZED """Window is maximized.""" -SDL_WINDOW_INPUT_GRABBED = lib.SDL_WINDOW_INPUT_GRABBED +SDL_WINDOW_INPUT_GRABBED = lib.SDL_WINDOW_MOUSE_GRABBED """Window has grabbed the input.""" -SDL_WINDOW_ALLOW_HIGHDPI = lib.SDL_WINDOW_ALLOW_HIGHDPI +SDL_WINDOW_ALLOW_HIGHDPI = lib.SDL_WINDOW_HIGH_PIXEL_DENSITY """High DPI mode, see the SDL documentation.""" RENDERER_OPENGL = lib.TCOD_RENDERER_OPENGL @@ -124,12 +124,12 @@ """ -def _handle_tileset(tileset: tcod.tileset.Tileset | None) -> Any: +def _handle_tileset(tileset: tcod.tileset.Tileset | None) -> Any: # noqa: ANN401 """Get the TCOD_Tileset pointer from a Tileset or return a NULL pointer.""" - return tileset._tileset_p if tileset else ffi.NULL + return tileset._tileset_p if tileset is not None else ffi.NULL -def _handle_title(title: str | None) -> Any: +def _handle_title(title: str | None) -> Any: # noqa: ANN401 """Return title as a CFFI string. If title is None then return a decent default title is returned. @@ -145,12 +145,12 @@ class Context: Use :any:`tcod.context.new` to create a new context. """ - def __init__(self, context_p: Any) -> None: + def __init__(self, context_p: Any) -> None: # noqa: ANN401 """Create a context from a cffi pointer.""" self._context_p = context_p @classmethod - def _claim(cls, context_p: Any) -> Context: + def _claim(cls, context_p: Any) -> Context: # noqa: ANN401 """Return a new instance wrapping a context pointer.""" return cls(ffi.gc(context_p, lib.TCOD_context_delete)) @@ -163,7 +163,7 @@ def _p(self) -> Any: # noqa: ANN401 msg = "This context has been closed can no longer be used." raise RuntimeError(msg) from None - def __enter__(self) -> Context: + def __enter__(self) -> Self: """Enter this context which will close on exiting.""" return self @@ -176,7 +176,7 @@ def close(self) -> None: ffi.release(self._context_p) del self._context_p - def __exit__(self, *args: Any) -> None: + def __exit__(self, *_: object) -> None: """Automatically close on the context on exit.""" self.close() @@ -223,13 +223,14 @@ def present( ) _check(lib.TCOD_context_present(self._p, console.console_c, viewport_args)) - def pixel_to_tile(self, x: int, y: int) -> tuple[int, int]: + def pixel_to_tile(self, x: float, y: float) -> tuple[float, float]: """Convert window pixel coordinates to tile coordinates.""" - with ffi.new("int[2]", (x, y)) as xy: - _check(lib.TCOD_context_screen_pixel_to_tile_i(self._p, xy, xy + 1)) + with ffi.new("double[2]", (x, y)) as xy: + _check(lib.TCOD_context_screen_pixel_to_tile_d(self._p, xy, xy + 1)) return xy[0], xy[1] - def pixel_to_subtile(self, x: int, y: int) -> tuple[float, float]: + @deprecated("Use pixel_to_tile method instead.") + def pixel_to_subtile(self, x: float, y: float) -> tuple[float, float]: """Convert window pixel coordinates to sub-tile coordinates.""" with ffi.new("double[2]", (x, y)) as xy: _check(lib.TCOD_context_screen_pixel_to_tile_d(self._p, xy, xy + 1)) @@ -254,9 +255,11 @@ def convert_event(self, event: _Event) -> _Event: Now returns a new event with the coordinates converted into tiles. """ event_copy = copy.copy(event) - if isinstance(event, (tcod.event.MouseState, tcod.event.MouseMotion)): - assert isinstance(event_copy, (tcod.event.MouseState, tcod.event.MouseMotion)) - event_copy.position = event._tile = tcod.event.Point(*self.pixel_to_tile(*event.position)) + if isinstance(event, tcod.event._MouseEventWithPosition): + assert isinstance(event_copy, tcod.event._MouseEventWithPosition) + event_copy.position = tcod.event.Point(*self.pixel_to_tile(event.position[0], event.position[1])) + if isinstance(event, tcod.event._MouseEventWithTile): + event._tile = tcod.event.Point(floor(event_copy.position[0]), floor(event_copy.position[1])) if isinstance(event, tcod.event.MouseMotion): assert isinstance(event_copy, tcod.event.MouseMotion) assert event._tile is not None @@ -264,8 +267,13 @@ def convert_event(self, event: _Event) -> _Event: event.position[0] - event.motion[0], event.position[1] - event.motion[1], ) - event_copy.motion = event.tile_motion = tcod.event.Point( - event._tile[0] - prev_tile[0], event._tile[1] - prev_tile[1] + event_copy.motion = tcod.event.Point( + event_copy.position[0] - prev_tile[0], + event_copy.position[1] - prev_tile[1], + ) + event._tile_motion = tcod.event.Point( + event._tile[0] - floor(prev_tile[0]), + event._tile[1] - floor(prev_tile[1]), ) return event_copy @@ -343,7 +351,8 @@ def new_console( scale = max(1, scale + event.y) """ if magnification < 0: - raise ValueError("Magnification must be greater than zero. (Got %f)" % magnification) + msg = f"Magnification must be greater than zero. (Got {magnification:f})" + raise ValueError(msg) size = ffi.new("int[2]") _check(lib.TCOD_context_recommended_console_size(self._p, magnification, size, size + 1)) width, height = max(min_columns, size[0]), max(min_rows, size[1]) @@ -366,7 +375,7 @@ def renderer_type(self) -> int: return _check(lib.TCOD_context_get_renderer_type(self._p)) @property - def sdl_window_p(self) -> Any: + def sdl_window_p(self) -> Any: # noqa: ANN401 '''A cffi `SDL_Window*` pointer. This pointer might be NULL. This pointer will become invalid if the context is closed or goes out @@ -445,8 +454,8 @@ def __reduce__(self) -> NoReturn: raise pickle.PicklingError(msg) -@ffi.def_extern() # type: ignore -def _pycall_cli_output(catch_reference: Any, output: Any) -> None: +@ffi.def_extern() # type: ignore[untyped-decorator] +def _pycall_cli_output(catch_reference: Any, output: Any) -> None: # noqa: ANN401 """Callback for the libtcod context CLI. Catches the CLI output. @@ -455,7 +464,7 @@ def _pycall_cli_output(catch_reference: Any, output: Any) -> None: catch.append(ffi.string(output).decode("utf-8")) -def new( +def new( # noqa: PLR0913 *, x: int | None = None, y: int | None = None, @@ -572,8 +581,8 @@ def new( return Context._claim(context_pp[0]) -@pending_deprecate("Call tcod.context.new with width and height as keyword parameters.") -def new_window( +@deprecated("Call tcod.context.new with width and height as keyword parameters.") +def new_window( # noqa: PLR0913 width: int, height: int, *, @@ -599,8 +608,8 @@ def new_window( ) -@pending_deprecate("Call tcod.context.new with columns and rows as keyword parameters.") -def new_terminal( +@deprecated("Call tcod.context.new with columns and rows as keyword parameters.") +def new_terminal( # noqa: PLR0913 columns: int, rows: int, *, diff --git a/tcod/event.py b/tcod/event.py index 16eaf498..dd700afe 100644 --- a/tcod/event.py +++ b/tcod/event.py @@ -27,21 +27,19 @@ while True: console = context.new_console() context.present(console, integer_scaling=True) - for event in tcod.event.wait(): - context.convert_event(event) # Adds tile coordinates to mouse events. + for pixel_event in tcod.event.wait(): + event = context.convert_event(pixel_event) # Convert mouse pixel coordinates to tile coordinates + print(event) # Print all events, for learning and debugging if isinstance(event, tcod.event.Quit): - print(event) raise SystemExit() elif isinstance(event, tcod.event.KeyDown): - print(event) # Prints the Scancode and KeySym enums for this event. + print(f"{event.sym=}, {event.scancode=}") # Show Scancode and KeySym enum names if event.sym in KEY_COMMANDS: print(f"Command: {KEY_COMMANDS[event.sym]}") elif isinstance(event, tcod.event.MouseButtonDown): - print(event) # Prints the mouse button constant names for this event. + print(f"{event.button=}, {event.integer_position=}") # Show mouse button and tile elif isinstance(event, tcod.event.MouseMotion): - print(event) # Prints the mouse button mask bits in a readable format. - else: - print(event) # Print any unhandled events. + print(f"{event.integer_position=}, {event.integer_motion=}") # Current mouse tile and tile motion Python 3.10 introduced `match statements `_ which can be used to dispatch events more gracefully: @@ -61,42 +59,75 @@ while True: console = context.new_console() context.present(console, integer_scaling=True) - for event in tcod.event.wait(): - context.convert_event(event) # Adds tile coordinates to mouse events. + for pixel_event in tcod.event.wait(): + event = context.convert_event(pixel_event) # Converts mouse pixel coordinates to tile coordinates. match event: case tcod.event.Quit(): raise SystemExit() - case tcod.event.KeyDown(sym) if sym in KEY_COMMANDS: + case tcod.event.KeyDown(sym=sym) if sym in KEY_COMMANDS: print(f"Command: {KEY_COMMANDS[sym]}") case tcod.event.KeyDown(sym=sym, scancode=scancode, mod=mod, repeat=repeat): print(f"KeyDown: {sym=}, {scancode=}, {mod=}, {repeat=}") - case tcod.event.MouseButtonDown(button=button, pixel=pixel, tile=tile): - print(f"MouseButtonDown: {button=}, {pixel=}, {tile=}") - case tcod.event.MouseMotion(pixel=pixel, pixel_motion=pixel_motion, tile=tile, tile_motion=tile_motion): - print(f"MouseMotion: {pixel=}, {pixel_motion=}, {tile=}, {tile_motion=}") + case tcod.event.MouseButtonDown(button=button, integer_position=tile): + print(f"MouseButtonDown: {button=}, {tile=}") + case tcod.event.MouseMotion(integer_position=tile, integer_motion=tile_motion): + assert isinstance(pixel_event, tcod.event.MouseMotion) + pixel_motion = pixel_event.motion + print(f"MouseMotion: {pixel_motion=}, {tile=}, {tile_motion=}") case tcod.event.Event() as event: - print(event) # Show any unhandled events. + print(event) # Print unhandled events .. versionadded:: 8.4 """ + from __future__ import annotations import enum +import functools +import sys import warnings -from typing import Any, Callable, Generic, Iterator, Mapping, NamedTuple, TypeVar - +from collections.abc import Callable, Iterator, Mapping +from math import floor +from pathlib import Path +from typing import ( + TYPE_CHECKING, + Any, + Final, + Generic, + Literal, + NamedTuple, + Protocol, + TypeAlias, + TypedDict, + TypeVar, + overload, + runtime_checkable, +) + +import attrs import numpy as np -from numpy.typing import NDArray -from typing_extensions import Final, Literal +from typing_extensions import Self, deprecated +import tcod.context import tcod.event_constants import tcod.sdl.joystick +import tcod.sdl.render +import tcod.sdl.sys from tcod.cffi import ffi, lib from tcod.event_constants import * # noqa: F403 -from tcod.event_constants import KMOD_ALT, KMOD_CTRL, KMOD_GUI, KMOD_SHIFT from tcod.sdl.joystick import _HAT_DIRECTIONS +if TYPE_CHECKING: + from numpy.typing import NDArray + T = TypeVar("T") +_EventType = TypeVar("_EventType", bound="Event") + +_C_SDL_Event: TypeAlias = Any +"""A CFFI pointer to an SDL_Event union. + +See SDL docs: https://wiki.libsdl.org/SDL3/SDL_Event +""" class _ConstantsWithPrefix(Mapping[int, str]): @@ -133,29 +164,39 @@ def _describe_bitmask(bits: int, table: Mapping[int, str], default: str = "0") - return "|".join(result) -def _pixel_to_tile(x: float, y: float) -> tuple[float, float] | None: +def _pixel_to_tile(xy: tuple[float, float], /) -> Point[float] | None: """Convert pixel coordinates to tile coordinates.""" if not lib.TCOD_ctx.engine: return None - xy = ffi.new("double[2]", (x, y)) - lib.TCOD_sys_pixel_to_tile(xy, xy + 1) - return xy[0], xy[1] + xy_out = ffi.new("double[2]", xy) + lib.TCOD_sys_pixel_to_tile(xy_out, xy_out + 1) + return Point(float(xy_out[0]), float(xy_out[1])) -class Point(NamedTuple): - """A 2D position used for events with mouse coordinates. +if sys.version_info >= (3, 11) or TYPE_CHECKING: - .. seealso:: - :any:`MouseMotion` :any:`MouseButtonDown` :any:`MouseButtonUp` - """ + class Point(NamedTuple, Generic[T]): + """A 2D position used for events with mouse coordinates. - x: int - """A pixel or tile coordinate starting with zero as the left-most position.""" - y: int - """A pixel or tile coordinate starting with zero as the top-most position.""" + .. seealso:: + :any:`MouseMotion` :any:`MouseButtonDown` :any:`MouseButtonUp` + + .. versionchanged:: 19.0 + Now uses floating point coordinates due to the port to SDL3. + """ + + x: T + """A pixel or tile coordinate starting with zero as the left-most position.""" + y: T + """A pixel or tile coordinate starting with zero as the top-most position.""" +else: + class Point(NamedTuple): # noqa: D101 + x: Any + y: Any -def _verify_tile_coordinates(xy: Point | None) -> Point: + +def _verify_tile_coordinates(xy: Point[int] | None) -> Point[int]: """Check if an events tile coordinate is initialized and warn if not. Always returns a valid Point object for backwards compatibility. @@ -172,16 +213,11 @@ def _verify_tile_coordinates(xy: Point | None) -> Point: return Point(0, 0) -_is_sdl_video_initialized = False - - def _init_sdl_video() -> None: """Keyboard layout stuff needs SDL to be initialized first.""" - global _is_sdl_video_initialized - if _is_sdl_video_initialized: + if lib.SDL_WasInit(lib.SDL_INIT_VIDEO): return lib.SDL_InitSubSystem(lib.SDL_INIT_VIDEO) - _is_sdl_video_initialized = True class Modifier(enum.IntFlag): @@ -194,6 +230,7 @@ class Modifier(enum.IntFlag): Example:: + >>> import tcod.event >>> mod = tcod.event.Modifier(4098) >>> mod & tcod.event.Modifier.SHIFT # Check if any shift key is held. @@ -258,6 +295,7 @@ class MouseButton(enum.IntEnum): """Forward mouse button.""" def __repr__(self) -> str: + """Return the enum name, excluding the value.""" return f"{self.__class__.__name__}.{self.name}" @@ -279,595 +317,669 @@ class MouseButtonMask(enum.IntFlag): """Forward mouse button is held.""" def __repr__(self) -> str: + """Return the bitwise OR flag combination of this value.""" if self.value == 0: return f"{self.__class__.__name__}(0)" return "|".join(f"{self.__class__.__name__}.{self.__class__(bit).name}" for bit in self.__class__ if bit & self) +class _CommonSDLEventAttributes(TypedDict): + """Common keywords for Event subclasses.""" + + sdl_event: _C_SDL_Event + timestamp_ns: int + + +def _unpack_sdl_event(sdl_event: _C_SDL_Event) -> _CommonSDLEventAttributes: + """Unpack an SDL_Event union into common attributes, such as timestamp.""" + return { + "sdl_event": sdl_event, + "timestamp_ns": sdl_event.common.timestamp, + } + + +@attrs.define(slots=True, kw_only=True) class Event: - """The base event class. + """The base event class.""" + + sdl_event: _C_SDL_Event = attrs.field(default=None, eq=False, repr=False) + """Holds a python-cffi ``SDL_Event*`` pointer for this event when available.""" - Attributes: - type (str): This events type. - sdl_event: When available, this holds a python-cffi 'SDL_Event*' - pointer. All sub-classes have this attribute. + timestamp_ns: int = attrs.field(default=0, eq=False) + """The time of this event in nanoseconds since SDL has been initialized. + + .. seealso:: + :any:`tcod.event.time_ns` + + .. versionadded:: 21.0 """ - def __init__(self, type: str | None = None) -> None: - if type is None: - type = self.__class__.__name__.upper() - self.type: Final = type - self.sdl_event = None + @property + def timestamp(self) -> float: + """The time of this event in seconds since SDL has been initialized. + + .. seealso:: + :any:`tcod.event.time` + + .. versionadded:: 21.0 + """ + return self.timestamp_ns / 1_000_000_000 + + @property + @deprecated("The Event.type attribute is deprecated, use isinstance instead.") + def type(self) -> str: + """This events type. + + .. deprecated:: 21.0 + Using this attribute is now actively discouraged. Use :func:`isinstance` or :ref:`match`. + + :meta private: + """ + type_override: str | None = getattr(self, "_type", None) + if type_override is not None: + return type_override + return self.__class__.__name__.upper() @classmethod - def from_sdl_event(cls, sdl_event: Any) -> Event: - """Return a class instance from a python-cffi 'SDL_Event*' pointer.""" - raise NotImplementedError() + def _from_sdl_event(cls, sdl_event: _C_SDL_Event) -> Event: + """Return a class instance from a python-cffi 'SDL_Event*' pointer. - def __str__(self) -> str: - return f"" + .. versionchanged:: 21.0 + This method was unsuitable for the public API and is now private. + """ + raise NotImplementedError +@attrs.define(slots=True, kw_only=True) class Quit(Event): """An application quit request event. For more info on when this event is triggered see: https://wiki.libsdl.org/SDL_EventType#SDL_QUIT - - Attributes: - type (str): Always "QUIT". """ @classmethod - def from_sdl_event(cls, sdl_event: Any) -> Quit: - self = cls() - self.sdl_event = sdl_event - return self - - def __repr__(self) -> str: - return f"tcod.event.{self.__class__.__name__}()" + def _from_sdl_event(cls, sdl_event: _C_SDL_Event) -> Self: + return cls(**_unpack_sdl_event(sdl_event)) +@attrs.define(slots=True, kw_only=True) class KeyboardEvent(Event): """Base keyboard event. - Attributes: - type (str): Will be "KEYDOWN" or "KEYUP", depending on the event. - scancode (Scancode): The keyboard scan-code, this is the physical location - of the key on the keyboard rather than the keys symbol. - sym (KeySym): The keyboard symbol. - mod (Modifier): A bitmask of the currently held modifier keys. + .. versionchanged:: 12.5 + `scancode`, `sym`, and `mod` now use their respective enums. + """ - For example, if shift is held then - ``event.mod & tcod.event.Modifier.SHIFT`` will evaluate to a true - value. + scancode: Scancode + """The keyboard scan-code, this is the physical location + of the key on the keyboard rather than the keys symbol.""" + sym: KeySym + """The keyboard symbol.""" + mod: Modifier + """A bitmask of the currently held modifier keys. + + For example, if shift is held then + ``event.mod & tcod.event.Modifier.SHIFT`` will evaluate to a true + value. + """ + repeat: bool = False + """True if this event exists because of key repeat.""" + which: int = 0 + """The SDL keyboard instance ID. Zero if unknown or virtual. - repeat (bool): True if this event exists because of key repeat. + .. versionadded:: 21.0 + """ + window_id: int = 0 + """The SDL window ID with keyboard focus. - .. versionchanged:: 12.5 - `scancode`, `sym`, and `mod` now use their respective enums. + .. versionadded:: 21.0 """ + pressed: bool = False + """True if the key was pressed, False if the key was released. - def __init__(self, scancode: int, sym: int, mod: int, repeat: bool = False) -> None: - super().__init__() - self.scancode = Scancode(scancode) - self.sym = KeySym(sym) - self.mod = Modifier(mod) - self.repeat = repeat + .. versionadded:: 21.0 + """ @classmethod - def from_sdl_event(cls, sdl_event: Any) -> Any: - keysym = sdl_event.key.keysym - self = cls(keysym.scancode, keysym.sym, keysym.mod, bool(sdl_event.key.repeat)) - self.sdl_event = sdl_event - return self - - def __repr__(self) -> str: - return "tcod.event.{}(scancode={!r}, sym={!r}, mod={!r}{})".format( - self.__class__.__name__, - self.scancode, - self.sym, - self.mod, - ", repeat=True" if self.repeat else "", + def _from_sdl_event(cls, sdl_event: _C_SDL_Event) -> Self: + keysym = sdl_event.key + return cls( + scancode=Scancode(keysym.scancode), + sym=KeySym(keysym.key), + mod=Modifier(keysym.mod), + repeat=bool(keysym.repeat), + pressed=bool(keysym.down), + which=int(keysym.which), + window_id=int(keysym.windowID), + **_unpack_sdl_event(sdl_event), ) - def __str__(self) -> str: - return self.__repr__().replace("tcod.event.", "") - +@attrs.define(slots=True, kw_only=True) class KeyDown(KeyboardEvent): - pass + """A :any:`KeyboardEvent` where the key was pressed.""" +@attrs.define(slots=True, kw_only=True) class KeyUp(KeyboardEvent): - pass + """A :any:`KeyboardEvent` where the key was released.""" +@attrs.define(slots=True, kw_only=True) class MouseState(Event): """Mouse state. - Attributes: - type (str): Always "MOUSESTATE". - position (Point): The position coordinates of the mouse. - tile (Point): The integer tile coordinates of the mouse on the screen. - state (int): A bitmask of which mouse buttons are currently held. - - Will be a combination of the following names: - - * tcod.event.BUTTON_LMASK - * tcod.event.BUTTON_MMASK - * tcod.event.BUTTON_RMASK - * tcod.event.BUTTON_X1MASK - * tcod.event.BUTTON_X2MASK - .. versionadded:: 9.3 .. versionchanged:: 15.0 Renamed `pixel` attribute to `position`. """ - def __init__( - self, - position: tuple[int, int] = (0, 0), - tile: tuple[int, int] | None = (0, 0), - state: int = 0, - ) -> None: - super().__init__() - self.position = Point(*position) - self._tile = Point(*tile) if tile is not None else None - self.state = state + position: Point[float] = attrs.field(default=Point(0.0, 0.0)) + """The position coordinates of the mouse.""" + _tile: Point[int] | None = attrs.field(default=Point(0, 0), alias="tile") + + state: MouseButtonMask = attrs.field(default=MouseButtonMask(0)) + """A bitmask of which mouse buttons are currently held.""" + + which: int = 0 + """The mouse device ID for this event. + + .. versionadded:: 21.0 + """ + + window_id: int = 0 + """The window ID with mouse focus. + + .. versionadded:: 21.0 + """ @property - def pixel(self) -> Point: - warnings.warn( - "The mouse.pixel attribute is deprecated. Use mouse.position instead.", - DeprecationWarning, - stacklevel=2, - ) + def integer_position(self) -> Point[int]: + """Integer coordinates of this event. + + .. versionadded:: 21.0 + """ + x, y = self.position + return Point(floor(x), floor(y)) + + @property + @deprecated("The mouse.pixel attribute is deprecated. Use mouse.position instead.") + def pixel(self) -> Point[float]: # noqa: D102 # Skip docstring for deprecated attribute return self.position @pixel.setter - def pixel(self, value: Point) -> None: + def pixel(self, value: Point[float]) -> None: self.position = value @property - def tile(self) -> Point: - warnings.warn( - "The mouse.tile attribute is deprecated. Use mouse.position of the event returned by context.convert_event instead.", - DeprecationWarning, - stacklevel=2, - ) + @deprecated( + "The mouse.tile attribute is deprecated." + " Use mouse.integer_position of the event returned by context.convert_event instead." + ) + def tile(self) -> Point[int]: + """The integer tile coordinates of the mouse on the screen. + + .. deprecated:: 21.0 + Use :any:`integer_position` of the event returned by :any:`Context.convert_event` instead. + """ return _verify_tile_coordinates(self._tile) @tile.setter + @deprecated( + "The mouse.tile attribute is deprecated." + " Use mouse.integer_position of the event returned by context.convert_event instead." + ) def tile(self, xy: tuple[int, int]) -> None: self._tile = Point(*xy) - def __repr__(self) -> str: - return ("tcod.event.{}(position={!r}, tile={!r}, state={})").format( - self.__class__.__name__, - tuple(self.position), - tuple(self.tile), - MouseButtonMask(self.state), - ) - - def __str__(self) -> str: - return ("<%s, position=(x=%i, y=%i), tile=(x=%i, y=%i), state=%s>") % ( - super().__str__().strip("<>"), - *self.position, - *self.tile, - MouseButtonMask(self.state), - ) - +@attrs.define(slots=True, kw_only=True) class MouseMotion(MouseState): """Mouse motion event. - Attributes: - type (str): Always "MOUSEMOTION". - position (Point): The pixel coordinates of the mouse. - motion (Point): The pixel delta. - tile (Point): The integer tile coordinates of the mouse on the screen. - tile_motion (Point): The integer tile delta. - state (int): A bitmask of which mouse buttons are currently held. - - Will be a combination of the following names: - - * tcod.event.BUTTON_LMASK - * tcod.event.BUTTON_MMASK - * tcod.event.BUTTON_RMASK - * tcod.event.BUTTON_X1MASK - * tcod.event.BUTTON_X2MASK - .. versionchanged:: 15.0 Renamed `pixel` attribute to `position`. Renamed `pixel_motion` attribute to `motion`. + + .. versionchanged:: 19.0 + `position` and `motion` now use floating point coordinates. """ - def __init__( - self, - position: tuple[int, int] = (0, 0), - motion: tuple[int, int] = (0, 0), - tile: tuple[int, int] | None = (0, 0), - tile_motion: tuple[int, int] | None = (0, 0), - state: int = 0, - ) -> None: - super().__init__(position, tile, state) - self.motion = Point(*motion) - self.__tile_motion = Point(*tile_motion) if tile_motion is not None else None + motion: Point[float] = attrs.field(default=Point(0.0, 0.0)) + """The pixel delta.""" + _tile_motion: Point[int] | None = attrs.field(default=Point(0, 0), alias="tile_motion") @property - def pixel_motion(self) -> Point: - warnings.warn( - "The mouse.pixel_motion attribute is deprecated. Use mouse.motion instead.", - DeprecationWarning, - stacklevel=2, - ) + def integer_motion(self) -> Point[int]: + """Integer motion of this event. + + .. versionadded:: 21.0 + """ + x, y = self.position + dx, dy = self.motion + prev_x, prev_y = x - dx, y - dy + return Point(floor(x) - floor(prev_x), floor(y) - floor(prev_y)) + + @property + @deprecated("The mouse.pixel_motion attribute is deprecated. Use mouse.motion instead.") + def pixel_motion(self) -> Point[float]: # noqa: D102 # Skip docstring for deprecated attribute return self.motion @pixel_motion.setter - def pixel_motion(self, value: Point) -> None: - warnings.warn( - "The mouse.pixel_motion attribute is deprecated. Use mouse.motion instead.", - DeprecationWarning, - stacklevel=2, - ) + @deprecated("The mouse.pixel_motion attribute is deprecated. Use mouse.motion instead.") + def pixel_motion(self, value: Point[float]) -> None: self.motion = value @property - def tile_motion(self) -> Point: - warnings.warn( - "The mouse.tile_motion attribute is deprecated." - " Use mouse.motion of the event returned by context.convert_event instead.", - DeprecationWarning, - stacklevel=2, - ) - return _verify_tile_coordinates(self.__tile_motion) + @deprecated( + "The mouse.tile_motion attribute is deprecated." + " Use mouse.integer_motion of the event returned by context.convert_event instead." + ) + def tile_motion(self) -> Point[int]: + """The tile delta. + + .. deprecated:: 21.0 + Use :any:`integer_motion` of the event returned by :any:`Context.convert_event` instead. + """ + return _verify_tile_coordinates(self._tile_motion) @tile_motion.setter + @deprecated( + "The mouse.tile_motion attribute is deprecated." + " Use mouse.integer_motion of the event returned by context.convert_event instead." + ) def tile_motion(self, xy: tuple[int, int]) -> None: - warnings.warn( - "The mouse.tile_motion attribute is deprecated." - " Use mouse.motion of the event returned by context.convert_event instead.", - DeprecationWarning, - stacklevel=2, - ) - self.__tile_motion = Point(*xy) + self._tile_motion = Point(*xy) @classmethod - def from_sdl_event(cls, sdl_event: Any) -> MouseMotion: + def _from_sdl_event(cls, sdl_event: _C_SDL_Event) -> Self: motion = sdl_event.motion + common = {"which": int(motion.which), "window_id": int(motion.windowID)} + state = MouseButtonMask(motion.state) - pixel = motion.x, motion.y - pixel_motion = motion.xrel, motion.yrel - subtile = _pixel_to_tile(*pixel) + pixel = Point(float(motion.x), float(motion.y)) + pixel_motion = Point(float(motion.xrel), float(motion.yrel)) + subtile = _pixel_to_tile(pixel) if subtile is None: - self = cls(pixel, pixel_motion, None, None, motion.state) + self = cls( + position=pixel, + motion=pixel_motion, + tile=None, + tile_motion=None, + state=state, + **common, + **_unpack_sdl_event(sdl_event), + ) else: - tile = int(subtile[0]), int(subtile[1]) - prev_pixel = pixel[0] - pixel_motion[0], pixel[1] - pixel_motion[1] - prev_subtile = _pixel_to_tile(*prev_pixel) or (0, 0) - prev_tile = int(prev_subtile[0]), int(prev_subtile[1]) - tile_motion = tile[0] - prev_tile[0], tile[1] - prev_tile[1] - self = cls(pixel, pixel_motion, tile, tile_motion, motion.state) + tile = Point(floor(subtile[0]), floor(subtile[1])) + prev_pixel = (pixel[0] - pixel_motion[0], pixel[1] - pixel_motion[1]) + prev_subtile = _pixel_to_tile(prev_pixel) or (0, 0) + prev_tile = floor(prev_subtile[0]), floor(prev_subtile[1]) + tile_motion = Point(tile[0] - prev_tile[0], tile[1] - prev_tile[1]) + self = cls( + position=pixel, + motion=pixel_motion, + tile=tile, + tile_motion=tile_motion, + state=state, + **common, + **_unpack_sdl_event(sdl_event), + ) self.sdl_event = sdl_event return self - def __repr__(self) -> str: - return ("tcod.event.{}(position={!r}, motion={!r}, tile={!r}, tile_motion={!r}, state={!r})").format( - self.__class__.__name__, - tuple(self.position), - tuple(self.motion), - tuple(self.tile), - tuple(self.tile_motion), - MouseButtonMask(self.state), - ) - def __str__(self) -> str: - return ("<%s, motion=(x=%i, y=%i), tile_motion=(x=%i, y=%i)>") % ( - super().__str__().strip("<>"), - *self.motion, - *self.tile_motion, - ) +@attrs.define(slots=True, kw_only=True) +class MouseButtonEvent(Event): + """Mouse button event. + .. versionchanged:: 19.0 + `position` and `tile` now use floating point coordinates. -class MouseButtonEvent(MouseState): - """Mouse button event. + .. versionchanged:: 21.0 + No longer a subclass of :any:`MouseState`. + """ - Attributes: - type (str): Will be "MOUSEBUTTONDOWN" or "MOUSEBUTTONUP", - depending on the event. - position (Point): The pixel coordinates of the mouse. - tile (Point): The integer tile coordinates of the mouse on the screen. - button (int): Which mouse button. + position: Point[float] = attrs.field(default=Point(0.0, 0.0)) + """The coordinates of the mouse.""" + _tile: Point[int] | None = attrs.field(default=Point(0, 0), alias="tile") + """The tile integer coordinates of the mouse on the screen. Deprecated.""" + button: MouseButton + """Which mouse button index was pressed or released in this event. - This will be one of the following names: + .. versionchanged:: 21.0 + Is now strictly a :any:`MouseButton` type. + """ - * tcod.event.BUTTON_LEFT - * tcod.event.BUTTON_MIDDLE - * tcod.event.BUTTON_RIGHT - * tcod.event.BUTTON_X1 - * tcod.event.BUTTON_X2 + which: int = 0 + """The mouse device ID for this event. + .. versionadded:: 21.0 """ - def __init__( - self, - pixel: tuple[int, int] = (0, 0), - tile: tuple[int, int] | None = (0, 0), - button: int = 0, - ) -> None: - super().__init__(pixel, tile, button) + window_id: int = 0 + """The window ID with mouse focus. + + .. versionadded:: 21.0 + """ @property - def button(self) -> int: - return self.state + def integer_position(self) -> Point[int]: + """Integer coordinates of this event. - @button.setter - def button(self, value: int) -> None: - self.state = value + .. versionadded:: 21.1 + """ + x, y = self.position + return Point(floor(x), floor(y)) @classmethod - def from_sdl_event(cls, sdl_event: Any) -> Any: + def _from_sdl_event(cls, sdl_event: _C_SDL_Event) -> Self: button = sdl_event.button - pixel = button.x, button.y - subtile = _pixel_to_tile(*pixel) + pixel = Point(float(button.x), float(button.y)) + subtile = _pixel_to_tile(pixel) if subtile is None: - tile: tuple[int, int] | None = None + tile: Point[int] | None = None else: - tile = int(subtile[0]), int(subtile[1]) - self = cls(pixel, tile, button.button) + tile = Point(floor(subtile[0]), floor(subtile[1])) + self = cls( + position=pixel, + tile=tile, + button=MouseButton(button.button), + which=int(button.which), + window_id=int(button.windowID), + **_unpack_sdl_event(sdl_event), + ) self.sdl_event = sdl_event return self - def __repr__(self) -> str: - return "tcod.event.{}(position={!r}, tile={!r}, button={!r})".format( - self.__class__.__name__, - tuple(self.position), - tuple(self.tile), - MouseButton(self.button), - ) - - def __str__(self) -> str: - return " int: # noqa: D102 # Skip docstring for deprecated property + return int(self.button) +@attrs.define(slots=True, kw_only=True) class MouseButtonDown(MouseButtonEvent): - """Same as MouseButtonEvent but with ``type="MouseButtonDown"``.""" + """Mouse button has been pressed.""" +@attrs.define(slots=True, kw_only=True) class MouseButtonUp(MouseButtonEvent): - """Same as MouseButtonEvent but with ``type="MouseButtonUp"``.""" + """Mouse button has been released.""" +@attrs.define(slots=True, kw_only=True) class MouseWheel(Event): - """Mouse wheel event. - - Attributes: - type (str): Always "MOUSEWHEEL". - x (int): Horizontal scrolling. A positive value means scrolling right. - y (int): Vertical scrolling. A positive value means scrolling away from - the user. - flipped (bool): If True then the values of `x` and `y` are the opposite - of their usual values. This depends on the settings of - the Operating System. + """Mouse wheel event.""" + + x: int + """Horizontal scrolling. A positive value means scrolling right.""" + y: int + """Vertical scrolling. A positive value means scrolling away from the user.""" + flipped: bool + """If True then the values of `x` and `y` are the opposite of their usual values. + This depends on the operating system settings. """ - def __init__(self, x: int, y: int, flipped: bool = False) -> None: - super().__init__() - self.x = x - self.y = y - self.flipped = flipped + position: Point[float] = attrs.field(default=Point(0.0, 0.0)) + """Coordinates of the mouse for this event. + + .. versionadded:: 21.2 + """ + + which: int = 0 + """Mouse device ID for this event. + + .. versionadded:: 21.2 + """ + + window_id: int = 0 + """Window ID with mouse focus. + + .. versionadded:: 21.2 + """ + + @property + def integer_position(self) -> Point[int]: + """Integer coordinates of this event. + + .. versionadded:: 21.2 + """ + x, y = self.position + return Point(floor(x), floor(y)) @classmethod - def from_sdl_event(cls, sdl_event: Any) -> MouseWheel: + def _from_sdl_event(cls, sdl_event: _C_SDL_Event) -> Self: wheel = sdl_event.wheel - self = cls(wheel.x, wheel.y, bool(wheel.direction)) - self.sdl_event = sdl_event - return self - - def __repr__(self) -> str: - return "tcod.event.%s(x=%i, y=%i%s)" % ( - self.__class__.__name__, - self.x, - self.y, - ", flipped=True" if self.flipped else "", + return cls( + x=int(wheel.integer_x), + y=int(wheel.integer_y), + flipped=bool(wheel.direction), + position=Point(float(wheel.mouse_x), float(wheel.mouse_y)), + which=int(wheel.which), + window_id=int(wheel.windowID), + **_unpack_sdl_event(sdl_event), ) - def __str__(self) -> str: - return "<%s, x=%i, y=%i, flipped=%r)" % ( - super().__str__().strip("<>"), - self.x, - self.y, - self.flipped, - ) + +@runtime_checkable +class _MouseEventWithPosition(Protocol): + """Mouse event with position. Used internally to handle conversions.""" + + position: Point[float] + + +@runtime_checkable +class _MouseEventWithTile(Protocol): + """Mouse event with position and deprecated tile attribute. Used internally to handle conversions.""" + + position: Point[float] + _tile: Point[int] | None +@attrs.define(slots=True, kw_only=True) class TextInput(Event): """SDL text input event. - Attributes: - type (str): Always "TEXTINPUT". - text (str): A Unicode string with the input. + .. warning:: + These events are not enabled by default since `19.0`. + + Use :any:`Window.start_text_input` to enable this event. """ - def __init__(self, text: str) -> None: - super().__init__() - self.text = text + text: str + """A Unicode string with the input.""" @classmethod - def from_sdl_event(cls, sdl_event: Any) -> TextInput: - self = cls(ffi.string(sdl_event.text.text, 32).decode("utf8")) - self.sdl_event = sdl_event - return self + def _from_sdl_event(cls, sdl_event: _C_SDL_Event) -> Self: + return cls(text=str(ffi.string(sdl_event.text.text, 32), encoding="utf8"), **_unpack_sdl_event(sdl_event)) - def __repr__(self) -> str: - return f"tcod.event.{self.__class__.__name__}(text={self.text!r})" - def __str__(self) -> str: - return "<{}, text={!r})".format(super().__str__().strip("<>"), self.text) +_WindowTypes = Literal[ + "WindowShown", + "WindowHidden", + "WindowExposed", + "WindowMoved", + "WindowResized", + "PixelSizeChanged", + "MetalViewResized", + "WindowMinimized", + "WindowMaximized", + "WindowRestored", + "WindowEnter", + "WindowLeave", + "WindowFocusGained", + "WindowFocusLost", + "WindowClose", + "WindowTakeFocus", + "WindowHitTest", + "ICCProfileChanged", + "DisplayChanged", + "DisplayScaleChanged", + "SafeAreaChanged", + "Occluded", + "EnterFullscreen", + "LeaveFullscreen", + "Destroyed", + "HDRStateChanged", +] +@attrs.define(slots=True, kw_only=True) class WindowEvent(Event): - """A window event.""" - - type: Final[ # type: ignore[misc] # Narrowing final type. - Literal[ - "WindowShown", - "WindowHidden", - "WindowExposed", - "WindowMoved", - "WindowResized", - "WindowSizeChanged", - "WindowMinimized", - "WindowMaximized", - "WindowRestored", - "WindowEnter", - "WindowLeave", - "WindowFocusGained", - "WindowFocusLost", - "WindowClose", - "WindowTakeFocus", - "WindowHitTest", - ] - ] + """A window event. + + Example:: + + match event: + case tcod.event.WindowEvent(type="WindowShown", window_id=window_id): + print(f"Window {window_id} was shown") + case tcod.event.WindowEvent(type="WindowHidden", window_id=window_id): + print(f"Window {window_id} was hidden") + case tcod.event.WindowEvent(type="WindowExposed", window_id=window_id): + print(f"Window {window_id} was exposed and needs to be redrawn") + case tcod.event.WindowEvent(type="WindowMoved", data=(x, y), window_id=window_id): + print(f"Window {window_id} was moved to {x=},{y=}") + case tcod.event.WindowEvent(type="WindowResized", data=(width, height), window_id=window_id): + print(f"Window {window_id} was resized to {width=},{height=}") + case tcod.event.WindowEvent(type="WindowMinimized", window_id=window_id): + print(f"Window {window_id} was minimized") + case tcod.event.WindowEvent(type="WindowMaximized", window_id=window_id): + print(f"Window {window_id} was maximized") + case tcod.event.WindowEvent(type="WindowRestored", window_id=window_id): + print(f"Window {window_id} was restored") + case tcod.event.WindowEvent(type="WindowEnter", window_id=window_id): + print(f"Mouse cursor has entered window {window_id}") + case tcod.event.WindowEvent(type="WindowLeave", window_id=window_id): + print(f"Mouse cursor has left window {window_id}") + case tcod.event.WindowEvent(type="WindowFocusGained", window_id=window_id): + print(f"Window {window_id} has gained keyboard focus") + case tcod.event.WindowEvent(type="WindowFocusLost", window_id=window_id): + print(f"Window {window_id} has lost keyboard focus") + case tcod.event.WindowEvent(type="WindowClose", window_id=window_id): + print(f"Window {window_id} has been closed") + case tcod.event.WindowEvent(type="DisplayChanged", data=(display_id, _), window_id=window_id): + print(f"Window {window_id} has been moved to display {display_id}") + case tcod.event.WindowEvent(type=subtype, data=data, window_id=window_id): + print(f"Other window event {subtype} on window {window_id} with {data=}") + + .. versionchanged:: 21.0 + Added `data` and `window_id` attributes and added missing SDL3 window events. + """ + + type: Final[_WindowTypes] """The current window event. This can be one of various options.""" - @classmethod - def from_sdl_event(cls, sdl_event: Any) -> WindowEvent | Undefined: - if sdl_event.window.event not in cls.__WINDOW_TYPES: - return Undefined.from_sdl_event(sdl_event) - event_type: Final = cls.__WINDOW_TYPES[sdl_event.window.event] - self: WindowEvent - if sdl_event.window.event == lib.SDL_WINDOWEVENT_MOVED: - self = WindowMoved(sdl_event.window.data1, sdl_event.window.data2) - elif sdl_event.window.event in ( - lib.SDL_WINDOWEVENT_RESIZED, - lib.SDL_WINDOWEVENT_SIZE_CHANGED, - ): - self = WindowResized(event_type, sdl_event.window.data1, sdl_event.window.data2) - else: - self = cls(event_type) - self.sdl_event = sdl_event - return self + window_id: int + """The SDL window ID associated with this event.""" - def __repr__(self) -> str: - return f"tcod.event.{self.__class__.__name__}(type={self.type!r})" - - __WINDOW_TYPES = { - lib.SDL_WINDOWEVENT_SHOWN: "WindowShown", - lib.SDL_WINDOWEVENT_HIDDEN: "WindowHidden", - lib.SDL_WINDOWEVENT_EXPOSED: "WindowExposed", - lib.SDL_WINDOWEVENT_MOVED: "WindowMoved", - lib.SDL_WINDOWEVENT_RESIZED: "WindowResized", - lib.SDL_WINDOWEVENT_SIZE_CHANGED: "WindowSizeChanged", - lib.SDL_WINDOWEVENT_MINIMIZED: "WindowMinimized", - lib.SDL_WINDOWEVENT_MAXIMIZED: "WindowMaximized", - lib.SDL_WINDOWEVENT_RESTORED: "WindowRestored", - lib.SDL_WINDOWEVENT_ENTER: "WindowEnter", - lib.SDL_WINDOWEVENT_LEAVE: "WindowLeave", - lib.SDL_WINDOWEVENT_FOCUS_GAINED: "WindowFocusGained", - lib.SDL_WINDOWEVENT_FOCUS_LOST: "WindowFocusLost", - lib.SDL_WINDOWEVENT_CLOSE: "WindowClose", - lib.SDL_WINDOWEVENT_TAKE_FOCUS: "WindowTakeFocus", - lib.SDL_WINDOWEVENT_HIT_TEST: "WindowHitTest", - } + data: tuple[int, int] + """The SDL data associated with this event. What these values are for depends on the event sub-type.""" + @classmethod + def _from_sdl_event(cls, sdl_event: _C_SDL_Event) -> WindowEvent | Undefined: + if sdl_event.type not in _WINDOW_TYPES_FROM_ENUM: + return Undefined._from_sdl_event(sdl_event) + event_type: Final = _WINDOW_TYPES_FROM_ENUM[sdl_event.type] + new_cls = cls + if sdl_event.type == lib.SDL_EVENT_WINDOW_MOVED: + new_cls = WindowMoved + elif sdl_event.type == lib.SDL_EVENT_WINDOW_RESIZED: + new_cls = WindowResized + return new_cls( + type=event_type, + window_id=int(sdl_event.window.windowID), + data=(int(sdl_event.window.data1), int(sdl_event.window.data2)), + **_unpack_sdl_event(sdl_event), + ) -class WindowMoved(WindowEvent): - """Window moved event. - Attributes: - x (int): Movement on the x-axis. - y (int): Movement on the y-axis. - """ +_WINDOW_TYPES_FROM_ENUM: Final[dict[int, _WindowTypes]] = { + lib.SDL_EVENT_WINDOW_SHOWN: "WindowShown", + lib.SDL_EVENT_WINDOW_HIDDEN: "WindowHidden", + lib.SDL_EVENT_WINDOW_EXPOSED: "WindowExposed", + lib.SDL_EVENT_WINDOW_MOVED: "WindowMoved", + lib.SDL_EVENT_WINDOW_RESIZED: "WindowResized", + lib.SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED: "PixelSizeChanged", + lib.SDL_EVENT_WINDOW_METAL_VIEW_RESIZED: "MetalViewResized", + lib.SDL_EVENT_WINDOW_MINIMIZED: "WindowMinimized", + lib.SDL_EVENT_WINDOW_MAXIMIZED: "WindowMaximized", + lib.SDL_EVENT_WINDOW_RESTORED: "WindowRestored", + lib.SDL_EVENT_WINDOW_MOUSE_ENTER: "WindowEnter", + lib.SDL_EVENT_WINDOW_MOUSE_LEAVE: "WindowLeave", + lib.SDL_EVENT_WINDOW_FOCUS_GAINED: "WindowFocusGained", + lib.SDL_EVENT_WINDOW_FOCUS_LOST: "WindowFocusLost", + lib.SDL_EVENT_WINDOW_CLOSE_REQUESTED: "WindowClose", + lib.SDL_EVENT_WINDOW_HIT_TEST: "WindowHitTest", + lib.SDL_EVENT_WINDOW_ICCPROF_CHANGED: "ICCProfileChanged", + lib.SDL_EVENT_WINDOW_DISPLAY_CHANGED: "DisplayChanged", + lib.SDL_EVENT_WINDOW_DISPLAY_SCALE_CHANGED: "DisplayScaleChanged", + lib.SDL_EVENT_WINDOW_SAFE_AREA_CHANGED: "SafeAreaChanged", + lib.SDL_EVENT_WINDOW_OCCLUDED: "Occluded", + lib.SDL_EVENT_WINDOW_ENTER_FULLSCREEN: "EnterFullscreen", + lib.SDL_EVENT_WINDOW_LEAVE_FULLSCREEN: "LeaveFullscreen", + lib.SDL_EVENT_WINDOW_DESTROYED: "Destroyed", + lib.SDL_EVENT_WINDOW_HDR_STATE_CHANGED: "HDRStateChanged", +} - type: Final[Literal["WINDOWMOVED"]] # type: ignore[assignment,misc] - """Always "WINDOWMOVED".""" - def __init__(self, x: int, y: int) -> None: - super().__init__(None) - self.x = x - self.y = y +@attrs.define(slots=True, kw_only=True) +class WindowMoved(WindowEvent): + """Window moved event.""" - def __repr__(self) -> str: - return "tcod.event.{}(type={!r}, x={!r}, y={!r})".format( - self.__class__.__name__, - self.type, - self.x, - self.y, - ) + @property + def x(self) -> int: + """Movement on the x-axis.""" + return self.data[0] - def __str__(self) -> str: - return "<{}, x={!r}, y={!r})".format( - super().__str__().strip("<>"), - self.x, - self.y, - ) + @property + def y(self) -> int: + """Movement on the y-axis.""" + return self.data[1] +@attrs.define(slots=True, kw_only=True) class WindowResized(WindowEvent): """Window resized event. - Attributes: - width (int): The current width of the window. - height (int): The current height of the window. + .. versionchanged:: 19.4 + Removed "WindowSizeChanged" type. """ - type: Final[Literal["WindowResized", "WindowSizeChanged"]] # type: ignore[misc] - """WindowResized" or "WindowSizeChanged""" - - def __init__(self, type: str, width: int, height: int) -> None: - super().__init__(type) - self.width = width - self.height = height - - def __repr__(self) -> str: - return "tcod.event.{}(type={!r}, width={!r}, height={!r})".format( - self.__class__.__name__, - self.type, - self.width, - self.height, - ) + @property + def width(self) -> int: + """The current width of the window.""" + return self.data[0] - def __str__(self) -> str: - return "<{}, width={!r}, height={!r})".format( - super().__str__().strip("<>"), - self.width, - self.height, - ) + @property + def height(self) -> int: + """The current height of the window.""" + return self.data[1] +@attrs.define(slots=True, kw_only=True) class JoystickEvent(Event): """A base class for joystick events. .. versionadded:: 13.8 """ - def __init__(self, type: str, which: int) -> None: - super().__init__(type) - self.which = which - """The ID of the joystick this event is for.""" + which: int + """The ID of the joystick this event is for.""" @property def joystick(self) -> tcod.sdl.joystick.Joystick: - if self.type == "JOYDEVICEADDED": + """The :any:`Joystick` for this event.""" + if isinstance(self, JoystickDevice) and self.type == "JOYDEVICEADDED": return tcod.sdl.joystick.Joystick._open(self.which) return tcod.sdl.joystick.Joystick._from_instance_id(self.which) - def __repr__(self) -> str: - return f"tcod.event.{self.__class__.__name__}" f"(type={self.type!r}, which={self.which})" - - def __str__(self) -> str: - prefix = super().__str__().strip("<>") - return f"<{prefix}, which={self.which}>" - +@attrs.define(slots=True, kw_only=True) class JoystickAxis(JoystickEvent): """When a joystick axis changes in value. @@ -877,31 +989,24 @@ class JoystickAxis(JoystickEvent): :any:`tcod.sdl.joystick` """ - which: int - """The ID of the joystick this event is for.""" + _type: Final[Literal["JOYAXISMOTION"]] = "JOYAXISMOTION" - def __init__(self, type: str, which: int, axis: int, value: int) -> None: - super().__init__(type, which) - self.axis = axis - """The index of the changed axis.""" - self.value = value - """The raw value of the axis in the range -32768 to 32767.""" + axis: int + """The index of the changed axis.""" + value: int + """The raw value of the axis in the range -32768 to 32767.""" @classmethod - def from_sdl_event(cls, sdl_event: Any) -> JoystickAxis: - return cls("JOYAXISMOTION", sdl_event.jaxis.which, sdl_event.jaxis.axis, sdl_event.jaxis.value) - - def __repr__(self) -> str: - return ( - f"tcod.event.{self.__class__.__name__}" - f"(type={self.type!r}, which={self.which}, axis={self.axis}, value={self.value})" + def _from_sdl_event(cls, sdl_event: _C_SDL_Event) -> Self: + return cls( + which=int(sdl_event.jaxis.which), + axis=int(sdl_event.jaxis.axis), + value=int(sdl_event.jaxis.value), + **_unpack_sdl_event(sdl_event), ) - def __str__(self) -> str: - prefix = super().__str__().strip("<>") - return f"<{prefix}, axis={self.axis}, value={self.value}>" - +@attrs.define(slots=True, kw_only=True) class JoystickBall(JoystickEvent): """When a joystick ball is moved. @@ -911,35 +1016,27 @@ class JoystickBall(JoystickEvent): :any:`tcod.sdl.joystick` """ - which: int - """The ID of the joystick this event is for.""" + _type: Final[Literal["JOYBALLMOTION"]] = "JOYBALLMOTION" - def __init__(self, type: str, which: int, ball: int, dx: int, dy: int) -> None: - super().__init__(type, which) - self.ball = ball - """The index of the moved ball.""" - self.dx = dx - """The X motion of the ball.""" - self.dy = dy - """The Y motion of the ball.""" + ball: int + """The index of the moved ball.""" + dx: int + """The X motion of the ball.""" + dy: int + """The Y motion of the ball.""" @classmethod - def from_sdl_event(cls, sdl_event: Any) -> JoystickBall: + def _from_sdl_event(cls, sdl_event: _C_SDL_Event) -> Self: return cls( - "JOYBALLMOTION", sdl_event.jball.which, sdl_event.jball.ball, sdl_event.jball.xrel, sdl_event.jball.yrel + which=int(sdl_event.jball.which), + ball=int(sdl_event.jball.ball), + dx=int(sdl_event.jball.xrel), + dy=int(sdl_event.jball.yrel), + **_unpack_sdl_event(sdl_event), ) - def __repr__(self) -> str: - return ( - f"tcod.event.{self.__class__.__name__}" - f"(type={self.type!r}, which={self.which}, ball={self.ball}, dx={self.dx}, dy={self.dy})" - ) - - def __str__(self) -> str: - prefix = super().__str__().strip("<>") - return f"<{prefix}, ball={self.ball}, dx={self.dx}, dy={self.dy}>" - +@attrs.define(slots=True, kw_only=True) class JoystickHat(JoystickEvent): """When a joystick hat changes direction. @@ -949,30 +1046,20 @@ class JoystickHat(JoystickEvent): :any:`tcod.sdl.joystick` """ - which: int - """The ID of the joystick this event is for.""" + _type: Final[Literal["JOYHATMOTION"]] = "JOYHATMOTION" - def __init__(self, type: str, which: int, x: Literal[-1, 0, 1], y: Literal[-1, 0, 1]) -> None: - super().__init__(type, which) - self.x = x - """The new X direction of the hat.""" - self.y = y - """The new Y direction of the hat.""" + x: Literal[-1, 0, 1] + """The new X direction of the hat.""" + y: Literal[-1, 0, 1] + """The new Y direction of the hat.""" @classmethod - def from_sdl_event(cls, sdl_event: Any) -> JoystickHat: - return cls("JOYHATMOTION", sdl_event.jhat.which, *_HAT_DIRECTIONS[sdl_event.jhat.hat]) - - def __repr__(self) -> str: - return ( - f"tcod.event.{self.__class__.__name__}" f"(type={self.type!r}, which={self.which}, x={self.x}, y={self.y})" - ) - - def __str__(self) -> str: - prefix = super().__str__().strip("<>") - return f"<{prefix}, x={self.x}, y={self.y}>" + def _from_sdl_event(cls, sdl_event: _C_SDL_Event) -> Self: + x, y = _HAT_DIRECTIONS[sdl_event.jhat.hat] + return cls(which=int(sdl_event.jhat.which), x=x, y=y, **_unpack_sdl_event(sdl_event)) +@attrs.define(slots=True, kw_only=True) class JoystickButton(JoystickEvent): """When a joystick button is pressed or released. @@ -988,32 +1075,32 @@ class JoystickButton(JoystickEvent): print(f"Released {button=} on controller {which}.") """ - which: int - """The ID of the joystick this event is for.""" - - def __init__(self, type: str, which: int, button: int) -> None: - super().__init__(type, which) - self.button = button - """The index of the button this event is for.""" + button: int + """The index of the button this event is for.""" + pressed: bool + """True if the button was pressed, False if the button was released.""" @property - def pressed(self) -> bool: - """True if the joystick button has been pressed, False when the button was released.""" - return self.type == "JOYBUTTONDOWN" - - @classmethod - def from_sdl_event(cls, sdl_event: Any) -> JoystickButton: - type = {lib.SDL_JOYBUTTONDOWN: "JOYBUTTONDOWN", lib.SDL_JOYBUTTONUP: "JOYBUTTONUP"}[sdl_event.type] - return cls(type, sdl_event.jbutton.which, sdl_event.jbutton.button) + @deprecated("Check 'JoystickButton.pressed' instead of '.type'.") + def type(self) -> Literal["JOYBUTTONUP", "JOYBUTTONDOWN"]: + """Button state as a string. - def __repr__(self) -> str: - return f"tcod.event.{self.__class__.__name__}" f"(type={self.type!r}, which={self.which}, button={self.button})" + .. deprecated:: 21.0 + Use :any:`pressed` instead. + """ + return ("JOYBUTTONUP", "JOYBUTTONDOWN")[self.pressed] - def __str__(self) -> str: - prefix = super().__str__().strip("<>") - return f"<{prefix}, button={self.button}>" + @classmethod + def _from_sdl_event(cls, sdl_event: _C_SDL_Event) -> Self: + return cls( + which=int(sdl_event.jbutton.which), + button=int(sdl_event.jbutton.button), + pressed=bool(sdl_event.jbutton.down), + **_unpack_sdl_event(sdl_event), + ) +@attrs.define(slots=True, kw_only=True) class JoystickDevice(JoystickEvent): """An event for when a joystick is added or removed. @@ -1030,7 +1117,7 @@ class JoystickDevice(JoystickEvent): joysticks.remove(joystick) """ - type: Final[Literal["JOYDEVICEADDED", "JOYDEVICEREMOVED"]] # type: ignore[misc] + type: Final[Literal["JOYDEVICEADDED", "JOYDEVICEREMOVED"]] which: int """When type="JOYDEVICEADDED" this is the device ID. @@ -1038,183 +1125,255 @@ class JoystickDevice(JoystickEvent): """ @classmethod - def from_sdl_event(cls, sdl_event: Any) -> JoystickDevice: - type = {lib.SDL_JOYDEVICEADDED: "JOYDEVICEADDED", lib.SDL_JOYDEVICEREMOVED: "JOYDEVICEREMOVED"}[sdl_event.type] - return cls(type, sdl_event.jdevice.which) + def _from_sdl_event(cls, sdl_event: _C_SDL_Event) -> Self: + types: Final[dict[int, Literal["JOYDEVICEADDED", "JOYDEVICEREMOVED"]]] = { + lib.SDL_EVENT_JOYSTICK_ADDED: "JOYDEVICEADDED", + lib.SDL_EVENT_JOYSTICK_REMOVED: "JOYDEVICEREMOVED", + } + return cls(type=types[sdl_event.type], which=int(sdl_event.jdevice.which), **_unpack_sdl_event(sdl_event)) +@attrs.define(slots=True, kw_only=True) class ControllerEvent(Event): """Base class for controller events. .. versionadded:: 13.8 """ - def __init__(self, type: str, which: int) -> None: - super().__init__(type) - self.which = which - """The ID of the joystick this event is for.""" + which: int + """The ID of the controller this event is for.""" @property def controller(self) -> tcod.sdl.joystick.GameController: """The :any:`GameController` for this event.""" - if self.type == "CONTROLLERDEVICEADDED": + if isinstance(self, ControllerDevice) and self.type == "CONTROLLERDEVICEADDED": return tcod.sdl.joystick.GameController._open(self.which) return tcod.sdl.joystick.GameController._from_instance_id(self.which) - def __repr__(self) -> str: - return f"tcod.event.{self.__class__.__name__}" f"(type={self.type!r}, which={self.which})" - - def __str__(self) -> str: - prefix = super().__str__().strip("<>") - return f"<{prefix}, which={self.which}>" - +@attrs.define(slots=True, kw_only=True) class ControllerAxis(ControllerEvent): """When a controller axis is moved. .. versionadded:: 13.8 """ - type: Final[Literal["CONTROLLERAXISMOTION"]] # type: ignore[misc] + _type: Final[Literal["CONTROLLERAXISMOTION"]] = "CONTROLLERAXISMOTION" - def __init__(self, type: str, which: int, axis: tcod.sdl.joystick.ControllerAxis, value: int) -> None: - super().__init__(type, which) - self.axis = axis - """Which axis is being moved. One of :any:`ControllerAxis`.""" - self.value = value - """The new value of this events axis. + axis: int + """Which axis is being moved. One of :any:`ControllerAxis`.""" + value: int + """The new value of this events axis. - This will be -32768 to 32767 for all axes except for triggers which are 0 to 32767 instead.""" + This will be -32768 to 32767 for all axes except for triggers which are 0 to 32767 instead.""" @classmethod - def from_sdl_event(cls, sdl_event: Any) -> ControllerAxis: + def _from_sdl_event(cls, sdl_event: _C_SDL_Event) -> Self: return cls( - "CONTROLLERAXISMOTION", - sdl_event.caxis.which, - tcod.sdl.joystick.ControllerAxis(sdl_event.caxis.axis), - sdl_event.caxis.value, - ) - - def __repr__(self) -> str: - return ( - f"tcod.event.{self.__class__.__name__}" - f"(type={self.type!r}, which={self.which}, axis={self.axis}, value={self.value})" + which=int(sdl_event.gaxis.which), + axis=tcod.sdl.joystick.ControllerAxis(sdl_event.gaxis.axis), + value=int(sdl_event.gaxis.value), + **_unpack_sdl_event(sdl_event), ) - def __str__(self) -> str: - prefix = super().__str__().strip("<>") - return f"<{prefix}, axis={self.axis}, value={self.value}>" - +@attrs.define(slots=True, kw_only=True) class ControllerButton(ControllerEvent): """When a controller button is pressed or released. .. versionadded:: 13.8 """ - type: Final[Literal["CONTROLLERBUTTONDOWN", "CONTROLLERBUTTONUP"]] # type: ignore[misc] + button: tcod.sdl.joystick.ControllerButton + """The button for this event. One of :any:`ControllerButton`.""" + pressed: bool + """True if the button was pressed, False if it was released.""" - def __init__(self, type: str, which: int, button: tcod.sdl.joystick.ControllerButton, pressed: bool) -> None: - super().__init__(type, which) - self.button = button - """The button for this event. One of :any:`ControllerButton`.""" - self.pressed = pressed - """True if the button was pressed, False if it was released.""" + @property + @deprecated("Check 'ControllerButton.pressed' instead of '.type'.") + def type(self) -> Literal["CONTROLLERBUTTONUP", "CONTROLLERBUTTONDOWN"]: + """Button state as a string. + + .. deprecated:: 21.0 + Use :any:`pressed` instead. + """ + return ("CONTROLLERBUTTONUP", "CONTROLLERBUTTONDOWN")[self.pressed] @classmethod - def from_sdl_event(cls, sdl_event: Any) -> ControllerButton: - type = { - lib.SDL_CONTROLLERBUTTONDOWN: "CONTROLLERBUTTONDOWN", - lib.SDL_CONTROLLERBUTTONUP: "CONTROLLERBUTTONUP", - }[sdl_event.type] + def _from_sdl_event(cls, sdl_event: _C_SDL_Event) -> Self: return cls( - type, - sdl_event.cbutton.which, - tcod.sdl.joystick.ControllerButton(sdl_event.cbutton.button), - sdl_event.cbutton.state == lib.SDL_PRESSED, - ) - - def __repr__(self) -> str: - return ( - f"tcod.event.{self.__class__.__name__}" - f"(type={self.type!r}, which={self.which}, button={self.button}, pressed={self.pressed})" + which=int(sdl_event.gbutton.which), + button=tcod.sdl.joystick.ControllerButton(sdl_event.gbutton.button), + pressed=bool(sdl_event.gbutton.down), + **_unpack_sdl_event(sdl_event), ) - def __str__(self) -> str: - prefix = super().__str__().strip("<>") - return f"<{prefix}, button={self.button}, pressed={self.pressed}>" - +@attrs.define(slots=True, kw_only=True) class ControllerDevice(ControllerEvent): """When a controller is added, removed, or remapped. .. versionadded:: 13.8 """ - type: Final[Literal["CONTROLLERDEVICEADDED", "CONTROLLERDEVICEREMOVED", "CONTROLLERDEVICEREMAPPED"]] # type: ignore[misc] + type: Final[Literal["CONTROLLERDEVICEADDED", "CONTROLLERDEVICEREMOVED", "CONTROLLERDEVICEREMAPPED"]] + + @classmethod + def _from_sdl_event(cls, sdl_event: _C_SDL_Event) -> Self: + types: dict[int, Literal["CONTROLLERDEVICEADDED", "CONTROLLERDEVICEREMOVED", "CONTROLLERDEVICEREMAPPED"]] = { + lib.SDL_EVENT_GAMEPAD_ADDED: "CONTROLLERDEVICEADDED", + lib.SDL_EVENT_GAMEPAD_REMOVED: "CONTROLLERDEVICEREMOVED", + lib.SDL_EVENT_GAMEPAD_REMAPPED: "CONTROLLERDEVICEREMAPPED", + } + return cls(type=types[sdl_event.type], which=int(sdl_event.gdevice.which), **_unpack_sdl_event(sdl_event)) + + +@attrs.define(slots=True, kw_only=True) +class ClipboardUpdate(Event): + """Announces changed contents of the clipboard. + + .. versionadded:: 21.0 + """ + + mime_types: tuple[str, ...] + """The MIME types of the clipboard.""" @classmethod - def from_sdl_event(cls, sdl_event: Any) -> ControllerDevice: - type = { - lib.SDL_CONTROLLERDEVICEADDED: "CONTROLLERDEVICEADDED", - lib.SDL_CONTROLLERDEVICEREMOVED: "CONTROLLERDEVICEREMOVED", - lib.SDL_CONTROLLERDEVICEREMAPPED: "CONTROLLERDEVICEREMAPPED", - }[sdl_event.type] - return cls(type, sdl_event.cdevice.which) + def _from_sdl_event(cls, sdl_event: _C_SDL_Event) -> Self: + return cls( + mime_types=tuple( + str(ffi.string(sdl_event.clipboard.mime_types[i]), encoding="utf8") + for i in range(sdl_event.clipboard.num_mime_types) + ), + **_unpack_sdl_event(sdl_event), + ) +@attrs.define(slots=True, kw_only=True) +class Drop(Event): + """Handle dropping text or files on the window. + + Example:: + + match event: + case tcod.event.Drop(type="BEGIN"): + print("Object dragged over the window") + case tcod.event.Drop(type="POSITION", position=position): + pass + case tcod.event.Drop(type="TEXT", position=position, text=text): + print(f"Dropped {text=} at {position=}") + case tcod.event.Drop(type="FILE", position=position, path=path): + print(f"Dropped {path=} at {position=}") + case tcod.event.Drop(type="COMPLETE"): + print("Drop handling finished") + + .. versionadded:: 21.0 + """ + + type: Literal["BEGIN", "FILE", "TEXT", "COMPLETE", "POSITION"] + """The subtype of this event.""" + window_id: int + """The active window ID for this event.""" + position: Point[float] + """Mouse position relative to the window. Available in all subtypes except for ``type="BEGIN"``.""" + source: str + """The source app for this event, or an empty string if unavailable.""" + text: str + """The dropped data of a ``Drop(type="TEXT")`` or ``Drop(type="FILE")`` event. + + - If ``Drop(type="TEXT")`` then `text` is the dropped string. + - If ``Drop(type="FILE")`` then `text` is the str path of the dropped file. + Alternatively :any:`path` can be used. + - Otherwise `text` is an empty string. + """ + + @property + def path(self) -> Path: + """Return the current `text` as a :any:`Path`.""" + return Path(self.text) + + @classmethod + def _from_sdl_event(cls, sdl_event: _C_SDL_Event) -> Self: + types: dict[int, Literal["BEGIN", "FILE", "TEXT", "COMPLETE", "POSITION"]] = { + lib.SDL_EVENT_DROP_BEGIN: "BEGIN", + lib.SDL_EVENT_DROP_FILE: "FILE", + lib.SDL_EVENT_DROP_TEXT: "TEXT", + lib.SDL_EVENT_DROP_COMPLETE: "COMPLETE", + lib.SDL_EVENT_DROP_POSITION: "POSITION", + } + return cls( + type=types[sdl_event.drop.type], + window_id=int(sdl_event.drop.windowID), + position=Point(float(sdl_event.drop.x), float(sdl_event.drop.y)), + source=str(ffi.string(sdl_event.drop.source), encoding="utf8") if sdl_event.drop.source else "", + text=str(ffi.string(sdl_event.drop.data), encoding="utf8") if sdl_event.drop.data else "", + **_unpack_sdl_event(sdl_event), + ) + + +@functools.cache +def _find_event_name(index: int, /) -> str: + """Return the SDL event name for this index.""" + for attr in dir(lib): + if attr.startswith("SDL_EVENT_") and getattr(lib, attr) == index: + return attr + return "???" + + +@attrs.define(slots=True, kw_only=True) class Undefined(Event): """This class is a place holder for SDL events without their own tcod.event class.""" - def __init__(self) -> None: - super().__init__("") - @classmethod - def from_sdl_event(cls, sdl_event: Any) -> Undefined: - self = cls() - self.sdl_event = sdl_event - return self + def _from_sdl_event(cls, sdl_event: _C_SDL_Event) -> Self: + return cls(**_unpack_sdl_event(sdl_event)) - def __str__(self) -> str: - if self.sdl_event: - return "" % self.sdl_event.type - return "" + def __repr__(self) -> str: + """Return debug info for this undefined event, including the SDL event name.""" + return f"" _SDL_TO_CLASS_TABLE: dict[int, type[Event]] = { - lib.SDL_QUIT: Quit, - lib.SDL_KEYDOWN: KeyDown, - lib.SDL_KEYUP: KeyUp, - lib.SDL_MOUSEMOTION: MouseMotion, - lib.SDL_MOUSEBUTTONDOWN: MouseButtonDown, - lib.SDL_MOUSEBUTTONUP: MouseButtonUp, - lib.SDL_MOUSEWHEEL: MouseWheel, - lib.SDL_TEXTINPUT: TextInput, - lib.SDL_WINDOWEVENT: WindowEvent, - lib.SDL_JOYAXISMOTION: JoystickAxis, - lib.SDL_JOYBALLMOTION: JoystickBall, - lib.SDL_JOYHATMOTION: JoystickHat, - lib.SDL_JOYBUTTONDOWN: JoystickButton, - lib.SDL_JOYBUTTONUP: JoystickButton, - lib.SDL_JOYDEVICEADDED: JoystickDevice, - lib.SDL_JOYDEVICEREMOVED: JoystickDevice, - lib.SDL_CONTROLLERAXISMOTION: ControllerAxis, - lib.SDL_CONTROLLERBUTTONDOWN: ControllerButton, - lib.SDL_CONTROLLERBUTTONUP: ControllerButton, - lib.SDL_CONTROLLERDEVICEADDED: ControllerDevice, - lib.SDL_CONTROLLERDEVICEREMOVED: ControllerDevice, - lib.SDL_CONTROLLERDEVICEREMAPPED: ControllerDevice, + lib.SDL_EVENT_QUIT: Quit, + lib.SDL_EVENT_KEY_DOWN: KeyDown, + lib.SDL_EVENT_KEY_UP: KeyUp, + lib.SDL_EVENT_MOUSE_MOTION: MouseMotion, + lib.SDL_EVENT_MOUSE_BUTTON_DOWN: MouseButtonDown, + lib.SDL_EVENT_MOUSE_BUTTON_UP: MouseButtonUp, + lib.SDL_EVENT_MOUSE_WHEEL: MouseWheel, + lib.SDL_EVENT_TEXT_INPUT: TextInput, + lib.SDL_EVENT_JOYSTICK_AXIS_MOTION: JoystickAxis, + lib.SDL_EVENT_JOYSTICK_BALL_MOTION: JoystickBall, + lib.SDL_EVENT_JOYSTICK_HAT_MOTION: JoystickHat, + lib.SDL_EVENT_JOYSTICK_BUTTON_DOWN: JoystickButton, + lib.SDL_EVENT_JOYSTICK_BUTTON_UP: JoystickButton, + lib.SDL_EVENT_JOYSTICK_ADDED: JoystickDevice, + lib.SDL_EVENT_JOYSTICK_REMOVED: JoystickDevice, + lib.SDL_EVENT_GAMEPAD_AXIS_MOTION: ControllerAxis, + lib.SDL_EVENT_GAMEPAD_BUTTON_DOWN: ControllerButton, + lib.SDL_EVENT_GAMEPAD_BUTTON_UP: ControllerButton, + lib.SDL_EVENT_GAMEPAD_ADDED: ControllerDevice, + lib.SDL_EVENT_GAMEPAD_REMOVED: ControllerDevice, + lib.SDL_EVENT_GAMEPAD_REMAPPED: ControllerDevice, + lib.SDL_EVENT_CLIPBOARD_UPDATE: ClipboardUpdate, + lib.SDL_EVENT_DROP_BEGIN: Drop, + lib.SDL_EVENT_DROP_FILE: Drop, + lib.SDL_EVENT_DROP_TEXT: Drop, + lib.SDL_EVENT_DROP_COMPLETE: Drop, + lib.SDL_EVENT_DROP_POSITION: Drop, } -def _parse_event(sdl_event: Any) -> Event: +def _parse_event(sdl_event: _C_SDL_Event) -> Event: """Convert a C SDL_Event* type into a tcod Event sub-class.""" - if sdl_event.type not in _SDL_TO_CLASS_TABLE: - return Undefined.from_sdl_event(sdl_event) - return _SDL_TO_CLASS_TABLE[sdl_event.type].from_sdl_event(sdl_event) + if sdl_event.type in _SDL_TO_CLASS_TABLE: + return _SDL_TO_CLASS_TABLE[sdl_event.type]._from_sdl_event(sdl_event) + if sdl_event.type in _WINDOW_TYPES_FROM_ENUM: + return WindowEvent._from_sdl_event(sdl_event) + return Undefined._from_sdl_event(sdl_event) -def get() -> Iterator[Any]: +def get() -> Iterator[Event]: """Return an iterator for all pending events. Events are processed as the iterator is consumed. @@ -1222,15 +1381,19 @@ def get() -> Iterator[Any]: It is also safe to call this function inside of a loop that is already handling events (the event iterator is reentrant.) """ + if not lib.SDL_WasInit(tcod.sdl.sys.Subsystem.EVENTS): + warnings.warn( + "Events polled before SDL was initialized.", + RuntimeWarning, + stacklevel=1, + ) + return sdl_event = ffi.new("SDL_Event*") while lib.SDL_PollEvent(sdl_event): - if sdl_event.type in _SDL_TO_CLASS_TABLE: - yield _SDL_TO_CLASS_TABLE[sdl_event.type].from_sdl_event(sdl_event) - else: - yield Undefined.from_sdl_event(sdl_event) + yield _parse_event(sdl_event) -def wait(timeout: float | None = None) -> Iterator[Any]: +def wait(timeout: float | None = None) -> Iterator[Event]: """Block until events exist, then return an event iterator. `timeout` is the maximum number of seconds to wait as a floating point @@ -1261,6 +1424,11 @@ def wait(timeout: float | None = None) -> Iterator[Any]: return get() +@deprecated( + """EventDispatch is no longer maintained. +Event dispatching should be handled via a single custom method in a Protocol instead of this class.""", + category=DeprecationWarning, +) class EventDispatch(Generic[T]): '''Dispatches events to methods depending on the events type attribute. @@ -1271,6 +1439,10 @@ class EventDispatch(Generic[T]): This is now a generic class. The type hints at the return value of :any:`dispatch` and the `ev_*` methods. + .. deprecated:: 18.0 + Event dispatch should be handled via a single custom method in a :class:`~typing.Protocol` instead of this class. + Note that events can and should be handled using :ref:`match`. + Example:: import tcod @@ -1367,7 +1539,7 @@ def cmd_quit(self) -> None: __slots__ = () - def dispatch(self, event: Any) -> T | None: + def dispatch(self, event: Any) -> T | None: # noqa: ANN401 """Send an event to an `ev_*` method. `*` will be the `event.type` attribute converted to lower-case. @@ -1393,168 +1565,165 @@ def dispatch(self, event: Any) -> T | None: return None return func(event) - def event_get(self) -> None: + def event_get(self) -> None: # noqa: D102 for event in get(): self.dispatch(event) - def event_wait(self, timeout: float | None) -> None: + def event_wait(self, timeout: float | None) -> None: # noqa: D102 wait(timeout) self.event_get() - def ev_quit(self, event: tcod.event.Quit) -> T | None: + def ev_quit(self, event: tcod.event.Quit, /) -> T | None: """Called when the termination of the program is requested.""" - def ev_keydown(self, event: tcod.event.KeyDown) -> T | None: + def ev_keydown(self, event: tcod.event.KeyDown, /) -> T | None: """Called when a keyboard key is pressed or repeated.""" - def ev_keyup(self, event: tcod.event.KeyUp) -> T | None: + def ev_keyup(self, event: tcod.event.KeyUp, /) -> T | None: """Called when a keyboard key is released.""" - def ev_mousemotion(self, event: tcod.event.MouseMotion) -> T | None: + def ev_mousemotion(self, event: tcod.event.MouseMotion, /) -> T | None: """Called when the mouse is moved.""" - def ev_mousebuttondown(self, event: tcod.event.MouseButtonDown) -> T | None: + def ev_mousebuttondown(self, event: tcod.event.MouseButtonDown, /) -> T | None: """Called when a mouse button is pressed.""" - def ev_mousebuttonup(self, event: tcod.event.MouseButtonUp) -> T | None: + def ev_mousebuttonup(self, event: tcod.event.MouseButtonUp, /) -> T | None: """Called when a mouse button is released.""" - def ev_mousewheel(self, event: tcod.event.MouseWheel) -> T | None: + def ev_mousewheel(self, event: tcod.event.MouseWheel, /) -> T | None: """Called when the mouse wheel is scrolled.""" - def ev_textinput(self, event: tcod.event.TextInput) -> T | None: + def ev_textinput(self, event: tcod.event.TextInput, /) -> T | None: """Called to handle Unicode input.""" - def ev_windowshown(self, event: tcod.event.WindowEvent) -> T | None: + def ev_windowshown(self, event: tcod.event.WindowEvent, /) -> T | None: """Called when the window is shown.""" - def ev_windowhidden(self, event: tcod.event.WindowEvent) -> T | None: + def ev_windowhidden(self, event: tcod.event.WindowEvent, /) -> T | None: """Called when the window is hidden.""" - def ev_windowexposed(self, event: tcod.event.WindowEvent) -> T | None: + def ev_windowexposed(self, event: tcod.event.WindowEvent, /) -> T | None: """Called when a window is exposed, and needs to be refreshed. This usually means a call to :any:`libtcodpy.console_flush` is necessary. """ - def ev_windowmoved(self, event: tcod.event.WindowMoved) -> T | None: + def ev_windowmoved(self, event: tcod.event.WindowMoved, /) -> T | None: """Called when the window is moved.""" - def ev_windowresized(self, event: tcod.event.WindowResized) -> T | None: + def ev_windowresized(self, event: tcod.event.WindowResized, /) -> T | None: """Called when the window is resized.""" - def ev_windowsizechanged(self, event: tcod.event.WindowResized) -> T | None: - """Called when the system or user changes the size of the window.""" - - def ev_windowminimized(self, event: tcod.event.WindowEvent) -> T | None: + def ev_windowminimized(self, event: tcod.event.WindowEvent, /) -> T | None: """Called when the window is minimized.""" - def ev_windowmaximized(self, event: tcod.event.WindowEvent) -> T | None: + def ev_windowmaximized(self, event: tcod.event.WindowEvent, /) -> T | None: """Called when the window is maximized.""" - def ev_windowrestored(self, event: tcod.event.WindowEvent) -> T | None: + def ev_windowrestored(self, event: tcod.event.WindowEvent, /) -> T | None: """Called when the window is restored.""" - def ev_windowenter(self, event: tcod.event.WindowEvent) -> T | None: + def ev_windowenter(self, event: tcod.event.WindowEvent, /) -> T | None: """Called when the window gains mouse focus.""" - def ev_windowleave(self, event: tcod.event.WindowEvent) -> T | None: + def ev_windowleave(self, event: tcod.event.WindowEvent, /) -> T | None: """Called when the window loses mouse focus.""" - def ev_windowfocusgained(self, event: tcod.event.WindowEvent) -> T | None: + def ev_windowfocusgained(self, event: tcod.event.WindowEvent, /) -> T | None: """Called when the window gains keyboard focus.""" - def ev_windowfocuslost(self, event: tcod.event.WindowEvent) -> T | None: + def ev_windowfocuslost(self, event: tcod.event.WindowEvent, /) -> T | None: """Called when the window loses keyboard focus.""" - def ev_windowclose(self, event: tcod.event.WindowEvent) -> T | None: + def ev_windowclose(self, event: tcod.event.WindowEvent, /) -> T | None: """Called when the window manager requests the window to be closed.""" - def ev_windowtakefocus(self, event: tcod.event.WindowEvent) -> T | None: + def ev_windowtakefocus(self, event: tcod.event.WindowEvent, /) -> T | None: # noqa: D102 pass - def ev_windowhittest(self, event: tcod.event.WindowEvent) -> T | None: + def ev_windowhittest(self, event: tcod.event.WindowEvent, /) -> T | None: # noqa: D102 pass - def ev_joyaxismotion(self, event: tcod.event.JoystickAxis) -> T | None: + def ev_joyaxismotion(self, event: tcod.event.JoystickAxis, /) -> T | None: """Called when a joystick analog is moved. .. versionadded:: 13.8 """ - def ev_joyballmotion(self, event: tcod.event.JoystickBall) -> T | None: + def ev_joyballmotion(self, event: tcod.event.JoystickBall, /) -> T | None: """Called when a joystick ball is moved. .. versionadded:: 13.8 """ - def ev_joyhatmotion(self, event: tcod.event.JoystickHat) -> T | None: + def ev_joyhatmotion(self, event: tcod.event.JoystickHat, /) -> T | None: """Called when a joystick hat is moved. .. versionadded:: 13.8 """ - def ev_joybuttondown(self, event: tcod.event.JoystickButton) -> T | None: + def ev_joybuttondown(self, event: tcod.event.JoystickButton, /) -> T | None: """Called when a joystick button is pressed. .. versionadded:: 13.8 """ - def ev_joybuttonup(self, event: tcod.event.JoystickButton) -> T | None: + def ev_joybuttonup(self, event: tcod.event.JoystickButton, /) -> T | None: """Called when a joystick button is released. .. versionadded:: 13.8 """ - def ev_joydeviceadded(self, event: tcod.event.JoystickDevice) -> T | None: + def ev_joydeviceadded(self, event: tcod.event.JoystickDevice, /) -> T | None: """Called when a joystick is added. .. versionadded:: 13.8 """ - def ev_joydeviceremoved(self, event: tcod.event.JoystickDevice) -> T | None: + def ev_joydeviceremoved(self, event: tcod.event.JoystickDevice, /) -> T | None: """Called when a joystick is removed. .. versionadded:: 13.8 """ - def ev_controlleraxismotion(self, event: tcod.event.ControllerAxis) -> T | None: + def ev_controlleraxismotion(self, event: tcod.event.ControllerAxis, /) -> T | None: """Called when a controller analog is moved. .. versionadded:: 13.8 """ - def ev_controllerbuttondown(self, event: tcod.event.ControllerButton) -> T | None: + def ev_controllerbuttondown(self, event: tcod.event.ControllerButton, /) -> T | None: """Called when a controller button is pressed. .. versionadded:: 13.8 """ - def ev_controllerbuttonup(self, event: tcod.event.ControllerButton) -> T | None: + def ev_controllerbuttonup(self, event: tcod.event.ControllerButton, /) -> T | None: """Called when a controller button is released. .. versionadded:: 13.8 """ - def ev_controllerdeviceadded(self, event: tcod.event.ControllerDevice) -> T | None: + def ev_controllerdeviceadded(self, event: tcod.event.ControllerDevice, /) -> T | None: """Called when a standard controller is added. .. versionadded:: 13.8 """ - def ev_controllerdeviceremoved(self, event: tcod.event.ControllerDevice) -> T | None: + def ev_controllerdeviceremoved(self, event: tcod.event.ControllerDevice, /) -> T | None: """Called when a standard controller is removed. .. versionadded:: 13.8 """ - def ev_controllerdeviceremapped(self, event: tcod.event.ControllerDevice) -> T | None: + def ev_controllerdeviceremapped(self, event: tcod.event.ControllerDevice, /) -> T | None: """Called when a standard controller is remapped. .. versionadded:: 13.8 """ - def ev_(self, event: Any) -> T | None: + def ev_(self, event: Any, /) -> T | None: # noqa: ANN401, D102 pass @@ -1563,16 +1732,88 @@ def get_mouse_state() -> MouseState: .. versionadded:: 9.3 """ - xy = ffi.new("int[2]") + xy = ffi.new("float[2]") buttons = lib.SDL_GetMouseState(xy, xy + 1) - tile = _pixel_to_tile(*xy) + tile = _pixel_to_tile(tuple(xy)) if tile is None: - return MouseState((xy[0], xy[1]), None, buttons) - return MouseState((xy[0], xy[1]), (int(tile[0]), int(tile[1])), buttons) + return MouseState(position=Point(xy[0], xy[1]), tile=None, state=buttons) + return MouseState(position=Point(xy[0], xy[1]), tile=Point(floor(tile[0]), floor(tile[1])), state=buttons) + + +@overload +def convert_coordinates_from_window( + event: _EventType, + /, + context: tcod.context.Context | tcod.sdl.render.Renderer, + console: tcod.console.Console | tuple[int, int], + dest_rect: tuple[int, int, int, int] | None = None, +) -> _EventType: ... +@overload +def convert_coordinates_from_window( + xy: tuple[float, float], + /, + context: tcod.context.Context | tcod.sdl.render.Renderer, + console: tcod.console.Console | tuple[int, int], + dest_rect: tuple[int, int, int, int] | None = None, +) -> tuple[float, float]: ... +def convert_coordinates_from_window( + event: _EventType | tuple[float, float], + /, + context: tcod.context.Context | tcod.sdl.render.Renderer, + console: tcod.console.Console | tuple[int, int], + dest_rect: tuple[int, int, int, int] | None = None, +) -> _EventType | tuple[float, float]: + """Return an event or position with window mouse coordinates converted into console tile coordinates. + + Args: + event: :any:`Event` to convert, or the `(x, y)` coordinates to convert. + context: Context or Renderer to fetch the SDL renderer from for reference with conversions. + console: A console used as a size reference. + Otherwise the `(columns, rows)` can be given directly as a tuple. + dest_rect: The consoles rendering destination as `(x, y, width, height)`. + If None is given then the whole rendering target is assumed. + + .. versionadded:: 20.0 + """ + if isinstance(context, tcod.context.Context): + maybe_renderer: Final = context.sdl_renderer + if maybe_renderer is None: + return event + context = maybe_renderer + + if isinstance(console, tcod.console.Console): + console = console.width, console.height + + if dest_rect is None: + dest_rect = (0, 0, *(context.logical_size or context.output_size)) + + x_scale: Final = console[0] / dest_rect[2] + y_scale: Final = console[1] / dest_rect[3] + x_offset: Final = dest_rect[0] + y_offset: Final = dest_rect[1] + + if not isinstance(event, Event): + x, y = context.coordinates_from_window(event) + return (x - x_offset) * x_scale, (y - y_offset) * y_scale + + if isinstance(event, MouseMotion): + previous_position = convert_coordinates_from_window( + ((event.position[0] - event.motion[0]), (event.position[1] - event.motion[1])), context, console, dest_rect + ) + position = convert_coordinates_from_window(event.position, context, console, dest_rect) + event.motion = Point(position[0] - previous_position[0], position[1] - previous_position[1]) + event._tile_motion = Point( + floor(position[0]) - floor(previous_position[0]), floor(position[1]) - floor(previous_position[1]) + ) + elif isinstance(event, _MouseEventWithPosition): + event.position = Point(*convert_coordinates_from_window(event.position, context, console, dest_rect)) + if isinstance(event, _MouseEventWithTile): + event._tile = Point(floor(event.position[0]), floor(event.position[1])) + return event -@ffi.def_extern() # type: ignore -def _sdl_event_watcher(userdata: Any, sdl_event: Any) -> int: +@ffi.def_extern() # type: ignore[untyped-decorator] +def _sdl_event_watcher(userdata: Any, sdl_event: _C_SDL_Event) -> int: # noqa: ANN401 callback: Callable[[Event], None] = ffi.from_handle(userdata) callback(_parse_event(sdl_event)) return 0 @@ -1610,7 +1851,9 @@ def handle_events(event: tcod.event.Event) -> None: .. versionadded:: 13.4 """ if callback in _event_watch_handles: - warnings.warn(f"{callback} is already an active event watcher, nothing was added.", RuntimeWarning) + warnings.warn( + f"{callback} is already an active event watcher, nothing was added.", RuntimeWarning, stacklevel=2 + ) return callback handle = _event_watch_handles[callback] = ffi.new_handle(callback) lib.SDL_AddEventWatch(lib._sdl_event_watcher, handle) @@ -1627,10 +1870,10 @@ def remove_watch(callback: Callable[[Event], None]) -> None: .. versionadded:: 13.4 """ if callback not in _event_watch_handles: - warnings.warn(f"{callback} is not an active event watcher, nothing was removed.", RuntimeWarning) + warnings.warn(f"{callback} is not an active event watcher, nothing was removed.", RuntimeWarning, stacklevel=2) return handle = _event_watch_handles[callback] - lib.SDL_DelEventWatch(lib._sdl_event_watcher, handle) + lib.SDL_RemoveEventWatch(lib._sdl_event_watcher, handle) del _event_watch_handles[callback] @@ -2141,35 +2384,41 @@ class Scancode(enum.IntEnum): RALT = 230 RGUI = 231 MODE = 257 - AUDIONEXT = 258 - AUDIOPREV = 259 - AUDIOSTOP = 260 - AUDIOPLAY = 261 - AUDIOMUTE = 262 - MEDIASELECT = 263 - WWW = 264 - MAIL = 265 - CALCULATOR = 266 - COMPUTER = 267 - AC_SEARCH = 268 - AC_HOME = 269 - AC_BACK = 270 - AC_FORWARD = 271 - AC_STOP = 272 - AC_REFRESH = 273 - AC_BOOKMARKS = 274 - BRIGHTNESSDOWN = 275 - BRIGHTNESSUP = 276 - DISPLAYSWITCH = 277 - KBDILLUMTOGGLE = 278 - KBDILLUMDOWN = 279 - KBDILLUMUP = 280 - EJECT = 281 - SLEEP = 282 - APP1 = 283 - APP2 = 284 - AUDIOREWIND = 285 - AUDIOFASTFORWARD = 286 + SLEEP = 258 + WAKE = 259 + CHANNEL_INCREMENT = 260 + CHANNEL_DECREMENT = 261 + MEDIA_PLAY = 262 + MEDIA_PAUSE = 263 + MEDIA_RECORD = 264 + MEDIA_FAST_FORWARD = 265 + MEDIA_REWIND = 266 + MEDIA_NEXT_TRACK = 267 + MEDIA_PREVIOUS_TRACK = 268 + MEDIA_STOP = 269 + MEDIA_EJECT = 270 + MEDIA_PLAY_PAUSE = 271 + MEDIA_SELECT = 272 + AC_NEW = 273 + AC_OPEN = 274 + AC_CLOSE = 275 + AC_EXIT = 276 + AC_SAVE = 277 + AC_PRINT = 278 + AC_PROPERTIES = 279 + AC_SEARCH = 280 + AC_HOME = 281 + AC_BACK = 282 + AC_FORWARD = 283 + AC_STOP = 284 + AC_REFRESH = 285 + AC_BOOKMARKS = 286 + SOFTLEFT = 287 + SOFTRIGHT = 288 + CALL = 289 + ENDCALL = 290 + RESERVED = 400 + COUNT = 512 # --- end --- @property @@ -2191,7 +2440,7 @@ def keysym(self) -> KeySym: Based on the current keyboard layout. """ _init_sdl_video() - return KeySym(lib.SDL_GetKeyFromScancode(self.value)) + return KeySym(lib.SDL_GetKeyFromScancode(self.value, 0, False)) # noqa: FBT003 @property def scancode(self) -> Scancode: @@ -2212,15 +2461,19 @@ def _missing_(cls, value: object) -> Scancode | None: result._value_ = value return result - def __eq__(self, other: Any) -> bool: + def __eq__(self, other: object) -> bool: + """Compare with another Scancode value. + + Comparison between :any:`KeySym` and :any:`Scancode` is not allowed and will raise :any:`TypeError`. + """ if isinstance(other, KeySym): msg = "Scancode and KeySym enums can not be compared directly. Convert one or the other to the same type." raise TypeError(msg) return super().__eq__(other) def __hash__(self) -> int: - # __eq__ was defined, so __hash__ must be defined. - return super().__hash__() + """Return the hash for this value.""" + return super().__hash__() # __eq__ was defined, so __hash__ must be defined def __repr__(self) -> str: """Return the fully qualified name of this enum.""" @@ -2230,11 +2483,19 @@ def __repr__(self) -> str: class KeySym(enum.IntEnum): """Keyboard constants based on their symbol. - These names are derived from SDL except for the numbers which are prefixed - with ``N`` (since raw numbers can not be a Python name.) + These names are derived from SDL except for numbers which are prefixed with ``N`` (since raw numbers can not be a Python name). + Alternatively ``KeySym["9"]`` can be used to represent numbers (since Python 3.13). .. versionadded:: 12.3 + .. versionchanged:: 19.0 + SDL backend was updated to 3.x, which means some enums have been renamed. + Single letters are now uppercase. + + .. versionchanged:: 19.6 + Number symbols can now be fetched with ``KeySym["9"]``, etc. + With Python 3.13 or later. + ================== ========== UNKNOWN 0 BACKSPACE 8 @@ -2280,32 +2541,32 @@ class KeySym(enum.IntEnum): CARET 94 UNDERSCORE 95 BACKQUOTE 96 - a 97 - b 98 - c 99 - d 100 - e 101 - f 102 - g 103 - h 104 - i 105 - j 106 - k 107 - l 108 - m 109 - n 110 - o 111 - p 112 - q 113 - r 114 - s 115 - t 116 - u 117 - v 118 - w 119 - x 120 - y 121 - z 122 + A 97 + B 98 + C 99 + D 100 + E 101 + F 102 + G 103 + H 104 + I 105 + J 106 + K 107 + L 108 + M 109 + N 110 + O 111 + P 112 + Q 113 + R 114 + S 115 + T 116 + U 117 + V 118 + W 119 + X 120 + Y 121 + Z 122 DELETE 127 SCANCODE_MASK 1073741824 CAPSLOCK 1073741881 @@ -2484,12 +2745,12 @@ class KeySym(enum.IntEnum): ESCAPE = 27 SPACE = 32 EXCLAIM = 33 - QUOTEDBL = 34 + DBLAPOSTROPHE = 34 HASH = 35 DOLLAR = 36 PERCENT = 37 AMPERSAND = 38 - QUOTE = 39 + APOSTROPHE = 39 LEFTPAREN = 40 RIGHTPAREN = 41 ASTERISK = 42 @@ -2520,34 +2781,47 @@ class KeySym(enum.IntEnum): RIGHTBRACKET = 93 CARET = 94 UNDERSCORE = 95 - BACKQUOTE = 96 - a = 97 - b = 98 - c = 99 - d = 100 - e = 101 - f = 102 - g = 103 - h = 104 - i = 105 - j = 106 - k = 107 - l = 108 # noqa: E741 - m = 109 - n = 110 - o = 111 - p = 112 - q = 113 - r = 114 - s = 115 - t = 116 - u = 117 - v = 118 - w = 119 - x = 120 - y = 121 - z = 122 + GRAVE = 96 + A = 97 + B = 98 + C = 99 + D = 100 + E = 101 + F = 102 + G = 103 + H = 104 + I = 105 # noqa: E741 + J = 106 + K = 107 + L = 108 + M = 109 + N = 110 + O = 111 # noqa: E741 + P = 112 + Q = 113 + R = 114 + S = 115 + T = 116 + U = 117 + V = 118 + W = 119 + X = 120 + Y = 121 + Z = 122 + LEFTBRACE = 123 + PIPE = 124 + RIGHTBRACE = 125 + TILDE = 126 DELETE = 127 + PLUSMINUS = 177 + EXTENDED_MASK = 536870912 + LEFT_TAB = 536870913 + LEVEL5_SHIFT = 536870914 + MULTI_KEY_COMPOSE = 536870915 + LMETA = 536870916 + RMETA = 536870917 + LHYPER = 536870918 + RHYPER = 536870919 SCANCODE_MASK = 1073741824 CAPSLOCK = 1073741881 F1 = 1073741882 @@ -2689,35 +2963,39 @@ class KeySym(enum.IntEnum): RALT = 1073742054 RGUI = 1073742055 MODE = 1073742081 - AUDIONEXT = 1073742082 - AUDIOPREV = 1073742083 - AUDIOSTOP = 1073742084 - AUDIOPLAY = 1073742085 - AUDIOMUTE = 1073742086 - MEDIASELECT = 1073742087 - WWW = 1073742088 - MAIL = 1073742089 - CALCULATOR = 1073742090 - COMPUTER = 1073742091 - AC_SEARCH = 1073742092 - AC_HOME = 1073742093 - AC_BACK = 1073742094 - AC_FORWARD = 1073742095 - AC_STOP = 1073742096 - AC_REFRESH = 1073742097 - AC_BOOKMARKS = 1073742098 - BRIGHTNESSDOWN = 1073742099 - BRIGHTNESSUP = 1073742100 - DISPLAYSWITCH = 1073742101 - KBDILLUMTOGGLE = 1073742102 - KBDILLUMDOWN = 1073742103 - KBDILLUMUP = 1073742104 - EJECT = 1073742105 - SLEEP = 1073742106 - APP1 = 1073742107 - APP2 = 1073742108 - AUDIOREWIND = 1073742109 - AUDIOFASTFORWARD = 1073742110 + SLEEP = 1073742082 + WAKE = 1073742083 + CHANNEL_INCREMENT = 1073742084 + CHANNEL_DECREMENT = 1073742085 + MEDIA_PLAY = 1073742086 + MEDIA_PAUSE = 1073742087 + MEDIA_RECORD = 1073742088 + MEDIA_FAST_FORWARD = 1073742089 + MEDIA_REWIND = 1073742090 + MEDIA_NEXT_TRACK = 1073742091 + MEDIA_PREVIOUS_TRACK = 1073742092 + MEDIA_STOP = 1073742093 + MEDIA_EJECT = 1073742094 + MEDIA_PLAY_PAUSE = 1073742095 + MEDIA_SELECT = 1073742096 + AC_NEW = 1073742097 + AC_OPEN = 1073742098 + AC_CLOSE = 1073742099 + AC_EXIT = 1073742100 + AC_SAVE = 1073742101 + AC_PRINT = 1073742102 + AC_PROPERTIES = 1073742103 + AC_SEARCH = 1073742104 + AC_HOME = 1073742105 + AC_BACK = 1073742106 + AC_FORWARD = 1073742107 + AC_STOP = 1073742108 + AC_REFRESH = 1073742109 + AC_BOOKMARKS = 1073742110 + SOFTLEFT = 1073742111 + SOFTRIGHT = 1073742112 + CALL = 1073742113 + ENDCALL = 1073742114 # --- end --- @property @@ -2731,6 +3009,7 @@ def label(self) -> str: Example:: + >>> import tcod.event >>> tcod.event.KeySym.F1.label 'F1' >>> tcod.event.KeySym.BACKSPACE.label @@ -2756,7 +3035,7 @@ def scancode(self) -> Scancode: Based on the current keyboard layout. """ _init_sdl_video() - return Scancode(lib.SDL_GetScancodeFromKey(self.value)) + return Scancode(lib.SDL_GetScancodeFromKey(self.value, ffi.NULL)) @classmethod def _missing_(cls, value: object) -> KeySym | None: @@ -2766,21 +3045,67 @@ def _missing_(cls, value: object) -> KeySym | None: result._value_ = value return result - def __eq__(self, other: Any) -> bool: + def __eq__(self, other: object) -> bool: + """Compare with another KeySym value. + + Comparison between :any:`KeySym` and :any:`Scancode` is not allowed and will raise :any:`TypeError`. + """ if isinstance(other, Scancode): msg = "Scancode and KeySym enums can not be compared directly. Convert one or the other to the same type." raise TypeError(msg) return super().__eq__(other) def __hash__(self) -> int: - # __eq__ was defined, so __hash__ must be defined. - return super().__hash__() + """Return the hash for this value.""" + return super().__hash__() # __eq__ was defined, so __hash__ must be defined def __repr__(self) -> str: """Return the fully qualified name of this enum.""" return f"tcod.event.{self.__class__.__name__}.{self.name}" +if sys.version_info >= (3, 13): + # Alias for lower case letters removed from SDL3 + KeySym.A._add_alias_("a") + KeySym.B._add_alias_("b") + KeySym.C._add_alias_("c") + KeySym.D._add_alias_("d") + KeySym.E._add_alias_("e") + KeySym.F._add_alias_("f") + KeySym.G._add_alias_("g") + KeySym.H._add_alias_("h") + KeySym.I._add_alias_("i") + KeySym.J._add_alias_("j") + KeySym.K._add_alias_("k") + KeySym.L._add_alias_("l") + KeySym.M._add_alias_("m") + KeySym.N._add_alias_("n") + KeySym.O._add_alias_("o") + KeySym.P._add_alias_("p") + KeySym.Q._add_alias_("q") + KeySym.R._add_alias_("r") + KeySym.S._add_alias_("s") + KeySym.T._add_alias_("t") + KeySym.U._add_alias_("u") + KeySym.V._add_alias_("v") + KeySym.W._add_alias_("w") + KeySym.X._add_alias_("x") + KeySym.Y._add_alias_("y") + KeySym.Z._add_alias_("z") + + # Alias for numbers, since Python enum names can not be number literals + KeySym.N0._add_alias_("0") + KeySym.N1._add_alias_("1") + KeySym.N2._add_alias_("2") + KeySym.N3._add_alias_("3") + KeySym.N4._add_alias_("4") + KeySym.N5._add_alias_("5") + KeySym.N6._add_alias_("6") + KeySym.N7._add_alias_("7") + KeySym.N8._add_alias_("8") + KeySym.N9._add_alias_("9") + + def __getattr__(name: str) -> int: """Migrate deprecated access of event constants.""" if name.startswith("BUTTON_"): @@ -2802,7 +3127,10 @@ def __getattr__(name: str) -> int: FutureWarning, stacklevel=2, ) - return replacement + return replacement # type: ignore[return-value] + + if name.startswith("K_") and len(name) == 3: # noqa: PLR2004 + name = name.upper() # Silently fix single letter key symbols removed from SDL3, these are still deprecated value: int | None = getattr(tcod.event_constants, name, None) if not value: @@ -2828,27 +3156,44 @@ def __getattr__(name: str) -> int: FutureWarning, stacklevel=2, ) + elif name.startswith("KMOD_"): + modifier = name[5:] + warnings.warn( + "Key modifiers have been replaced with the Modifier IntFlag.\n" + f"`tcod.event.{modifier}` should be replaced with `tcod.event.Modifier.{modifier}`", + FutureWarning, + stacklevel=2, + ) return value -__all__ = [ # noqa: F405 - "Modifier", +def time_ns() -> int: + """Return the nanoseconds elapsed since SDL was initialized. + + .. versionadded:: 21.0 + """ + return int(lib.SDL_GetTicksNS()) + + +def time() -> float: + """Return the seconds elapsed since SDL was initialized. + + .. versionadded:: 21.0 + """ + return time_ns() / 1_000_000_000 + + +__all__ = ( # noqa: F405 RUF022 "Point", - "BUTTON_LEFT", - "BUTTON_MIDDLE", - "BUTTON_RIGHT", - "BUTTON_X1", - "BUTTON_X2", - "BUTTON_LMASK", - "BUTTON_MMASK", - "BUTTON_RMASK", - "BUTTON_X1MASK", - "BUTTON_X2MASK", + "Modifier", + "MouseButton", + "MouseButtonMask", "Event", "Quit", "KeyboardEvent", "KeyDown", "KeyUp", + "MouseState", "MouseMotion", "MouseButtonEvent", "MouseButtonDown", @@ -2879,25 +3224,9 @@ def __getattr__(name: str) -> int: "get_modifier_state", "Scancode", "KeySym", + "time_ns", + "time", # --- From event_constants.py --- - "KMOD_NONE", - "KMOD_LSHIFT", - "KMOD_RSHIFT", - "KMOD_SHIFT", - "KMOD_LCTRL", - "KMOD_RCTRL", - "KMOD_CTRL", - "KMOD_LALT", - "KMOD_RALT", - "KMOD_ALT", - "KMOD_LGUI", - "KMOD_RGUI", - "KMOD_GUI", - "KMOD_NUM", - "KMOD_CAPS", - "KMOD_MODE", - "KMOD_RESERVED", "MOUSEWHEEL_NORMAL", "MOUSEWHEEL_FLIPPED", - "MOUSEWHEEL", -] +) diff --git a/tcod/event_constants.py b/tcod/event_constants.py index d10eeb74..efeecc56 100644 --- a/tcod/event_constants.py +++ b/tcod/event_constants.py @@ -218,35 +218,41 @@ SCANCODE_RALT = 230 SCANCODE_RGUI = 231 SCANCODE_MODE = 257 -SCANCODE_AUDIONEXT = 258 -SCANCODE_AUDIOPREV = 259 -SCANCODE_AUDIOSTOP = 260 -SCANCODE_AUDIOPLAY = 261 -SCANCODE_AUDIOMUTE = 262 -SCANCODE_MEDIASELECT = 263 -SCANCODE_WWW = 264 -SCANCODE_MAIL = 265 -SCANCODE_CALCULATOR = 266 -SCANCODE_COMPUTER = 267 -SCANCODE_AC_SEARCH = 268 -SCANCODE_AC_HOME = 269 -SCANCODE_AC_BACK = 270 -SCANCODE_AC_FORWARD = 271 -SCANCODE_AC_STOP = 272 -SCANCODE_AC_REFRESH = 273 -SCANCODE_AC_BOOKMARKS = 274 -SCANCODE_BRIGHTNESSDOWN = 275 -SCANCODE_BRIGHTNESSUP = 276 -SCANCODE_DISPLAYSWITCH = 277 -SCANCODE_KBDILLUMTOGGLE = 278 -SCANCODE_KBDILLUMDOWN = 279 -SCANCODE_KBDILLUMUP = 280 -SCANCODE_EJECT = 281 -SCANCODE_SLEEP = 282 -SCANCODE_APP1 = 283 -SCANCODE_APP2 = 284 -SCANCODE_AUDIOREWIND = 285 -SCANCODE_AUDIOFASTFORWARD = 286 +SCANCODE_SLEEP = 258 +SCANCODE_WAKE = 259 +SCANCODE_CHANNEL_INCREMENT = 260 +SCANCODE_CHANNEL_DECREMENT = 261 +SCANCODE_MEDIA_PLAY = 262 +SCANCODE_MEDIA_PAUSE = 263 +SCANCODE_MEDIA_RECORD = 264 +SCANCODE_MEDIA_FAST_FORWARD = 265 +SCANCODE_MEDIA_REWIND = 266 +SCANCODE_MEDIA_NEXT_TRACK = 267 +SCANCODE_MEDIA_PREVIOUS_TRACK = 268 +SCANCODE_MEDIA_STOP = 269 +SCANCODE_MEDIA_EJECT = 270 +SCANCODE_MEDIA_PLAY_PAUSE = 271 +SCANCODE_MEDIA_SELECT = 272 +SCANCODE_AC_NEW = 273 +SCANCODE_AC_OPEN = 274 +SCANCODE_AC_CLOSE = 275 +SCANCODE_AC_EXIT = 276 +SCANCODE_AC_SAVE = 277 +SCANCODE_AC_PRINT = 278 +SCANCODE_AC_PROPERTIES = 279 +SCANCODE_AC_SEARCH = 280 +SCANCODE_AC_HOME = 281 +SCANCODE_AC_BACK = 282 +SCANCODE_AC_FORWARD = 283 +SCANCODE_AC_STOP = 284 +SCANCODE_AC_REFRESH = 285 +SCANCODE_AC_BOOKMARKS = 286 +SCANCODE_SOFTLEFT = 287 +SCANCODE_SOFTRIGHT = 288 +SCANCODE_CALL = 289 +SCANCODE_ENDCALL = 290 +SCANCODE_RESERVED = 400 +SCANCODE_COUNT = 512 # --- SDL keyboard symbols --- K_UNKNOWN = 0 @@ -256,12 +262,12 @@ K_ESCAPE = 27 K_SPACE = 32 K_EXCLAIM = 33 -K_QUOTEDBL = 34 +K_DBLAPOSTROPHE = 34 K_HASH = 35 K_DOLLAR = 36 K_PERCENT = 37 K_AMPERSAND = 38 -K_QUOTE = 39 +K_APOSTROPHE = 39 K_LEFTPAREN = 40 K_RIGHTPAREN = 41 K_ASTERISK = 42 @@ -292,34 +298,47 @@ K_RIGHTBRACKET = 93 K_CARET = 94 K_UNDERSCORE = 95 -K_BACKQUOTE = 96 -K_a = 97 -K_b = 98 -K_c = 99 -K_d = 100 -K_e = 101 -K_f = 102 -K_g = 103 -K_h = 104 -K_i = 105 -K_j = 106 -K_k = 107 -K_l = 108 -K_m = 109 -K_n = 110 -K_o = 111 -K_p = 112 -K_q = 113 -K_r = 114 -K_s = 115 -K_t = 116 -K_u = 117 -K_v = 118 -K_w = 119 -K_x = 120 -K_y = 121 -K_z = 122 +K_GRAVE = 96 +K_A = 97 +K_B = 98 +K_C = 99 +K_D = 100 +K_E = 101 +K_F = 102 +K_G = 103 +K_H = 104 +K_I = 105 +K_J = 106 +K_K = 107 +K_L = 108 +K_M = 109 +K_N = 110 +K_O = 111 +K_P = 112 +K_Q = 113 +K_R = 114 +K_S = 115 +K_T = 116 +K_U = 117 +K_V = 118 +K_W = 119 +K_X = 120 +K_Y = 121 +K_Z = 122 +K_LEFTBRACE = 123 +K_PIPE = 124 +K_RIGHTBRACE = 125 +K_TILDE = 126 K_DELETE = 127 +K_PLUSMINUS = 177 +K_EXTENDED_MASK = 536870912 +K_LEFT_TAB = 536870913 +K_LEVEL5_SHIFT = 536870914 +K_MULTI_KEY_COMPOSE = 536870915 +K_LMETA = 536870916 +K_RMETA = 536870917 +K_LHYPER = 536870918 +K_RHYPER = 536870919 K_SCANCODE_MASK = 1073741824 K_CAPSLOCK = 1073741881 K_F1 = 1073741882 @@ -461,41 +480,46 @@ K_RALT = 1073742054 K_RGUI = 1073742055 K_MODE = 1073742081 -K_AUDIONEXT = 1073742082 -K_AUDIOPREV = 1073742083 -K_AUDIOSTOP = 1073742084 -K_AUDIOPLAY = 1073742085 -K_AUDIOMUTE = 1073742086 -K_MEDIASELECT = 1073742087 -K_WWW = 1073742088 -K_MAIL = 1073742089 -K_CALCULATOR = 1073742090 -K_COMPUTER = 1073742091 -K_AC_SEARCH = 1073742092 -K_AC_HOME = 1073742093 -K_AC_BACK = 1073742094 -K_AC_FORWARD = 1073742095 -K_AC_STOP = 1073742096 -K_AC_REFRESH = 1073742097 -K_AC_BOOKMARKS = 1073742098 -K_BRIGHTNESSDOWN = 1073742099 -K_BRIGHTNESSUP = 1073742100 -K_DISPLAYSWITCH = 1073742101 -K_KBDILLUMTOGGLE = 1073742102 -K_KBDILLUMDOWN = 1073742103 -K_KBDILLUMUP = 1073742104 -K_EJECT = 1073742105 -K_SLEEP = 1073742106 -K_APP1 = 1073742107 -K_APP2 = 1073742108 -K_AUDIOREWIND = 1073742109 -K_AUDIOFASTFORWARD = 1073742110 +K_SLEEP = 1073742082 +K_WAKE = 1073742083 +K_CHANNEL_INCREMENT = 1073742084 +K_CHANNEL_DECREMENT = 1073742085 +K_MEDIA_PLAY = 1073742086 +K_MEDIA_PAUSE = 1073742087 +K_MEDIA_RECORD = 1073742088 +K_MEDIA_FAST_FORWARD = 1073742089 +K_MEDIA_REWIND = 1073742090 +K_MEDIA_NEXT_TRACK = 1073742091 +K_MEDIA_PREVIOUS_TRACK = 1073742092 +K_MEDIA_STOP = 1073742093 +K_MEDIA_EJECT = 1073742094 +K_MEDIA_PLAY_PAUSE = 1073742095 +K_MEDIA_SELECT = 1073742096 +K_AC_NEW = 1073742097 +K_AC_OPEN = 1073742098 +K_AC_CLOSE = 1073742099 +K_AC_EXIT = 1073742100 +K_AC_SAVE = 1073742101 +K_AC_PRINT = 1073742102 +K_AC_PROPERTIES = 1073742103 +K_AC_SEARCH = 1073742104 +K_AC_HOME = 1073742105 +K_AC_BACK = 1073742106 +K_AC_FORWARD = 1073742107 +K_AC_STOP = 1073742108 +K_AC_REFRESH = 1073742109 +K_AC_BOOKMARKS = 1073742110 +K_SOFTLEFT = 1073742111 +K_SOFTRIGHT = 1073742112 +K_CALL = 1073742113 +K_ENDCALL = 1073742114 # --- SDL keyboard modifiers --- KMOD_NONE = 0 KMOD_LSHIFT = 1 KMOD_RSHIFT = 2 KMOD_SHIFT = 3 +KMOD_LEVEL5 = 4 KMOD_LCTRL = 64 KMOD_RCTRL = 128 KMOD_CTRL = 192 @@ -514,6 +538,7 @@ 1: "KMOD_LSHIFT", 2: "KMOD_RSHIFT", 3: "KMOD_SHIFT", + 4: "KMOD_LEVEL5", 64: "KMOD_LCTRL", 128: "KMOD_RCTRL", 192: "KMOD_CTRL", @@ -532,32 +557,12 @@ # --- SDL wheel --- MOUSEWHEEL_NORMAL = 0 MOUSEWHEEL_FLIPPED = 1 -MOUSEWHEEL = 1027 _REVERSE_WHEEL_TABLE = { 0: "MOUSEWHEEL_NORMAL", 1: "MOUSEWHEEL_FLIPPED", - 1027: "MOUSEWHEEL", } -__all__ = [ - "KMOD_NONE", - "KMOD_LSHIFT", - "KMOD_RSHIFT", - "KMOD_SHIFT", - "KMOD_LCTRL", - "KMOD_RCTRL", - "KMOD_CTRL", - "KMOD_LALT", - "KMOD_RALT", - "KMOD_ALT", - "KMOD_LGUI", - "KMOD_RGUI", - "KMOD_GUI", - "KMOD_NUM", - "KMOD_CAPS", - "KMOD_MODE", - "KMOD_SCROLL", +__all__ = [ # noqa: RUF022 "MOUSEWHEEL_NORMAL", "MOUSEWHEEL_FLIPPED", - "MOUSEWHEEL", ] diff --git a/tcod/image.py b/tcod/image.py index b404361a..0f154450 100644 --- a/tcod/image.py +++ b/tcod/image.py @@ -8,19 +8,25 @@ The best it can do with consoles is convert an image into semigraphics which can be shown on non-emulated terminals. For true pixel-based rendering you'll want to access the SDL rendering port at :any:`tcod.sdl.render`. """ + from __future__ import annotations -from os import PathLike from pathlib import Path -from typing import Any +from typing import TYPE_CHECKING, Any import numpy as np -from numpy.typing import ArrayLike, NDArray +from typing_extensions import deprecated -import tcod.console -from tcod._internal import _console, deprecate +from tcod._internal import _console, _path_encode from tcod.cffi import ffi, lib +if TYPE_CHECKING: + from os import PathLike + + from numpy.typing import ArrayLike, NDArray + + import tcod.console + class Image: """A libtcod image. @@ -38,10 +44,16 @@ def __init__(self, width: int, height: int) -> None: """Initialize a blank image.""" self.width, self.height = width, height self.image_c = ffi.gc(lib.TCOD_image_new(width, height), lib.TCOD_image_delete) + if self.image_c == ffi.NULL: + msg = "Failed to allocate image." + raise MemoryError(msg) @classmethod - def _from_cdata(cls, cdata: Any) -> Image: + def _from_cdata(cls, cdata: Any) -> Image: # noqa: ANN401 self: Image = object.__new__(cls) + if cdata == ffi.NULL: + msg = "Pointer must not be NULL." + raise RuntimeError(msg) self.image_c = cdata self.width, self.height = self._get_size() return self @@ -59,7 +71,7 @@ def from_array(cls, array: ArrayLike) -> Image: .. versionadded:: 11.4 """ array = np.asarray(array, dtype=np.uint8) - height, width, depth = array.shape + height, width, _depth = array.shape image = cls(width, height) image_array: NDArray[np.uint8] = np.asarray(image) image_array[...] = array @@ -72,7 +84,7 @@ def from_file(cls, path: str | PathLike[str]) -> Image: .. versionadded:: 16.0 """ path = Path(path).resolve(strict=True) - return cls._from_cdata(ffi.gc(lib.TCOD_image_load(bytes(path)), lib.TCOD_image_delete)) + return cls._from_cdata(ffi.gc(lib.TCOD_image_load(_path_encode(path)), lib.TCOD_image_delete)) def clear(self, color: tuple[int, int, int]) -> None: """Fill this entire Image with color. @@ -133,7 +145,7 @@ def get_alpha(self, x: int, y: int) -> int: int: The alpha value of the pixel. With 0 being fully transparent and 255 being fully opaque. """ - return lib.TCOD_image_get_alpha(self.image_c, x, y) # type: ignore + return int(lib.TCOD_image_get_alpha(self.image_c, x, y)) def refresh_console(self, console: tcod.console.Console) -> None: """Update an Image created with :any:`libtcodpy.image_from_console`. @@ -207,7 +219,7 @@ def put_pixel(self, x: int, y: int, color: tuple[int, int, int]) -> None: """ lib.TCOD_image_put_pixel(self.image_c, x, y, color) - def blit( + def blit( # noqa: PLR0913 self, console: tcod.console.Console, x: float, @@ -242,7 +254,7 @@ def blit( angle, ) - def blit_rect( + def blit_rect( # noqa: PLR0913 self, console: tcod.console.Console, x: int, @@ -263,7 +275,7 @@ def blit_rect( """ lib.TCOD_image_blit_rect(self.image_c, _console(console), x, y, width, height, bg_blend) - def blit_2x( + def blit_2x( # noqa: PLR0913 self, console: tcod.console.Console, dest_x: int, @@ -306,7 +318,7 @@ def save_as(self, filename: str | PathLike[str]) -> None: .. versionchanged:: 16.0 Added PathLike support. """ - lib.TCOD_image_save(self.image_c, bytes(Path(filename))) + lib.TCOD_image_save(self.image_c, _path_encode(Path(filename))) @property def __array_interface__(self) -> dict[str, Any]: @@ -339,21 +351,9 @@ def __array_interface__(self) -> dict[str, Any]: } -def _get_format_name(format: int) -> str: - """Return the SDL_PIXELFORMAT_X name for this format, if possible.""" - for attr in dir(lib): - if not attr.startswith("SDL_PIXELFORMAT"): - continue - if getattr(lib, attr) != format: - continue - return attr - return str(format) - - -@deprecate( +@deprecated( "This function may be removed in the future." " It's recommended to load images with a more complete image library such as python-Pillow or python-imageio.", - category=PendingDeprecationWarning, ) def load(filename: str | PathLike[str]) -> NDArray[np.uint8]: """Load a PNG file as an RGBA array. @@ -364,10 +364,11 @@ def load(filename: str | PathLike[str]) -> NDArray[np.uint8]: .. versionadded:: 11.4 """ - image = Image._from_cdata(ffi.gc(lib.TCOD_image_load(bytes(Path(filename))), lib.TCOD_image_delete)) + filename = Path(filename).resolve(strict=True) + image = Image._from_cdata(ffi.gc(lib.TCOD_image_load(_path_encode(filename)), lib.TCOD_image_delete)) array: NDArray[np.uint8] = np.asarray(image, dtype=np.uint8) height, width, depth = array.shape - if depth == 3: + if depth == 3: # noqa: PLR2004 array = np.concatenate( ( array, @@ -376,3 +377,42 @@ def load(filename: str | PathLike[str]) -> NDArray[np.uint8]: axis=2, ) return array + + +class _TempImage: + """An Image-like container for NumPy arrays.""" + + def __init__(self, array: ArrayLike) -> None: + """Initialize an image from the given array. May copy or reference the array.""" + self._array: NDArray[np.uint8] = np.ascontiguousarray(array, dtype=np.uint8) + height, width, depth = self._array.shape + if depth != 3: # noqa: PLR2004 + msg = f"Array must have RGB channels. Shape is: {self._array.shape!r}" + raise TypeError(msg) + self._buffer = ffi.from_buffer("TCOD_color_t[]", self._array) + self._mipmaps = ffi.new( + "struct TCOD_mipmap_*", + { + "width": width, + "height": height, + "fwidth": width, + "fheight": height, + "buf": self._buffer, + "dirty": True, + }, + ) + self.image_c = ffi.new( + "TCOD_Image*", + { + "nb_mipmaps": 1, + "mipmaps": self._mipmaps, + "has_key_color": False, + }, + ) + + +def _as_image(image: ArrayLike | Image | _TempImage) -> _TempImage | Image: + """Convert this input into an Image-like object.""" + if isinstance(image, (Image, _TempImage)): + return image + return _TempImage(image) diff --git a/tcod/libtcodpy.py b/tcod/libtcodpy.py index 1e026ce3..bae62a1f 100644 --- a/tcod/libtcodpy.py +++ b/tcod/libtcodpy.py @@ -1,17 +1,16 @@ """This module handles backward compatibility with the ctypes libtcodpy module.""" + from __future__ import annotations import atexit import sys import threading import warnings -from os import PathLike from pathlib import Path -from typing import Any, Callable, Hashable, Iterable, Iterator, Sequence +from typing import TYPE_CHECKING, Any, Final, Literal import numpy as np -from numpy.typing import NDArray -from typing_extensions import Literal +from typing_extensions import deprecated import tcod.bsp import tcod.console @@ -31,11 +30,11 @@ _console, _fmt, _int, + _path_encode, _PropagateException, _unicode, _unpack_char_p, deprecate, - pending_deprecate, ) from tcod.cffi import ffi, lib from tcod.color import Color @@ -52,8 +51,14 @@ NOISE_DEFAULT, ) +if TYPE_CHECKING: + from collections.abc import Callable, Hashable, Iterable, Iterator, Sequence + from os import PathLike + + from numpy.typing import NDArray + # Functions are too deprecated to make changes. -# ruff: noqa: ANN401 PLR0913 +# ruff: noqa: ANN401 PLR0913 D102 D103 D105 D107 Bsp = tcod.bsp.BSP @@ -63,18 +68,24 @@ NOISE_DEFAULT_LACUNARITY = 2.0 -def FOV_PERMISSIVE(p: int) -> int: +def FOV_PERMISSIVE(p: int) -> int: # noqa: N802 return FOV_PERMISSIVE_0 + p -def BKGND_ALPHA(a: int) -> int: +def BKGND_ALPHA(a: int) -> int: # noqa: N802 return BKGND_ALPH | (int(a * 255) << 8) -def BKGND_ADDALPHA(a: int) -> int: +def BKGND_ADDALPHA(a: int) -> int: # noqa: N802 return BKGND_ADDA | (int(a * 255) << 8) +_PENDING_DEPRECATE_MSG: Final = ( + "This function may be deprecated in the future. Consider raising an issue on GitHub if you need this feature." +) + + +@deprecated("Console array attributes perform better than this class.") class ConsoleBuffer: """Simple console that allows direct (fast) access to cells. Simplifies use of the "fill" functions. @@ -109,11 +120,6 @@ def __init__( Values to fill the buffer are optional, defaults to black with no characters. """ - warnings.warn( - "Console array attributes perform better than this class.", - DeprecationWarning, - stacklevel=2, - ) self.width = width self.height = height self.clear(back_r, back_g, back_b, fore_r, fore_g, fore_b, char) @@ -237,8 +243,8 @@ def set( def blit( self, dest: tcod.console.Console, - fill_fore: bool = True, - fill_back: bool = True, + fill_fore: bool = True, # noqa: FBT001, FBT002 + fill_back: bool = True, # noqa: FBT001, FBT002 ) -> None: """Use libtcod's "fill" functions to write the buffer to a console. @@ -269,6 +275,7 @@ def blit( dest.ch.ravel()[:] = self.char +@deprecated("Using this class is not recommended.") class Dice(_CDataWrapper): """A libtcod dice object. @@ -284,11 +291,6 @@ class Dice(_CDataWrapper): """ def __init__(self, *args: Any, **kwargs: Any) -> None: - warnings.warn( - "Using this class is not recommended.", - DeprecationWarning, - stacklevel=2, - ) super().__init__(*args, **kwargs) if self.cdata == ffi.NULL: self._init(*args, **kwargs) @@ -315,26 +317,15 @@ def nb_dices(self, value: int) -> None: self.nb_rolls = value def __str__(self) -> str: - add = "+(%s)" % self.addsub if self.addsub != 0 else "" - return "%id%ix%s%s" % ( - self.nb_dices, - self.nb_faces, - self.multiplier, - add, - ) + add = f"+({self.addsub})" if self.addsub != 0 else "" + return f"{self.nb_dices}d{self.nb_faces}x{self.multiplier}{add}" def __repr__(self) -> str: - return "{}(nb_dices={!r},nb_faces={!r},multiplier={!r},addsub={!r})".format( - self.__class__.__name__, - self.nb_dices, - self.nb_faces, - self.multiplier, - self.addsub, - ) + return f"{self.__class__.__name__}(nb_dices={self.nb_dices!r},nb_faces={self.nb_faces!r},multiplier={self.multiplier!r},addsub={self.addsub!r})" # reverse lookup table for KEY_X attributes, used by Key.__repr__ -_LOOKUP_VK = {value: "KEY_%s" % key[6:] for key, value in lib.__dict__.items() if key.startswith("TCODK")} +_LOOKUP_VK = {value: f"KEY_{key[6:]}" for key, value in lib.__dict__.items() if key.startswith("TCODK")} class Key(_CDataWrapper): @@ -359,6 +350,8 @@ class Key(_CDataWrapper): Use events from the :any:`tcod.event` module instead. """ + cdata: Any + _BOOL_ATTRIBUTES = ( "lalt", "lctrl", @@ -375,14 +368,14 @@ def __init__( vk: int = 0, c: int = 0, text: str = "", - pressed: bool = False, - lalt: bool = False, - lctrl: bool = False, - lmeta: bool = False, - ralt: bool = False, - rctrl: bool = False, - rmeta: bool = False, - shift: bool = False, + pressed: bool = False, # noqa: FBT001, FBT002 + lalt: bool = False, # noqa: FBT001, FBT002 + lctrl: bool = False, # noqa: FBT001, FBT002 + lmeta: bool = False, # noqa: FBT001, FBT002 + ralt: bool = False, # noqa: FBT001, FBT002 + rctrl: bool = False, # noqa: FBT001, FBT002 + rmeta: bool = False, # noqa: FBT001, FBT002 + shift: bool = False, # noqa: FBT001, FBT002 ) -> None: if isinstance(vk, ffi.CData): self.cdata = vk @@ -420,11 +413,11 @@ def __setattr__(self, attr: str, value: Any) -> None: def __repr__(self) -> str: """Return a representation of this Key object.""" params = [] - params.append(f"pressed={self.pressed!r}, vk=tcod.{_LOOKUP_VK[self.vk]}") + params.append(f"pressed={self.pressed!r}, vk=libtcodpy.{_LOOKUP_VK[self.vk]}") if self.c: - params.append("c=ord(%r)" % chr(self.c)) + params.append(f"c=ord({chr(self.c)!r})") if self.text: - params.append("text=%r" % self.text) + params.append(f"text={self.text!r}") for attr in [ "shift", "lalt", @@ -435,8 +428,8 @@ def __repr__(self) -> str: "rmeta", ]: if getattr(self, attr): - params.append(f"{attr}={getattr(self, attr)!r}") - return "tcod.Key(%s)" % ", ".join(params) + params.append(f"{attr}={getattr(self, attr)!r}") # noqa: PERF401 + return "libtcodpy.Key({})".format(", ".join(params)) @property def key_p(self) -> Any: @@ -513,15 +506,15 @@ def __repr__(self) -> str: "wheel_down", ]: if getattr(self, attr): - params.append(f"{attr}={getattr(self, attr)!r}") - return "tcod.Mouse(%s)" % ", ".join(params) + params.append(f"{attr}={getattr(self, attr)!r}") # noqa: PERF401 + return "libtcodpy.Mouse({})".format(", ".join(params)) @property def mouse_p(self) -> Any: return self.cdata -@deprecate("Call tcod.bsp.BSP(x, y, width, height) instead.", FutureWarning) +@deprecate("Call tcod.bsp.BSP(x, y, width, height) instead.", category=FutureWarning) def bsp_new_with_size(x: int, y: int, w: int, h: int) -> tcod.bsp.BSP: """Create a new BSP instance with the given rectangle. @@ -540,8 +533,8 @@ def bsp_new_with_size(x: int, y: int, w: int, h: int) -> tcod.bsp.BSP: return Bsp(x, y, w, h) -@deprecate("Call node.split_once instead.", FutureWarning) -def bsp_split_once(node: tcod.bsp.BSP, horizontal: bool, position: int) -> None: +@deprecate("Call node.split_once instead.", category=FutureWarning) +def bsp_split_once(node: tcod.bsp.BSP, horizontal: bool, position: int) -> None: # noqa: FBT001 """Deprecated function. .. deprecated:: 2.0 @@ -550,25 +543,27 @@ def bsp_split_once(node: tcod.bsp.BSP, horizontal: bool, position: int) -> None: node.split_once(horizontal, position) -@deprecate("Call node.split_recursive instead.", FutureWarning) +@deprecate("Call node.split_recursive instead.", category=FutureWarning) def bsp_split_recursive( node: tcod.bsp.BSP, - randomizer: tcod.random.Random | None, + randomizer: Literal[0] | tcod.random.Random | None, nb: int, - minHSize: int, - minVSize: int, - maxHRatio: float, - maxVRatio: float, + minHSize: int, # noqa: N803 + minVSize: int, # noqa: N803 + maxHRatio: float, # noqa: N803 + maxVRatio: float, # noqa: N803 ) -> None: """Deprecated function. .. deprecated:: 2.0 Use :any:`BSP.split_recursive` instead. """ + if randomizer == 0: + randomizer = None node.split_recursive(nb, minHSize, minVSize, maxHRatio, maxVRatio, randomizer) -@deprecate("Assign values via attribute instead.", FutureWarning) +@deprecate("Assign values via attribute instead.", category=FutureWarning) def bsp_resize(node: tcod.bsp.BSP, x: int, y: int, w: int, h: int) -> None: """Deprecated function. @@ -601,7 +596,7 @@ def bsp_right(node: tcod.bsp.BSP) -> tcod.bsp.BSP | None: return None if not node.children else node.children[1] -@deprecate("Get the parent with 'node.parent' instead.", FutureWarning) +@deprecate("Get the parent with 'node.parent' instead.", category=FutureWarning) def bsp_father(node: tcod.bsp.BSP) -> tcod.bsp.BSP | None: """Deprecated function. @@ -611,7 +606,7 @@ def bsp_father(node: tcod.bsp.BSP) -> tcod.bsp.BSP | None: return node.parent -@deprecate("Check for children with 'bool(node.children)' instead.", FutureWarning) +@deprecate("Check for children with 'bool(node.children)' instead.", category=FutureWarning) def bsp_is_leaf(node: tcod.bsp.BSP) -> bool: """Deprecated function. @@ -621,7 +616,7 @@ def bsp_is_leaf(node: tcod.bsp.BSP) -> bool: return not node.children -@deprecate("Use 'node.contains' instead.", FutureWarning) +@deprecate("Use 'node.contains' instead.", category=FutureWarning) def bsp_contains(node: tcod.bsp.BSP, cx: int, cy: int) -> bool: """Deprecated function. @@ -631,7 +626,7 @@ def bsp_contains(node: tcod.bsp.BSP, cx: int, cy: int) -> bool: return node.contains(cx, cy) -@deprecate("Use 'node.find_node' instead.", FutureWarning) +@deprecate("Use 'node.find_node' instead.", category=FutureWarning) def bsp_find_node(node: tcod.bsp.BSP, cx: int, cy: int) -> tcod.bsp.BSP | None: """Deprecated function. @@ -644,7 +639,7 @@ def bsp_find_node(node: tcod.bsp.BSP, cx: int, cy: int) -> tcod.bsp.BSP | None: def _bsp_traverse( node_iter: Iterable[tcod.bsp.BSP], callback: Callable[[tcod.bsp.BSP, Any], None], - userData: Any, + userData: Any, # noqa: N803 ) -> None: """Pack callback into a handle for use with the callback _pycall_bsp_callback.""" for node in node_iter: @@ -655,7 +650,7 @@ def _bsp_traverse( def bsp_traverse_pre_order( node: tcod.bsp.BSP, callback: Callable[[tcod.bsp.BSP, Any], None], - userData: Any = 0, + userData: Any = 0, # noqa: N803 ) -> None: """Traverse this nodes hierarchy with a callback. @@ -669,7 +664,7 @@ def bsp_traverse_pre_order( def bsp_traverse_in_order( node: tcod.bsp.BSP, callback: Callable[[tcod.bsp.BSP, Any], None], - userData: Any = 0, + userData: Any = 0, # noqa: N803 ) -> None: """Traverse this nodes hierarchy with a callback. @@ -683,7 +678,7 @@ def bsp_traverse_in_order( def bsp_traverse_post_order( node: tcod.bsp.BSP, callback: Callable[[tcod.bsp.BSP, Any], None], - userData: Any = 0, + userData: Any = 0, # noqa: N803 ) -> None: """Traverse this nodes hierarchy with a callback. @@ -697,7 +692,7 @@ def bsp_traverse_post_order( def bsp_traverse_level_order( node: tcod.bsp.BSP, callback: Callable[[tcod.bsp.BSP, Any], None], - userData: Any = 0, + userData: Any = 0, # noqa: N803 ) -> None: """Traverse this nodes hierarchy with a callback. @@ -707,11 +702,11 @@ def bsp_traverse_level_order( _bsp_traverse(node.level_order(), callback, userData) -@deprecate("Iterate over nodes using " "'for n in node.inverted_level_order():' instead.") +@deprecate("Iterate over nodes using 'for n in node.inverted_level_order():' instead.") def bsp_traverse_inverted_level_order( node: tcod.bsp.BSP, callback: Callable[[tcod.bsp.BSP, Any], None], - userData: Any = 0, + userData: Any = 0, # noqa: N803 ) -> None: """Traverse this nodes hierarchy with a callback. @@ -735,7 +730,7 @@ def bsp_remove_sons(node: tcod.bsp.BSP) -> None: node.children = () -@deprecate("libtcod objects are deleted automatically.", FutureWarning) +@deprecate("libtcod objects are deleted automatically.", category=FutureWarning) def bsp_delete(node: tcod.bsp.BSP) -> None: """Exists for backward compatibility. Does nothing. @@ -748,7 +743,7 @@ def bsp_delete(node: tcod.bsp.BSP) -> None: """ -@pending_deprecate() +@deprecate(_PENDING_DEPRECATE_MSG, category=PendingDeprecationWarning) def color_lerp(c1: tuple[int, int, int], c2: tuple[int, int, int], a: float) -> Color: """Return the linear interpolation between two colors. @@ -768,7 +763,7 @@ def color_lerp(c1: tuple[int, int, int], c2: tuple[int, int, int], a: float) -> return Color._new_from_cdata(lib.TCOD_color_lerp(c1, c2, a)) -@pending_deprecate() +@deprecate(_PENDING_DEPRECATE_MSG, category=PendingDeprecationWarning) def color_set_hsv(c: Color, h: float, s: float, v: float) -> None: """Set a color using: hue, saturation, and value parameters. @@ -785,7 +780,7 @@ def color_set_hsv(c: Color, h: float, s: float, v: float) -> None: c[:] = new_color.r, new_color.g, new_color.b -@pending_deprecate() +@deprecate(_PENDING_DEPRECATE_MSG, category=PendingDeprecationWarning) def color_get_hsv(c: tuple[int, int, int]) -> tuple[float, float, float]: """Return the (hue, saturation, value) of a color. @@ -802,8 +797,8 @@ def color_get_hsv(c: tuple[int, int, int]) -> tuple[float, float, float]: return hsv[0], hsv[1], hsv[2] -@pending_deprecate() -def color_scale_HSV(c: Color, scoef: float, vcoef: float) -> None: +@deprecate(_PENDING_DEPRECATE_MSG, category=PendingDeprecationWarning) +def color_scale_HSV(c: Color, scoef: float, vcoef: float) -> None: # noqa: N802 """Scale a color's saturation and value. Does not return a new Color. ``c`` is modified in-place. @@ -821,7 +816,7 @@ def color_scale_HSV(c: Color, scoef: float, vcoef: float) -> None: c[:] = color_p.r, color_p.g, color_p.b -@pending_deprecate() +@deprecate(_PENDING_DEPRECATE_MSG, category=PendingDeprecationWarning) def color_gen_map(colors: Iterable[tuple[int, int, int]], indexes: Iterable[int]) -> list[Color]: """Return a smoothly defined scale of colors. @@ -838,7 +833,8 @@ def color_gen_map(colors: Iterable[tuple[int, int, int]], indexes: Iterable[int] List[Color]: A list of Color instances. Example: - >>> tcod.color_gen_map([(0, 0, 0), (255, 128, 0)], [0, 5]) + >>> from tcod import libtcodpy + >>> libtcodpy.color_gen_map([(0, 0, 0), (255, 128, 0)], [0, 5]) [Color(0, 0, 0), Color(51, 25, 0), Color(102, 51, 0), \ Color(153, 76, 0), Color(204, 102, 0), Color(255, 128, 0)] """ @@ -858,10 +854,10 @@ def console_init_root( w: int, h: int, title: str | None = None, - fullscreen: bool = False, + fullscreen: bool = False, # noqa: FBT001, FBT002 renderer: int | None = None, order: Literal["C", "F"] = "C", - vsync: bool | None = None, + vsync: bool | None = None, # noqa: FBT001 ) -> tcod.console.Console: """Set up the primary display and return the root console. @@ -957,7 +953,7 @@ def console_init_root( https://python-tcod.readthedocs.io/en/latest/tcod/getting-started.html""" ) def console_set_custom_font( - fontFile: str | PathLike[str], + fontFile: str | PathLike[str], # noqa: N803 flags: int = FONT_LAYOUT_ASCII_INCOL, nb_char_horiz: int = 0, nb_char_vertic: int = 0, @@ -990,8 +986,8 @@ def console_set_custom_font( .. versionchanged:: 16.0 Added PathLike support. `fontFile` no longer takes bytes. """ - fontFile = Path(fontFile).resolve(strict=True) - _check(lib.TCOD_console_set_custom_font(bytes(fontFile), flags, nb_char_horiz, nb_char_vertic)) + fontFile = Path(fontFile).resolve(strict=True) # noqa: N806 + _check(lib.TCOD_console_set_custom_font(_path_encode(fontFile), flags, nb_char_horiz, nb_char_vertic)) @deprecate("Check `con.width` instead.") @@ -1027,7 +1023,7 @@ def console_get_height(con: tcod.console.Console) -> int: @deprecate("Setup fonts using the tcod.tileset module.") -def console_map_ascii_code_to_font(asciiCode: int, fontCharX: int, fontCharY: int) -> None: +def console_map_ascii_code_to_font(asciiCode: int, fontCharX: int, fontCharY: int) -> None: # noqa: N803 """Set a character code to new coordinates on the tile-set. `asciiCode` should be any Unicode codepoint. @@ -1047,7 +1043,7 @@ def console_map_ascii_code_to_font(asciiCode: int, fontCharX: int, fontCharY: in @deprecate("Setup fonts using the tcod.tileset module.") -def console_map_ascii_codes_to_font(firstAsciiCode: int, nbCodes: int, fontCharX: int, fontCharY: int) -> None: +def console_map_ascii_codes_to_font(firstAsciiCode: int, nbCodes: int, fontCharX: int, fontCharY: int) -> None: # noqa: N803 """Remap a contiguous set of codes to a contiguous set of tiles. Both the tile-set and character codes must be contiguous to use this @@ -1071,7 +1067,7 @@ def console_map_ascii_codes_to_font(firstAsciiCode: int, nbCodes: int, fontCharX @deprecate("Setup fonts using the tcod.tileset module.") -def console_map_string_to_font(s: str, fontCharX: int, fontCharY: int) -> None: +def console_map_string_to_font(s: str, fontCharX: int, fontCharY: int) -> None: # noqa: N803 r"""Remap a string of codes to a contiguous set of tiles. Args: @@ -1103,7 +1099,7 @@ def console_is_fullscreen() -> bool: @deprecate("This function is not supported if contexts are being used.") -def console_set_fullscreen(fullscreen: bool) -> None: +def console_set_fullscreen(fullscreen: bool) -> None: # noqa: FBT001 """Change the display to be fullscreen or windowed. Args: @@ -1159,7 +1155,7 @@ def console_set_window_title(title: str) -> None: lib.TCOD_console_set_window_title(_bytes(title)) -@pending_deprecate() +@deprecate(_PENDING_DEPRECATE_MSG, category=PendingDeprecationWarning) def console_credits() -> None: lib.TCOD_console_credits() @@ -1168,7 +1164,7 @@ def console_credits_reset() -> None: lib.TCOD_console_credits_reset() -def console_credits_render(x: int, y: int, alpha: bool) -> bool: +def console_credits_render(x: int, y: int, alpha: bool) -> bool: # noqa: FBT001 return bool(lib.TCOD_console_credits_render(x, y, alpha)) @@ -1225,7 +1221,7 @@ def console_flush( DeprecationWarning, stacklevel=2, ) - if len(clear_color) == 3: + if len(clear_color) == 3: # noqa: PLR2004 clear_color = clear_color[0], clear_color[1], clear_color[2], 255 options = { "keep_aspect": keep_aspect, @@ -1287,7 +1283,7 @@ def console_clear(con: tcod.console.Console) -> None: lib.TCOD_console_clear(_console(con)) -@pending_deprecate() +@deprecate(_PENDING_DEPRECATE_MSG, category=PendingDeprecationWarning) def console_put_char( con: tcod.console.Console, x: int, @@ -1307,7 +1303,7 @@ def console_put_char( lib.TCOD_console_put_char(_console(con), x, y, _int(c), flag) -@pending_deprecate() +@deprecate(_PENDING_DEPRECATE_MSG, category=PendingDeprecationWarning) def console_put_char_ex( con: tcod.console.Console, x: int, @@ -1331,7 +1327,7 @@ def console_put_char_ex( lib.TCOD_console_put_char_ex(_console(con), x, y, _int(c), fore, back) -@pending_deprecate() +@deprecate(_PENDING_DEPRECATE_MSG, category=PendingDeprecationWarning) def console_set_char_background( con: tcod.console.Console, x: int, @@ -1476,6 +1472,9 @@ def console_print_ex( con (Console): Any Console instance. x (int): Character x position from the left. y (int): Character y position from the top. + flag: Blending mode to use. + alignment: The libtcod alignment constant. + fmt: A unicode or bytes string, optionally using color codes. .. deprecated:: 8.5 Use :any:`Console.print_` instead. @@ -1544,7 +1543,7 @@ def console_rect( y: int, w: int, h: int, - clr: bool, + clr: bool, # noqa: FBT001 flag: int = BKGND_DEFAULT, ) -> None: """Draw a the background color on a rect optionally clearing the text. @@ -1600,7 +1599,7 @@ def console_print_frame( y: int, w: int, h: int, - clear: bool = True, + clear: bool = True, # noqa: FBT001, FBT002 flag: int = BKGND_DEFAULT, fmt: str = "", ) -> None: @@ -1618,11 +1617,11 @@ def console_print_frame( .. deprecated:: 8.5 Use :any:`Console.print_frame` instead. """ - fmt = _fmt(fmt) if fmt else ffi.NULL - _check(lib.TCOD_console_printf_frame(_console(con), x, y, w, h, clear, flag, fmt)) + fmt_: Any = _fmt(fmt) if fmt else ffi.NULL + _check(lib.TCOD_console_printf_frame(_console(con), x, y, w, h, clear, flag, fmt_)) -@pending_deprecate() +@deprecate(_PENDING_DEPRECATE_MSG, category=PendingDeprecationWarning) def console_set_color_control(con: int, fore: tuple[int, int, int], back: tuple[int, int, int]) -> None: """Configure :term:`color controls`. @@ -1686,11 +1685,11 @@ def console_get_char(con: tcod.console.Console, x: int, y: int) -> int: Array access performs significantly faster than using this function. See :any:`Console.ch`. """ - return lib.TCOD_console_get_char(_console(con), x, y) # type: ignore + return lib.TCOD_console_get_char(_console(con), x, y) -@deprecate("This function is not supported if contexts are being used.", FutureWarning) -def console_set_fade(fade: int, fadingColor: tuple[int, int, int]) -> None: +@deprecate("This function is not supported if contexts are being used.", category=FutureWarning) +def console_set_fade(fade: int, fadingColor: tuple[int, int, int]) -> None: # noqa: N803 """Deprecated function. .. deprecated:: 11.13 @@ -1699,7 +1698,7 @@ def console_set_fade(fade: int, fadingColor: tuple[int, int, int]) -> None: lib.TCOD_console_set_fade(fade, fadingColor) -@deprecate("This function is not supported if contexts are being used.", FutureWarning) +@deprecate("This function is not supported if contexts are being used.", category=FutureWarning) def console_get_fade() -> int: """Deprecated function. @@ -1709,7 +1708,7 @@ def console_get_fade() -> int: return int(lib.TCOD_console_get_fade()) -@deprecate("This function is not supported if contexts are being used.", FutureWarning) +@deprecate("This function is not supported if contexts are being used.", category=FutureWarning) def console_get_fading_color() -> Color: """Deprecated function. @@ -1721,12 +1720,11 @@ def console_get_fading_color() -> Color: # handling keyboard input @deprecate("Use the tcod.event.wait function to wait for events.") -def console_wait_for_keypress(flush: bool) -> Key: +def console_wait_for_keypress(flush: bool) -> Key: # noqa: FBT001 """Block until the user presses a key, then returns a new Key. Args: - flush bool: If True then the event queue is cleared before waiting - for the next event. + flush: If True then the event queue is cleared before waiting for the next event. Returns: Key: A new Key instance. @@ -1763,7 +1761,7 @@ def console_check_for_keypress(flags: int = KEY_RELEASED) -> Key: return key -@deprecate("Use tcod.event.get_keyboard_state to see if a key is held.", FutureWarning) +@deprecate("Use tcod.event.get_keyboard_state to see if a key is held.", category=FutureWarning) def console_is_key_pressed(key: int) -> bool: """Return True if a key is held. @@ -1806,7 +1804,7 @@ def console_from_file(filename: str | PathLike[str]) -> tcod.console.Console: Added PathLike support. """ filename = Path(filename).resolve(strict=True) - return tcod.console.Console._from_cdata(_check_p(lib.TCOD_console_from_file(bytes(filename)))) + return tcod.console.Console._from_cdata(_check_p(lib.TCOD_console_from_file(_path_encode(filename)))) @deprecate("Call the `Console.blit` method instead.") @@ -1871,7 +1869,7 @@ def console_delete(con: tcod.console.Console) -> None: ) else: warnings.warn( - "You no longer need to make this call, " "Console's are deleted when they go out of scope.", + "You no longer need to make this call, Console's are deleted when they go out of scope.", DeprecationWarning, stacklevel=2, ) @@ -1985,7 +1983,7 @@ def console_load_asc(con: tcod.console.Console, filename: str | PathLike[str]) - Added PathLike support. """ filename = Path(filename).resolve(strict=True) - return bool(lib.TCOD_console_load_asc(_console(con), bytes(filename))) + return bool(lib.TCOD_console_load_asc(_console(con), _path_encode(filename))) @deprecate("This format is not actively supported") @@ -1998,7 +1996,7 @@ def console_save_asc(con: tcod.console.Console, filename: str | PathLike[str]) - .. versionchanged:: 16.0 Added PathLike support. """ - return bool(lib.TCOD_console_save_asc(_console(con), bytes(Path(filename)))) + return bool(lib.TCOD_console_save_asc(_console(con), _path_encode(Path(filename)))) @deprecate("This format is not actively supported") @@ -2012,7 +2010,7 @@ def console_load_apf(con: tcod.console.Console, filename: str | PathLike[str]) - Added PathLike support. """ filename = Path(filename).resolve(strict=True) - return bool(lib.TCOD_console_load_apf(_console(con), bytes(filename))) + return bool(lib.TCOD_console_load_apf(_console(con), _path_encode(filename))) @deprecate("This format is not actively supported") @@ -2025,7 +2023,7 @@ def console_save_apf(con: tcod.console.Console, filename: str | PathLike[str]) - .. versionchanged:: 16.0 Added PathLike support. """ - return bool(lib.TCOD_console_save_apf(_console(con), bytes(Path(filename)))) + return bool(lib.TCOD_console_save_apf(_console(con), _path_encode(Path(filename)))) @deprecate("Use tcod.console.load_xp to load this file.") @@ -2040,7 +2038,7 @@ def console_load_xp(con: tcod.console.Console, filename: str | PathLike[str]) -> Added PathLike support. """ filename = Path(filename).resolve(strict=True) - return bool(lib.TCOD_console_load_xp(_console(con), bytes(filename))) + return bool(lib.TCOD_console_load_xp(_console(con), _path_encode(filename))) @deprecate("Use tcod.console.save_xp to save this console.") @@ -2050,7 +2048,7 @@ def console_save_xp(con: tcod.console.Console, filename: str | PathLike[str], co .. versionchanged:: 16.0 Added PathLike support. """ - return bool(lib.TCOD_console_save_xp(_console(con), bytes(Path(filename)), compress_level)) + return bool(lib.TCOD_console_save_xp(_console(con), _path_encode(Path(filename)), compress_level)) @deprecate("Use tcod.console.load_xp to load this file.") @@ -2061,7 +2059,7 @@ def console_from_xp(filename: str | PathLike[str]) -> tcod.console.Console: Added PathLike support. """ filename = Path(filename).resolve(strict=True) - return tcod.console.Console._from_cdata(_check_p(lib.TCOD_console_from_xp(bytes(filename)))) + return tcod.console.Console._from_cdata(_check_p(lib.TCOD_console_from_xp(_path_encode(filename)))) @deprecate("Use tcod.console.load_xp to load this file.") @@ -2074,7 +2072,7 @@ def console_list_load_xp( Added PathLike support. """ filename = Path(filename).resolve(strict=True) - tcod_list = lib.TCOD_console_list_from_xp(bytes(filename)) + tcod_list = lib.TCOD_console_list_from_xp(_path_encode(filename)) if tcod_list == ffi.NULL: return None try: @@ -2102,12 +2100,12 @@ def console_list_save_xp( try: for console in console_list: lib.TCOD_list_push(tcod_list, _console(console)) - return bool(lib.TCOD_console_list_save_xp(tcod_list, bytes(Path(filename)), compress_level)) + return bool(lib.TCOD_console_list_save_xp(tcod_list, _path_encode(Path(filename)), compress_level)) finally: lib.TCOD_list_delete(tcod_list) -@pending_deprecate() +@deprecate(_PENDING_DEPRECATE_MSG, category=PendingDeprecationWarning) def path_new_using_map(m: tcod.map.Map, dcost: float = 1.41) -> tcod.path.AStar: """Return a new AStar using the given Map. @@ -2122,12 +2120,12 @@ def path_new_using_map(m: tcod.map.Map, dcost: float = 1.41) -> tcod.path.AStar: return tcod.path.AStar(m, dcost) -@pending_deprecate() +@deprecate(_PENDING_DEPRECATE_MSG, category=PendingDeprecationWarning) def path_new_using_function( w: int, h: int, func: Callable[[int, int, int, int, Any], float], - userData: Any = 0, + userData: Any = 0, # noqa: N803 dcost: float = 1.41, ) -> tcod.path.AStar: """Return a new AStar using the given callable function. @@ -2135,8 +2133,8 @@ def path_new_using_function( Args: w (int): Clipping width. h (int): Clipping height. - func (Callable[[int, int, int, int, Any], float]): - userData (Any): + func: Callback function with the format: `f(origin_x, origin_y, dest_x, dest_y, userData) -> float` + userData (Any): An object passed to the callback. dcost (float): A multiplier for the cost of diagonal movement. Can be set to 0 to disable diagonal movement. @@ -2146,7 +2144,7 @@ def path_new_using_function( return tcod.path.AStar(tcod.path._EdgeCostFunc((func, userData), (w, h)), dcost) -@pending_deprecate() +@deprecate(_PENDING_DEPRECATE_MSG, category=PendingDeprecationWarning) def path_compute(p: tcod.path.AStar, ox: int, oy: int, dx: int, dy: int) -> bool: """Find a path from (ox, oy) to (dx, dy). Return True if path is found. @@ -2163,7 +2161,7 @@ def path_compute(p: tcod.path.AStar, ox: int, oy: int, dx: int, dy: int) -> bool return bool(lib.TCOD_path_compute(p._path_c, ox, oy, dx, dy)) -@pending_deprecate() +@deprecate(_PENDING_DEPRECATE_MSG, category=PendingDeprecationWarning) def path_get_origin(p: tcod.path.AStar) -> tuple[int, int]: """Get the current origin position. @@ -2181,7 +2179,7 @@ def path_get_origin(p: tcod.path.AStar) -> tuple[int, int]: return x[0], y[0] -@pending_deprecate() +@deprecate(_PENDING_DEPRECATE_MSG, category=PendingDeprecationWarning) def path_get_destination(p: tcod.path.AStar) -> tuple[int, int]: """Get the current destination position. @@ -2197,7 +2195,7 @@ def path_get_destination(p: tcod.path.AStar) -> tuple[int, int]: return x[0], y[0] -@pending_deprecate() +@deprecate(_PENDING_DEPRECATE_MSG, category=PendingDeprecationWarning) def path_size(p: tcod.path.AStar) -> int: """Return the current length of the computed path. @@ -2210,7 +2208,7 @@ def path_size(p: tcod.path.AStar) -> int: return int(lib.TCOD_path_size(p._path_c)) -@pending_deprecate() +@deprecate(_PENDING_DEPRECATE_MSG, category=PendingDeprecationWarning) def path_reverse(p: tcod.path.AStar) -> None: """Reverse the direction of a path. @@ -2222,7 +2220,7 @@ def path_reverse(p: tcod.path.AStar) -> None: lib.TCOD_path_reverse(p._path_c) -@pending_deprecate() +@deprecate(_PENDING_DEPRECATE_MSG, category=PendingDeprecationWarning) def path_get(p: tcod.path.AStar, idx: int) -> tuple[int, int]: """Get a point on a path. @@ -2236,7 +2234,7 @@ def path_get(p: tcod.path.AStar, idx: int) -> tuple[int, int]: return x[0], y[0] -@pending_deprecate() +@deprecate(_PENDING_DEPRECATE_MSG, category=PendingDeprecationWarning) def path_is_empty(p: tcod.path.AStar) -> bool: """Return True if a path is empty. @@ -2249,8 +2247,8 @@ def path_is_empty(p: tcod.path.AStar) -> bool: return bool(lib.TCOD_path_is_empty(p._path_c)) -@pending_deprecate() -def path_walk(p: tcod.path.AStar, recompute: bool) -> tuple[int, int] | tuple[None, None]: +@deprecate(_PENDING_DEPRECATE_MSG, category=PendingDeprecationWarning) +def path_walk(p: tcod.path.AStar, recompute: bool) -> tuple[int, int] | tuple[None, None]: # noqa: FBT001 """Return the next (x, y) point in a path, or (None, None) if it's empty. When ``recompute`` is True and a previously valid path reaches a point @@ -2279,48 +2277,48 @@ def path_delete(p: tcod.path.AStar) -> None: """ -@pending_deprecate() +@deprecate(_PENDING_DEPRECATE_MSG, category=PendingDeprecationWarning) def dijkstra_new(m: tcod.map.Map, dcost: float = 1.41) -> tcod.path.Dijkstra: return tcod.path.Dijkstra(m, dcost) -@pending_deprecate() +@deprecate(_PENDING_DEPRECATE_MSG, category=PendingDeprecationWarning) def dijkstra_new_using_function( w: int, h: int, func: Callable[[int, int, int, int, Any], float], - userData: Any = 0, + userData: Any = 0, # noqa: N803 dcost: float = 1.41, ) -> tcod.path.Dijkstra: return tcod.path.Dijkstra(tcod.path._EdgeCostFunc((func, userData), (w, h)), dcost) -@pending_deprecate() +@deprecate(_PENDING_DEPRECATE_MSG, category=PendingDeprecationWarning) def dijkstra_compute(p: tcod.path.Dijkstra, ox: int, oy: int) -> None: lib.TCOD_dijkstra_compute(p._path_c, ox, oy) -@pending_deprecate() +@deprecate(_PENDING_DEPRECATE_MSG, category=PendingDeprecationWarning) def dijkstra_path_set(p: tcod.path.Dijkstra, x: int, y: int) -> bool: return bool(lib.TCOD_dijkstra_path_set(p._path_c, x, y)) -@pending_deprecate() +@deprecate(_PENDING_DEPRECATE_MSG, category=PendingDeprecationWarning) def dijkstra_get_distance(p: tcod.path.Dijkstra, x: int, y: int) -> int: return int(lib.TCOD_dijkstra_get_distance(p._path_c, x, y)) -@pending_deprecate() +@deprecate(_PENDING_DEPRECATE_MSG, category=PendingDeprecationWarning) def dijkstra_size(p: tcod.path.Dijkstra) -> int: return int(lib.TCOD_dijkstra_size(p._path_c)) -@pending_deprecate() +@deprecate(_PENDING_DEPRECATE_MSG, category=PendingDeprecationWarning) def dijkstra_reverse(p: tcod.path.Dijkstra) -> None: lib.TCOD_dijkstra_reverse(p._path_c) -@pending_deprecate() +@deprecate(_PENDING_DEPRECATE_MSG, category=PendingDeprecationWarning) def dijkstra_get(p: tcod.path.Dijkstra, idx: int) -> tuple[int, int]: x = ffi.new("int *") y = ffi.new("int *") @@ -2328,12 +2326,12 @@ def dijkstra_get(p: tcod.path.Dijkstra, idx: int) -> tuple[int, int]: return x[0], y[0] -@pending_deprecate() +@deprecate(_PENDING_DEPRECATE_MSG, category=PendingDeprecationWarning) def dijkstra_is_empty(p: tcod.path.Dijkstra) -> bool: return bool(lib.TCOD_dijkstra_is_empty(p._path_c)) -@pending_deprecate() +@deprecate(_PENDING_DEPRECATE_MSG, category=PendingDeprecationWarning) def dijkstra_path_walk( p: tcod.path.Dijkstra, ) -> tuple[int, int] | tuple[None, None]: @@ -2352,7 +2350,7 @@ def dijkstra_delete(p: tcod.path.Dijkstra) -> None: """ -def _heightmap_cdata(array: NDArray[np.float32]) -> ffi.CData: +def _heightmap_cdata(array: NDArray[np.float32]) -> Any: """Return a new TCOD_heightmap_t instance using an array. Formatting is verified during this function. @@ -2363,13 +2361,14 @@ def _heightmap_cdata(array: NDArray[np.float32]) -> ffi.CData: msg = "array must be a contiguous segment." raise ValueError(msg) if array.dtype != np.float32: - raise ValueError("array dtype must be float32, not %r" % array.dtype) + msg = f"array dtype must be float32, not {array.dtype!r}" + raise ValueError(msg) height, width = array.shape pointer = ffi.from_buffer("float *", array) return ffi.new("TCOD_heightmap_t *", (width, height, pointer)) -@pending_deprecate() +@deprecate(_PENDING_DEPRECATE_MSG, category=PendingDeprecationWarning) def heightmap_new(w: int, h: int, order: str = "C") -> NDArray[np.float32]: """Return a new numpy.ndarray formatted for use with heightmap functions. @@ -2390,11 +2389,10 @@ def heightmap_new(w: int, h: int, order: str = "C") -> NDArray[np.float32]: """ if order == "C": return np.zeros((h, w), np.float32, order="C") - elif order == "F": + if order == "F": return np.zeros((w, h), np.float32, order="F") - else: - msg = "Invalid order parameter, should be 'C' or 'F'." - raise ValueError(msg) + msg = "Invalid order parameter, should be 'C' or 'F'." + raise ValueError(msg) @deprecate("Assign to heightmaps as a NumPy array instead.") @@ -2406,7 +2404,7 @@ def heightmap_set_value(hm: NDArray[np.float32], x: int, y: int, value: float) - """ if hm.flags["C_CONTIGUOUS"]: warnings.warn( - "Assign to this heightmap with hm[i,j] = value\n" "consider using order='F'", + "Assign to this heightmap with hm[i,j] = value\nconsider using order='F'", DeprecationWarning, stacklevel=2, ) @@ -2484,6 +2482,7 @@ def heightmap_copy(hm1: NDArray[np.float32], hm2: NDArray[np.float32]) -> None: """Copy the heightmap ``hm1`` to ``hm2``. Args: + hm: A numpy.ndarray formatted for heightmap functions. hm1 (numpy.ndarray): The source heightmap. hm2 (numpy.ndarray): The destination heightmap. @@ -2493,18 +2492,19 @@ def heightmap_copy(hm1: NDArray[np.float32], hm2: NDArray[np.float32]) -> None: hm2[:] = hm1[:] -@pending_deprecate() +@deprecate(_PENDING_DEPRECATE_MSG, category=PendingDeprecationWarning) def heightmap_normalize(hm: NDArray[np.float32], mi: float = 0.0, ma: float = 1.0) -> None: """Normalize heightmap values between ``mi`` and ``ma``. Args: + hm: A numpy.ndarray formatted for heightmap functions. mi (float): The lowest value after normalization. ma (float): The highest value after normalization. """ lib.TCOD_heightmap_normalize(_heightmap_cdata(hm), mi, ma) -@pending_deprecate() +@deprecate(_PENDING_DEPRECATE_MSG, category=PendingDeprecationWarning) def heightmap_lerp_hm( hm1: NDArray[np.float32], hm2: NDArray[np.float32], @@ -2568,7 +2568,7 @@ def heightmap_multiply_hm( hm3[:] = hm1[:] * hm2[:] -@pending_deprecate() +@deprecate(_PENDING_DEPRECATE_MSG, category=PendingDeprecationWarning) def heightmap_add_hill(hm: NDArray[np.float32], x: float, y: float, radius: float, height: float) -> None: """Add a hill (a half spheroid) at given position. @@ -2584,7 +2584,7 @@ def heightmap_add_hill(hm: NDArray[np.float32], x: float, y: float, radius: floa lib.TCOD_heightmap_add_hill(_heightmap_cdata(hm), x, y, radius, height) -@pending_deprecate() +@deprecate(_PENDING_DEPRECATE_MSG, category=PendingDeprecationWarning) def heightmap_dig_hill(hm: NDArray[np.float32], x: float, y: float, radius: float, height: float) -> None: """Dig a hill in a heightmap. @@ -2602,12 +2602,12 @@ def heightmap_dig_hill(hm: NDArray[np.float32], x: float, y: float, radius: floa lib.TCOD_heightmap_dig_hill(_heightmap_cdata(hm), x, y, radius, height) -@pending_deprecate() +@deprecate(_PENDING_DEPRECATE_MSG, category=PendingDeprecationWarning) def heightmap_rain_erosion( hm: NDArray[np.float32], - nbDrops: int, - erosionCoef: float, - sedimentationCoef: float, + nbDrops: int, # noqa: N803 + erosionCoef: float, # noqa: N803 + sedimentationCoef: float, # noqa: N803 rnd: tcod.random.Random | None = None, ) -> None: """Simulate the effect of rain drops on the terrain, resulting in erosion. @@ -2631,15 +2631,15 @@ def heightmap_rain_erosion( ) -@pending_deprecate() +@deprecate(_PENDING_DEPRECATE_MSG, category=PendingDeprecationWarning) def heightmap_kernel_transform( hm: NDArray[np.float32], kernelsize: int, dx: Sequence[int], dy: Sequence[int], weight: Sequence[float], - minLevel: float, - maxLevel: float, + minLevel: float, # noqa: N803 + maxLevel: float, # noqa: N803 ) -> None: """Apply a generic transformation on the map, so that each resulting cell value is the weighted sum of several neighbor cells. @@ -2673,13 +2673,13 @@ def heightmap_kernel_transform( Example: >>> import numpy as np + >>> from tcod import libtcodpy >>> heightmap = np.zeros((3, 3), dtype=np.float32) >>> heightmap[:,1] = 1 >>> dx = [-1, 1, 0] >>> dy = [0, 0, 0] >>> weight = [0.33, 0.33, 0.33] - >>> tcod.heightmap_kernel_transform(heightmap, 3, dx, dy, weight, - ... 0.0, 1.0) + >>> libtcodpy.heightmap_kernel_transform(heightmap, 3, dx, dy, weight, 0.0, 1.0) """ c_dx = ffi.new("int[]", dx) c_dy = ffi.new("int[]", dy) @@ -2687,11 +2687,11 @@ def heightmap_kernel_transform( lib.TCOD_heightmap_kernel_transform(_heightmap_cdata(hm), kernelsize, c_dx, c_dy, c_weight, minLevel, maxLevel) -@pending_deprecate() +@deprecate(_PENDING_DEPRECATE_MSG, category=PendingDeprecationWarning) def heightmap_add_voronoi( hm: NDArray[np.float32], - nbPoints: Any, - nbCoef: int, + nbPoints: Any, # noqa: N803 + nbCoef: int, # noqa: N803 coef: Sequence[float], rnd: tcod.random.Random | None = None, ) -> None: @@ -2708,7 +2708,7 @@ def heightmap_add_voronoi( second closest site : coef[1], ... rnd (Optional[Random]): A Random instance, or None. """ - nbPoints = len(coef) + nbPoints = len(coef) # noqa: N806 ccoef = ffi.new("float[]", coef) lib.TCOD_heightmap_add_voronoi( _heightmap_cdata(hm), @@ -2810,15 +2810,15 @@ def heightmap_scale_fbm( ) -@pending_deprecate() +@deprecate(_PENDING_DEPRECATE_MSG, category=PendingDeprecationWarning) def heightmap_dig_bezier( hm: NDArray[np.float32], px: tuple[int, int, int, int], py: tuple[int, int, int, int], - startRadius: float, - startDepth: float, - endRadius: float, - endDepth: float, + startRadius: float, # noqa: N803 + startDepth: float, # noqa: N803 + endRadius: float, # noqa: N803 + endDepth: float, # noqa: N803 ) -> None: """Carve a path along a cubic Bezier curve. @@ -2853,24 +2853,23 @@ def heightmap_get_value(hm: NDArray[np.float32], x: int, y: int) -> float: """ if hm.flags["C_CONTIGUOUS"]: warnings.warn( - "Get a value from this heightmap with hm[i,j]\n" "consider using order='F'", + "Get a value from this heightmap with hm[i,j]\nconsider using order='F'", DeprecationWarning, stacklevel=2, ) - return hm[y, x] # type: ignore - elif hm.flags["F_CONTIGUOUS"]: + return hm.item(y, x) + if hm.flags["F_CONTIGUOUS"]: warnings.warn( "Get a value from this heightmap with hm[x,y]", DeprecationWarning, stacklevel=2, ) - return hm[x, y] # type: ignore - else: - msg = "This array is not contiguous." - raise ValueError(msg) + return hm.item(x, y) + msg = "This array is not contiguous." + raise ValueError(msg) -@pending_deprecate() +@deprecate(_PENDING_DEPRECATE_MSG, category=PendingDeprecationWarning) def heightmap_get_interpolated_value(hm: NDArray[np.float32], x: float, y: float) -> float: """Return the interpolated height at non integer coordinates. @@ -2885,7 +2884,7 @@ def heightmap_get_interpolated_value(hm: NDArray[np.float32], x: float, y: float return float(lib.TCOD_heightmap_get_interpolated_value(_heightmap_cdata(hm), x, y)) -@pending_deprecate() +@deprecate(_PENDING_DEPRECATE_MSG, category=PendingDeprecationWarning) def heightmap_get_slope(hm: NDArray[np.float32], x: int, y: int) -> float: """Return the slope between 0 and (pi / 2) at given coordinates. @@ -2900,8 +2899,8 @@ def heightmap_get_slope(hm: NDArray[np.float32], x: int, y: int) -> float: return float(lib.TCOD_heightmap_get_slope(_heightmap_cdata(hm), x, y)) -@pending_deprecate() -def heightmap_get_normal(hm: NDArray[np.float32], x: float, y: float, waterLevel: float) -> tuple[float, float, float]: +@deprecate(_PENDING_DEPRECATE_MSG, category=PendingDeprecationWarning) +def heightmap_get_normal(hm: NDArray[np.float32], x: float, y: float, waterLevel: float) -> tuple[float, float, float]: # noqa: N803 """Return the map normal at given coordinates. Args: @@ -2915,7 +2914,7 @@ def heightmap_get_normal(hm: NDArray[np.float32], x: float, y: float, waterLevel """ cn = ffi.new("float[3]") lib.TCOD_heightmap_get_normal(_heightmap_cdata(hm), x, y, cn, waterLevel) - return tuple(cn) # type: ignore + return tuple(cn) @deprecate("This function is deprecated, see documentation.") @@ -2937,13 +2936,13 @@ def heightmap_count_cells(hm: NDArray[np.float32], mi: float, ma: float) -> int: return int(lib.TCOD_heightmap_count_cells(_heightmap_cdata(hm), mi, ma)) -@pending_deprecate() +@deprecate(_PENDING_DEPRECATE_MSG, category=PendingDeprecationWarning) def heightmap_has_land_on_border(hm: NDArray[np.float32], waterlevel: float) -> bool: """Returns True if the map edges are below ``waterlevel``, otherwise False. Args: hm (numpy.ndarray): A numpy.ndarray formatted for heightmap functions. - waterLevel (float): The water level to use. + waterlevel (float): The water level to use. Returns: bool: True if the map edges are below ``waterlevel``, otherwise False. @@ -2981,52 +2980,52 @@ def heightmap_delete(hm: Any) -> None: """ -@deprecate("Use `tcod.image.Image(width, height)` instead.", FutureWarning) +@deprecate("Use `tcod.image.Image(width, height)` instead.", category=FutureWarning) def image_new(width: int, height: int) -> tcod.image.Image: return tcod.image.Image(width, height) -@deprecate("Use the `image.clear()` method instead.", FutureWarning) +@deprecate("Use the `image.clear()` method instead.", category=FutureWarning) def image_clear(image: tcod.image.Image, col: tuple[int, int, int]) -> None: image.clear(col) -@deprecate("Use the `image.invert()` method instead.", FutureWarning) +@deprecate("Use the `image.invert()` method instead.", category=FutureWarning) def image_invert(image: tcod.image.Image) -> None: image.invert() -@deprecate("Use the `image.hflip()` method instead.", FutureWarning) +@deprecate("Use the `image.hflip()` method instead.", category=FutureWarning) def image_hflip(image: tcod.image.Image) -> None: image.hflip() -@deprecate("Use the `image.rotate90(n)` method instead.", FutureWarning) +@deprecate("Use the `image.rotate90(n)` method instead.", category=FutureWarning) def image_rotate90(image: tcod.image.Image, num: int = 1) -> None: image.rotate90(num) -@deprecate("Use the `image.vflip()` method instead.", FutureWarning) +@deprecate("Use the `image.vflip()` method instead.", category=FutureWarning) def image_vflip(image: tcod.image.Image) -> None: image.vflip() -@deprecate("Use the `image.scale(new_width, new_height)` method instead.", FutureWarning) +@deprecate("Use the `image.scale(new_width, new_height)` method instead.", category=FutureWarning) def image_scale(image: tcod.image.Image, neww: int, newh: int) -> None: image.scale(neww, newh) -@deprecate("Use the `image.image_set_key_color(rgb)` method instead.", FutureWarning) +@deprecate("Use the `image.image_set_key_color(rgb)` method instead.", category=FutureWarning) def image_set_key_color(image: tcod.image.Image, col: tuple[int, int, int]) -> None: image.set_key_color(col) -@deprecate("Use `np.asarray(image)[y, x, 3]` instead.", FutureWarning) +@deprecate("Use `np.asarray(image)[y, x, 3]` instead.", category=FutureWarning) def image_get_alpha(image: tcod.image.Image, x: int, y: int) -> int: return image.get_alpha(x, y) -@deprecate("Use the Numpy array interface to check alpha or color keys.", FutureWarning) +@deprecate("Use the Numpy array interface to check alpha or color keys.", category=FutureWarning) def image_is_pixel_transparent(image: tcod.image.Image, x: int, y: int) -> bool: return bool(lib.TCOD_image_is_pixel_transparent(image.image_c, x, y)) @@ -3034,7 +3033,7 @@ def image_is_pixel_transparent(image: tcod.image.Image, x: int, y: int) -> bool: @deprecate( "Call the classmethod `tcod.image.Image.from_file` instead to load images." "\nIt's recommended to load images with a more complete image library such as python-Pillow or python-imageio.", - FutureWarning, + category=FutureWarning, ) def image_load(filename: str | PathLike[str]) -> tcod.image.Image: """Load an image file into an Image instance and return it. @@ -3051,7 +3050,7 @@ def image_load(filename: str | PathLike[str]) -> tcod.image.Image: return tcod.image.Image.from_file(filename) -@deprecate("Use `Tileset.render` instead of this function.", FutureWarning) +@deprecate("Use `Tileset.render` instead of this function.", category=FutureWarning) def image_from_console(console: tcod.console.Console) -> tcod.image.Image: """Return an Image with a Consoles pixel data. @@ -3071,7 +3070,7 @@ def image_from_console(console: tcod.console.Console) -> tcod.image.Image: ) -@deprecate("Use `Tileset.render` instead of this function.", FutureWarning) +@deprecate("Use `Tileset.render` instead of this function.", category=FutureWarning) def image_refresh_console(image: tcod.image.Image, console: tcod.console.Console) -> None: """Update an image made with :any:`image_from_console`. @@ -3081,27 +3080,27 @@ def image_refresh_console(image: tcod.image.Image, console: tcod.console.Console image.refresh_console(console) -@deprecate("Access an images size with `image.width` or `image.height`.", FutureWarning) +@deprecate("Access an images size with `image.width` or `image.height`.", category=FutureWarning) def image_get_size(image: tcod.image.Image) -> tuple[int, int]: return image.width, image.height -@deprecate("Use `np.asarray(image)[y, x, :3]` instead.", FutureWarning) +@deprecate("Use `np.asarray(image)[y, x, :3]` instead.", category=FutureWarning) def image_get_pixel(image: tcod.image.Image, x: int, y: int) -> tuple[int, int, int]: return image.get_pixel(x, y) -@deprecate("Use the `image.get_mipmap_pixel(...)` method instead.", FutureWarning) +@deprecate("Use the `image.get_mipmap_pixel(...)` method instead.", category=FutureWarning) def image_get_mipmap_pixel(image: tcod.image.Image, x0: float, y0: float, x1: float, y1: float) -> tuple[int, int, int]: return image.get_mipmap_pixel(x0, y0, x1, y1) -@deprecate("Use `np.asarray(image)[y, x, :3] = rgb` instead.", FutureWarning) +@deprecate("Use `np.asarray(image)[y, x, :3] = rgb` instead.", category=FutureWarning) def image_put_pixel(image: tcod.image.Image, x: int, y: int, col: tuple[int, int, int]) -> None: image.put_pixel(x, y, col) -@deprecate("Use the `image.blit(...)` method instead.", FutureWarning) +@deprecate("Use the `image.blit(...)` method instead.", category=FutureWarning) def image_blit( image: tcod.image.Image, console: tcod.console.Console, @@ -3115,7 +3114,7 @@ def image_blit( image.blit(console, x, y, bkgnd_flag, scalex, scaley, angle) -@deprecate("Use the `image.blit_rect(...)` method instead.", FutureWarning) +@deprecate("Use the `image.blit_rect(...)` method instead.", category=FutureWarning) def image_blit_rect( image: tcod.image.Image, console: tcod.console.Console, @@ -3128,7 +3127,7 @@ def image_blit_rect( image.blit_rect(console, x, y, w, h, bkgnd_flag) -@deprecate("Use `Console.draw_semigraphics(image, ...)` instead.", FutureWarning) +@deprecate("Use `Console.draw_semigraphics(image, ...)` instead.", category=FutureWarning) def image_blit_2x( image: tcod.image.Image, console: tcod.console.Console, @@ -3142,12 +3141,12 @@ def image_blit_2x( image.blit_2x(console, dx, dy, sx, sy, w, h) -@deprecate("Use the `image.save_as` method instead.", FutureWarning) +@deprecate("Use the `image.save_as` method instead.", category=FutureWarning) def image_save(image: tcod.image.Image, filename: str | PathLike[str]) -> None: image.save_as(filename) -@deprecate("libtcod objects are deleted automatically.", FutureWarning) +@deprecate("libtcod objects are deleted automatically.", category=FutureWarning) def image_delete(image: tcod.image.Image) -> None: """Does nothing. libtcod objects are managed by Python's garbage collector. @@ -3155,7 +3154,7 @@ def image_delete(image: tcod.image.Image) -> None: """ -@deprecate("Use tcod.los.bresenham instead.", FutureWarning) +@deprecate("Use tcod.los.bresenham instead.", category=FutureWarning) def line_init(xo: int, yo: int, xd: int, yd: int) -> None: """Initialize a line whose points will be returned by `line_step`. @@ -3175,7 +3174,7 @@ def line_init(xo: int, yo: int, xd: int, yd: int) -> None: lib.TCOD_line_init(xo, yo, xd, yd) -@deprecate("Use tcod.los.bresenham instead.", FutureWarning) +@deprecate("Use tcod.los.bresenham instead.", category=FutureWarning) def line_step() -> tuple[int, int] | tuple[None, None]: """After calling line_init returns (x, y) points of the line. @@ -3197,7 +3196,7 @@ def line_step() -> tuple[int, int] | tuple[None, None]: return None, None -@deprecate("Use tcod.los.bresenham instead.", FutureWarning) +@deprecate("Use tcod.los.bresenham instead.", category=FutureWarning) def line(xo: int, yo: int, xd: int, yd: int, py_callback: Callable[[int, int], bool]) -> bool: """Iterate over a line using a callback function. @@ -3229,7 +3228,7 @@ def line(xo: int, yo: int, xd: int, yd: int, py_callback: Callable[[int, int], b return False -@deprecate("This function has been replaced by tcod.los.bresenham.", FutureWarning) +@deprecate("This function has been replaced by tcod.los.bresenham.", category=FutureWarning) def line_iter(xo: int, yo: int, xd: int, yd: int) -> Iterator[tuple[int, int]]: """Returns an Iterable over a Bresenham line. @@ -3256,8 +3255,8 @@ def line_iter(xo: int, yo: int, xd: int, yd: int) -> Iterator[tuple[int, int]]: yield (x[0], y[0]) -@deprecate("This function has been replaced by tcod.los.bresenham.", FutureWarning) -def line_where(x1: int, y1: int, x2: int, y2: int, inclusive: bool = True) -> tuple[NDArray[np.intc], NDArray[np.intc]]: +@deprecate("This function has been replaced by tcod.los.bresenham.", category=FutureWarning) +def line_where(x1: int, y1: int, x2: int, y2: int, inclusive: bool = True) -> tuple[NDArray[np.intc], NDArray[np.intc]]: # noqa: FBT001, FBT002 """Return a NumPy index array following a Bresenham line. If `inclusive` is true then the start point is included in the result. @@ -3274,7 +3273,7 @@ def line_where(x1: int, y1: int, x2: int, y2: int, inclusive: bool = True) -> tu return i, j -@deprecate("Call tcod.map.Map(width, height) instead.", FutureWarning) +@deprecate("Call tcod.map.Map(width, height) instead.", category=FutureWarning) def map_new(w: int, h: int) -> tcod.map.Map: """Return a :any:`tcod.map.Map` with a width and height. @@ -3285,7 +3284,7 @@ def map_new(w: int, h: int) -> tcod.map.Map: return tcod.map.Map(w, h) -@deprecate("Use Python's standard copy module instead.", FutureWarning) +@deprecate("Use Python's standard copy module instead.", category=FutureWarning) def map_copy(source: tcod.map.Map, dest: tcod.map.Map) -> None: """Copy map data from `source` to `dest`. @@ -3294,12 +3293,12 @@ def map_copy(source: tcod.map.Map, dest: tcod.map.Map) -> None: array attributes manually. """ if source.width != dest.width or source.height != dest.height: - dest.__init__(source.width, source.height, source._order) # type: ignore - dest._Map__buffer[:] = source._Map__buffer[:] # type: ignore + tcod.map.Map.__init__(dest, source.width, source.height, source._order) + dest._buffer[:] = source._buffer[:] -@deprecate("Set properties using the m.transparent and m.walkable arrays.", FutureWarning) -def map_set_properties(m: tcod.map.Map, x: int, y: int, isTrans: bool, isWalk: bool) -> None: +@deprecate("Set properties using the m.transparent and m.walkable arrays.", category=FutureWarning) +def map_set_properties(m: tcod.map.Map, x: int, y: int, isTrans: bool, isWalk: bool) -> None: # noqa: FBT001, N803 """Set the properties of a single cell. .. note:: @@ -3311,8 +3310,8 @@ def map_set_properties(m: tcod.map.Map, x: int, y: int, isTrans: bool, isWalk: b lib.TCOD_map_set_properties(m.map_c, x, y, isTrans, isWalk) -@deprecate("Clear maps using NumPy broadcast rules instead.", FutureWarning) -def map_clear(m: tcod.map.Map, transparent: bool = False, walkable: bool = False) -> None: +@deprecate("Clear maps using NumPy broadcast rules instead.", category=FutureWarning) +def map_clear(m: tcod.map.Map, transparent: bool = False, walkable: bool = False) -> None: # noqa: FBT001, FBT002 """Change all map cells to a specific value. .. deprecated:: 4.5 @@ -3323,13 +3322,13 @@ def map_clear(m: tcod.map.Map, transparent: bool = False, walkable: bool = False m.walkable[:] = walkable -@deprecate("Call the map.compute_fov method instead.", FutureWarning) +@deprecate("Call the map.compute_fov method instead.", category=FutureWarning) def map_compute_fov( m: tcod.map.Map, x: int, y: int, radius: int = 0, - light_walls: bool = True, + light_walls: bool = True, # noqa: FBT001, FBT002 algo: int = FOV_RESTRICTIVE, ) -> None: """Compute the field-of-view for a map instance. @@ -3340,7 +3339,7 @@ def map_compute_fov( m.compute_fov(x, y, radius, light_walls, algo) -@deprecate("Use map.fov to check for this property.", FutureWarning) +@deprecate("Use map.fov to check for this property.", category=FutureWarning) def map_is_in_fov(m: tcod.map.Map, x: int, y: int) -> bool: """Return True if the cell at x,y is lit by the last field-of-view algorithm. @@ -3352,7 +3351,7 @@ def map_is_in_fov(m: tcod.map.Map, x: int, y: int) -> bool: return bool(lib.TCOD_map_is_in_fov(m.map_c, x, y)) -@deprecate("Use map.transparent to check for this property.", FutureWarning) +@deprecate("Use map.transparent to check for this property.", category=FutureWarning) def map_is_transparent(m: tcod.map.Map, x: int, y: int) -> bool: """Return True is a map cell is transparent. @@ -3364,7 +3363,7 @@ def map_is_transparent(m: tcod.map.Map, x: int, y: int) -> bool: return bool(lib.TCOD_map_is_transparent(m.map_c, x, y)) -@deprecate("Use map.walkable to check for this property.", FutureWarning) +@deprecate("Use map.walkable to check for this property.", category=FutureWarning) def map_is_walkable(m: tcod.map.Map, x: int, y: int) -> bool: """Return True is a map cell is walkable. @@ -3376,7 +3375,7 @@ def map_is_walkable(m: tcod.map.Map, x: int, y: int) -> bool: return bool(lib.TCOD_map_is_walkable(m.map_c, x, y)) -@deprecate("libtcod objects are deleted automatically.", FutureWarning) +@deprecate("libtcod objects are deleted automatically.", category=FutureWarning) def map_delete(m: tcod.map.Map) -> None: """Does nothing. libtcod objects are managed by Python's garbage collector. @@ -3384,8 +3383,8 @@ def map_delete(m: tcod.map.Map) -> None: """ -@deprecate("Check the map.width attribute instead.", FutureWarning) -def map_get_width(map: tcod.map.Map) -> int: +@deprecate("Check the map.width attribute instead.", category=FutureWarning) +def map_get_width(map: tcod.map.Map) -> int: # noqa: A002 """Return the width of a map. .. deprecated:: 4.5 @@ -3394,8 +3393,8 @@ def map_get_width(map: tcod.map.Map) -> int: return map.width -@deprecate("Check the map.height attribute instead.", FutureWarning) -def map_get_height(map: tcod.map.Map) -> int: +@deprecate("Check the map.height attribute instead.", category=FutureWarning) +def map_get_height(map: tcod.map.Map) -> int: # noqa: A002 """Return the height of a map. .. deprecated:: 4.5 @@ -3404,8 +3403,8 @@ def map_get_height(map: tcod.map.Map) -> int: return map.height -@deprecate("Use `tcod.sdl.mouse.show(visible)` instead.", FutureWarning) -def mouse_show_cursor(visible: bool) -> None: +@deprecate("Use `tcod.sdl.mouse.show(visible)` instead.", category=FutureWarning) +def mouse_show_cursor(visible: bool) -> None: # noqa: FBT001 """Change the visibility of the mouse cursor. .. deprecated:: 16.0 @@ -3414,7 +3413,7 @@ def mouse_show_cursor(visible: bool) -> None: lib.TCOD_mouse_show_cursor(visible) -@deprecate("Use `is_visible = tcod.sdl.mouse.show()` instead.", FutureWarning) +@deprecate("Use `is_visible = tcod.sdl.mouse.show()` instead.", category=FutureWarning) def mouse_is_cursor_visible() -> bool: """Return True if the mouse cursor is visible. @@ -3424,32 +3423,32 @@ def mouse_is_cursor_visible() -> bool: return bool(lib.TCOD_mouse_is_cursor_visible()) -@deprecate("Use `tcod.sdl.mouse.warp_in_window` instead.", FutureWarning) +@deprecate("Use `tcod.sdl.mouse.warp_in_window` instead.", category=FutureWarning) def mouse_move(x: int, y: int) -> None: lib.TCOD_mouse_move(x, y) -@deprecate("Use tcod.event.get_mouse_state() instead.", FutureWarning) +@deprecate("Use tcod.event.get_mouse_state() instead.", category=FutureWarning) def mouse_get_status() -> Mouse: return Mouse(lib.TCOD_mouse_get_status()) -@pending_deprecate() +@deprecate(_PENDING_DEPRECATE_MSG, category=PendingDeprecationWarning) def namegen_parse(filename: str | PathLike[str], random: tcod.random.Random | None = None) -> None: - lib.TCOD_namegen_parse(bytes(Path(filename)), random or ffi.NULL) + lib.TCOD_namegen_parse(_path_encode(Path(filename).resolve(strict=True)), random or ffi.NULL) -@pending_deprecate() +@deprecate(_PENDING_DEPRECATE_MSG, category=PendingDeprecationWarning) def namegen_generate(name: str) -> str: - return _unpack_char_p(lib.TCOD_namegen_generate(_bytes(name), False)) + return _unpack_char_p(lib.TCOD_namegen_generate(_bytes(name), False)) # noqa: FBT003 -@pending_deprecate() +@deprecate(_PENDING_DEPRECATE_MSG, category=PendingDeprecationWarning) def namegen_generate_custom(name: str, rule: str) -> str: - return _unpack_char_p(lib.TCOD_namegen_generate_custom(_bytes(name), _bytes(rule), False)) + return _unpack_char_p(lib.TCOD_namegen_generate_custom(_bytes(name), _bytes(rule), False)) # noqa: FBT003 -@pending_deprecate() +@deprecate(_PENDING_DEPRECATE_MSG, category=PendingDeprecationWarning) def namegen_get_sets() -> list[str]: sets = lib.TCOD_namegen_get_sets() try: @@ -3461,12 +3460,12 @@ def namegen_get_sets() -> list[str]: return lst -@pending_deprecate() +@deprecate(_PENDING_DEPRECATE_MSG, category=PendingDeprecationWarning) def namegen_destroy() -> None: lib.TCOD_namegen_destroy() -@deprecate("Use `tcod.noise.Noise(dimensions, hurst=, lacunarity=)` instead.", FutureWarning) +@deprecate("Use `tcod.noise.Noise(dimensions, hurst=, lacunarity=)` instead.", category=FutureWarning) def noise_new( dim: int, h: float = NOISE_DEFAULT_HURST, @@ -3487,17 +3486,18 @@ def noise_new( return tcod.noise.Noise(dim, hurst=h, lacunarity=l, seed=random) -@deprecate("Use `noise.algorithm = x` instead.", FutureWarning) +@deprecate("Use `noise.algorithm = x` instead.", category=FutureWarning) def noise_set_type(n: tcod.noise.Noise, typ: int) -> None: """Set a Noise objects default noise algorithm. Args: + n: Noise object. typ (int): Any NOISE_* constant. """ n.algorithm = typ -@deprecate("Use `value = noise[x]` instead.", FutureWarning) +@deprecate("Use `value = noise[x]` instead.", category=FutureWarning) def noise_get(n: tcod.noise.Noise, f: Sequence[float], typ: int = NOISE_DEFAULT) -> float: """Return the noise value sampled from the ``f`` coordinate. @@ -3517,7 +3517,7 @@ def noise_get(n: tcod.noise.Noise, f: Sequence[float], typ: int = NOISE_DEFAULT) return float(lib.TCOD_noise_get_ex(n.noise_c, ffi.new("float[4]", f), typ)) -@deprecate("Configure a Noise instance for FBM and then sample it like normal.", FutureWarning) +@deprecate("Configure a Noise instance for FBM and then sample it like normal.", category=FutureWarning) def noise_get_fbm( n: tcod.noise.Noise, f: Sequence[float], @@ -3530,7 +3530,7 @@ def noise_get_fbm( n (Noise): A Noise instance. f (Sequence[float]): The point to sample the noise from. typ (int): The noise algorithm to use. - octaves (float): The level of level. Should be more than 1. + oc (float): The level of level. Should be more than 1. Returns: float: The sampled noise value. @@ -3538,7 +3538,7 @@ def noise_get_fbm( return float(lib.TCOD_noise_get_fbm_ex(n.noise_c, ffi.new("float[4]", f), oc, typ)) -@deprecate("Configure a Noise instance for FBM and then sample it like normal.", FutureWarning) +@deprecate("Configure a Noise instance for FBM and then sample it like normal.", category=FutureWarning) def noise_get_turbulence( n: tcod.noise.Noise, f: Sequence[float], @@ -3551,7 +3551,7 @@ def noise_get_turbulence( n (Noise): A Noise instance. f (Sequence[float]): The point to sample the noise from. typ (int): The noise algorithm to use. - octaves (float): The level of level. Should be more than 1. + oc (float): The level of level. Should be more than 1. Returns: float: The sampled noise value. @@ -3559,7 +3559,7 @@ def noise_get_turbulence( return float(lib.TCOD_noise_get_turbulence_ex(n.noise_c, ffi.new("float[4]", f), oc, typ)) -@deprecate("libtcod objects are deleted automatically.", FutureWarning) +@deprecate("libtcod objects are deleted automatically.", category=FutureWarning) def noise_delete(n: tcod.noise.Noise) -> None: # type (Any) -> None """Does nothing. libtcod objects are managed by Python's garbage collector. @@ -3568,30 +3568,33 @@ def noise_delete(n: tcod.noise.Noise) -> None: """ -def _unpack_union(type_: int, union: Any) -> Any: +def _unpack_union(type_: int, union: Any) -> Any: # noqa: PLR0911 """Unpack items from parser new_property (value_converter).""" if type_ == lib.TCOD_TYPE_BOOL: return bool(union.b) - elif type_ == lib.TCOD_TYPE_CHAR: + if type_ == lib.TCOD_TYPE_CHAR: return union.c.decode("latin-1") - elif type_ == lib.TCOD_TYPE_INT: + if type_ == lib.TCOD_TYPE_INT: return union.i - elif type_ == lib.TCOD_TYPE_FLOAT: + if type_ == lib.TCOD_TYPE_FLOAT: return union.f - elif type_ == lib.TCOD_TYPE_STRING or lib.TCOD_TYPE_VALUELIST15 >= type_ >= lib.TCOD_TYPE_VALUELIST00: + if type_ == lib.TCOD_TYPE_STRING or lib.TCOD_TYPE_VALUELIST15 >= type_ >= lib.TCOD_TYPE_VALUELIST00: return _unpack_char_p(union.s) - elif type_ == lib.TCOD_TYPE_COLOR: + if type_ == lib.TCOD_TYPE_COLOR: return Color._new_from_cdata(union.col) - elif type_ == lib.TCOD_TYPE_DICE: + if type_ == lib.TCOD_TYPE_DICE: return Dice(union.dice) - elif type_ & lib.TCOD_TYPE_LIST: + if type_ & lib.TCOD_TYPE_LIST: return _convert_TCODList(union.list, type_ & 0xFF) - else: - raise RuntimeError("Unknown libtcod type: %i" % type_) + msg = f"Unknown libtcod type: {type_}" + raise RuntimeError(msg) -def _convert_TCODList(c_list: Any, type_: int) -> Any: - return [_unpack_union(type_, lib.TDL_list_get_union(c_list, i)) for i in range(lib.TCOD_list_size(c_list))] +def _convert_TCODList(c_list: Any, type_: int) -> Any: # noqa: N802 + with ffi.new("TCOD_value_t[]", lib.TCOD_list_size(c_list)) as unions: + for i, union in enumerate(unions): + union.custom = lib.TCOD_list_get(c_list, i) + return [_unpack_union(type_, union) for union in unions] @deprecate("Parser functions have been deprecated.") @@ -3610,36 +3613,37 @@ def parser_new_struct(parser: Any, name: str) -> Any: _parser_listener: Any = None -@ffi.def_extern() # type: ignore +@ffi.def_extern() # type: ignore[untyped-decorator] def _pycall_parser_new_struct(struct: Any, name: str) -> Any: return _parser_listener.new_struct(struct, _unpack_char_p(name)) -@ffi.def_extern() # type: ignore +@ffi.def_extern() # type: ignore[untyped-decorator] def _pycall_parser_new_flag(name: str) -> Any: return _parser_listener.new_flag(_unpack_char_p(name)) -@ffi.def_extern() # type: ignore -def _pycall_parser_new_property(propname: Any, type: Any, value: Any) -> Any: +@ffi.def_extern() # type: ignore[untyped-decorator] +def _pycall_parser_new_property(propname: Any, type: Any, value: Any) -> Any: # noqa: A002 return _parser_listener.new_property(_unpack_char_p(propname), type, _unpack_union(type, value)) -@ffi.def_extern() # type: ignore +@ffi.def_extern() # type: ignore[untyped-decorator] def _pycall_parser_end_struct(struct: Any, name: Any) -> Any: return _parser_listener.end_struct(struct, _unpack_char_p(name)) -@ffi.def_extern() # type: ignore +@ffi.def_extern() # type: ignore[untyped-decorator] def _pycall_parser_error(msg: Any) -> None: _parser_listener.error(_unpack_char_p(msg)) @deprecate("Parser functions have been deprecated.") def parser_run(parser: Any, filename: str | PathLike[str], listener: Any = None) -> None: - global _parser_listener + global _parser_listener # noqa: PLW0603 + filename = Path(filename).resolve(strict=True) if not listener: - lib.TCOD_parser_run(parser, bytes(Path(filename)), ffi.NULL) + lib.TCOD_parser_run(parser, _path_encode(filename), ffi.NULL) return propagate_manager = _PropagateException() @@ -3658,7 +3662,7 @@ def parser_run(parser: Any, filename: str | PathLike[str], listener: Any = None) with _parser_callback_lock: _parser_listener = listener with propagate_manager: - lib.TCOD_parser_run(parser, bytes(Path(filename)), c_listener) + lib.TCOD_parser_run(parser, _path_encode(filename), c_listener) @deprecate("libtcod objects are deleted automatically.") @@ -3708,7 +3712,7 @@ def parser_get_dice_property(parser: Any, name: str) -> Dice: @deprecate("Parser functions have been deprecated.") -def parser_get_list_property(parser: Any, name: str, type: Any) -> Any: +def parser_get_list_property(parser: Any, name: str, type: Any) -> Any: # noqa: A002 c_list = lib.TCOD_parser_get_list_property(parser, _bytes(name), type) return _convert_TCODList(c_list, type) @@ -3723,7 +3727,7 @@ def parser_get_list_property(parser: Any, name: str, type: Any) -> Any: DISTRIBUTION_GAUSSIAN_RANGE_INVERSE = 4 -@pending_deprecate() +@deprecate(_PENDING_DEPRECATE_MSG, category=PendingDeprecationWarning) def random_get_instance() -> tcod.random.Random: """Return the default Random instance. @@ -3733,7 +3737,7 @@ def random_get_instance() -> tcod.random.Random: return tcod.random.Random._new_from_cdata(lib.TCOD_random_get_instance()) -@pending_deprecate() +@deprecate(_PENDING_DEPRECATE_MSG, category=PendingDeprecationWarning) def random_new(algo: int = RNG_CMWC) -> tcod.random.Random: """Return a new Random instance. Using ``algo``. @@ -3746,7 +3750,7 @@ def random_new(algo: int = RNG_CMWC) -> tcod.random.Random: return tcod.random.Random(algo) -@pending_deprecate() +@deprecate(_PENDING_DEPRECATE_MSG, category=PendingDeprecationWarning) def random_new_from_seed(seed: Hashable, algo: int = RNG_CMWC) -> tcod.random.Random: """Return a new Random instance. Using the given ``seed`` and ``algo``. @@ -3761,7 +3765,7 @@ def random_new_from_seed(seed: Hashable, algo: int = RNG_CMWC) -> tcod.random.Ra return tcod.random.Random(algo, seed) -@pending_deprecate() +@deprecate(_PENDING_DEPRECATE_MSG, category=PendingDeprecationWarning) def random_set_distribution(rnd: tcod.random.Random | None, dist: int) -> None: """Change the distribution mode of a random number generator. @@ -3772,7 +3776,7 @@ def random_set_distribution(rnd: tcod.random.Random | None, dist: int) -> None: lib.TCOD_random_set_distribution(rnd.random_c if rnd else ffi.NULL, dist) -@pending_deprecate() +@deprecate(_PENDING_DEPRECATE_MSG, category=PendingDeprecationWarning) def random_get_int(rnd: tcod.random.Random | None, mi: int, ma: int) -> int: """Return a random integer in the range: ``mi`` <= n <= ``ma``. @@ -3780,8 +3784,8 @@ def random_get_int(rnd: tcod.random.Random | None, mi: int, ma: int) -> int: Args: rnd (Optional[Random]): A Random instance, or None to use the default. - low (int): The lower bound of the random range, inclusive. - high (int): The upper bound of the random range, inclusive. + mi (int): The lower bound of the random range, inclusive. + ma (int): The upper bound of the random range, inclusive. Returns: int: A random integer in the range ``mi`` <= n <= ``ma``. @@ -3789,7 +3793,7 @@ def random_get_int(rnd: tcod.random.Random | None, mi: int, ma: int) -> int: return int(lib.TCOD_random_get_int(rnd.random_c if rnd else ffi.NULL, mi, ma)) -@pending_deprecate() +@deprecate(_PENDING_DEPRECATE_MSG, category=PendingDeprecationWarning) def random_get_float(rnd: tcod.random.Random | None, mi: float, ma: float) -> float: """Return a random float in the range: ``mi`` <= n <= ``ma``. @@ -3797,8 +3801,8 @@ def random_get_float(rnd: tcod.random.Random | None, mi: float, ma: float) -> fl Args: rnd (Optional[Random]): A Random instance, or None to use the default. - low (float): The lower bound of the random range, inclusive. - high (float): The upper bound of the random range, inclusive. + mi (float): The lower bound of the random range, inclusive. + ma (float): The upper bound of the random range, inclusive. Returns: float: A random double precision float @@ -3818,7 +3822,7 @@ def random_get_double(rnd: tcod.random.Random | None, mi: float, ma: float) -> f return float(lib.TCOD_random_get_double(rnd.random_c if rnd else ffi.NULL, mi, ma)) -@pending_deprecate() +@deprecate(_PENDING_DEPRECATE_MSG, category=PendingDeprecationWarning) def random_get_int_mean(rnd: tcod.random.Random | None, mi: int, ma: int, mean: int) -> int: """Return a random weighted integer in the range: ``mi`` <= n <= ``ma``. @@ -3826,8 +3830,8 @@ def random_get_int_mean(rnd: tcod.random.Random | None, mi: int, ma: int, mean: Args: rnd (Optional[Random]): A Random instance, or None to use the default. - low (int): The lower bound of the random range, inclusive. - high (int): The upper bound of the random range, inclusive. + mi (int): The lower bound of the random range, inclusive. + ma (int): The upper bound of the random range, inclusive. mean (int): The mean return value. Returns: @@ -3836,7 +3840,7 @@ def random_get_int_mean(rnd: tcod.random.Random | None, mi: int, ma: int, mean: return int(lib.TCOD_random_get_int_mean(rnd.random_c if rnd else ffi.NULL, mi, ma, mean)) -@pending_deprecate() +@deprecate(_PENDING_DEPRECATE_MSG, category=PendingDeprecationWarning) def random_get_float_mean(rnd: tcod.random.Random | None, mi: float, ma: float, mean: float) -> float: """Return a random weighted float in the range: ``mi`` <= n <= ``ma``. @@ -3844,8 +3848,8 @@ def random_get_float_mean(rnd: tcod.random.Random | None, mi: float, ma: float, Args: rnd (Optional[Random]): A Random instance, or None to use the default. - low (float): The lower bound of the random range, inclusive. - high (float): The upper bound of the random range, inclusive. + mi (float): The lower bound of the random range, inclusive. + ma (float): The upper bound of the random range, inclusive. mean (float): The mean return value. Returns: @@ -3911,19 +3915,19 @@ def struct_add_flag(struct: Any, name: str) -> None: @deprecate("This function is deprecated.") -def struct_add_property(struct: Any, name: str, typ: int, mandatory: bool) -> None: +def struct_add_property(struct: Any, name: str, typ: int, mandatory: bool) -> None: # noqa: FBT001 lib.TCOD_struct_add_property(struct, _bytes(name), typ, mandatory) @deprecate("This function is deprecated.") -def struct_add_value_list(struct: Any, name: str, value_list: Iterable[str], mandatory: bool) -> None: +def struct_add_value_list(struct: Any, name: str, value_list: Iterable[str], mandatory: bool) -> None: # noqa: FBT001 c_strings = [ffi.new("char[]", value.encode("utf-8")) for value in value_list] c_value_list = ffi.new("char*[]", c_strings) lib.TCOD_struct_add_value_list(struct, name, c_value_list, mandatory) @deprecate("This function is deprecated.") -def struct_add_list_property(struct: Any, name: str, typ: int, mandatory: bool) -> None: +def struct_add_list_property(struct: Any, name: str, typ: int, mandatory: bool) -> None: # noqa: FBT001 lib.TCOD_struct_add_list_property(struct, _bytes(name), typ, mandatory) @@ -4070,7 +4074,7 @@ def sys_save_screenshot(name: str | PathLike[str] | None = None) -> None: screenshot000.png, screenshot001.png, etc. Whichever is available first. Args: - file Optional[AnyStr]: File path to save screenshot. + name: File path to save screenshot. .. deprecated:: 11.13 This function is not supported by contexts. @@ -4079,7 +4083,7 @@ def sys_save_screenshot(name: str | PathLike[str] | None = None) -> None: .. versionchanged:: 16.0 Added PathLike support. """ - lib.TCOD_sys_save_screenshot(bytes(Path(name)) if name is not None else ffi.NULL) + lib.TCOD_sys_save_screenshot(_path_encode(Path(name)) if name is not None else ffi.NULL) # custom fullscreen resolution @@ -4136,7 +4140,7 @@ def sys_get_char_size() -> tuple[int, int]: # update font bitmap @deprecate("This function is not supported if contexts are being used.") def sys_update_char( - asciiCode: int, + asciiCode: int, # noqa: N803 fontx: int, fonty: int, img: tcod.image.Image, @@ -4166,7 +4170,7 @@ def sys_update_char( @deprecate("This function is not supported if contexts are being used.") -def sys_register_SDL_renderer(callback: Callable[[Any], None]) -> None: +def sys_register_SDL_renderer(callback: Callable[[Any], None]) -> None: # noqa: N802 """Register a custom rendering function with libtcod. Note: @@ -4178,15 +4182,14 @@ def sys_register_SDL_renderer(callback: Callable[[Any], None]) -> None: The callback is called on every call to :any:`libtcodpy.console_flush`. Args: - callback Callable[[CData], None]: - A function which takes a single argument. + callback: A function which takes a single argument. .. deprecated:: 11.13 This function is not supported by contexts. """ with _PropagateException() as propagate: - @ffi.def_extern(onerror=propagate) # type: ignore + @ffi.def_extern(onerror=propagate) # type: ignore[untyped-decorator] def _pycall_sdl_hook(sdl_surface: Any) -> None: callback(sdl_surface) @@ -4211,7 +4214,7 @@ def sys_check_for_event(mask: int, k: Key | None, m: Mouse | None) -> int: @deprecate("Use tcod.event.wait to wait for events.") -def sys_wait_for_event(mask: int, k: Key | None, m: Mouse | None, flush: bool) -> int: +def sys_wait_for_event(mask: int, k: Key | None, m: Mouse | None, flush: bool) -> int: # noqa: FBT001 """Wait for an event then return. If flush is True then the buffer will be cleared before waiting. Otherwise @@ -4288,7 +4291,7 @@ def __getattr__(name: str) -> Color: raise AttributeError(msg) from None -__all__ = [ # noqa: F405 +__all__ = [ # noqa: F405 RUF022 "Color", "Bsp", "NB_FOV_ALGORITHMS", diff --git a/tcod/los.py b/tcod/los.py index d6cd1362..f09b073c 100644 --- a/tcod/los.py +++ b/tcod/los.py @@ -1,13 +1,16 @@ """This modules holds functions for NumPy-based line of sight algorithms.""" + from __future__ import annotations -from typing import Any +from typing import TYPE_CHECKING, Any import numpy as np -from numpy.typing import NDArray from tcod.cffi import ffi, lib +if TYPE_CHECKING: + from numpy.typing import NDArray + def bresenham(start: tuple[int, int], end: tuple[int, int]) -> NDArray[np.intc]: """Return a thin Bresenham line as a NumPy array of shape (length, 2). diff --git a/tcod/map.py b/tcod/map.py index 820c981d..e1f4403b 100644 --- a/tcod/map.py +++ b/tcod/map.py @@ -1,18 +1,22 @@ """libtcod map attributes and field-of-view functions.""" + from __future__ import annotations import warnings -from typing import Any +from typing import TYPE_CHECKING, Any, Final, Literal import numpy as np -from numpy.typing import ArrayLike, NDArray -from typing_extensions import Literal +from typing_extensions import deprecated import tcod._internal import tcod.constants from tcod.cffi import ffi, lib +if TYPE_CHECKING: + from numpy.typing import ArrayLike, NDArray + +@deprecated("This class may perform poorly and is no longer needed.") class Map: """A map containing libtcod attributes. @@ -27,13 +31,6 @@ class Map: height (int): Height of the new Map. order (str): Which numpy memory order to use. - Attributes: - width (int): Read only width of this Map. - height (int): Read only height of this Map. - transparent: A boolean array of transparent cells. - walkable: A boolean array of walkable cells. - fov: A boolean array of the cells lit by :any:'compute_fov'. - Example:: >>> import tcod @@ -60,7 +57,7 @@ class Map: [ True, True, True], [False, True, True], [False, False, True]]...) - >>> m.fov[3,1] + >>> m.fov.item(3, 1) False .. deprecated:: 11.13 @@ -75,42 +72,43 @@ def __init__( height: int, order: Literal["C", "F"] = "C", ) -> None: - warnings.warn( - "This class may perform poorly and is no longer needed.", - DeprecationWarning, - stacklevel=2, - ) - self.width = width - self.height = height - self._order = tcod._internal.verify_order(order) - - self.__buffer: NDArray[np.bool_] = np.zeros((height, width, 3), dtype=np.bool_) + """Initialize the map.""" + self.width: Final = width + """Read only width of this Map.""" + self.height: Final = height + """Read only height of this Map.""" + self._order: Literal["C", "F"] = tcod._internal.verify_order(order) + + self._buffer: NDArray[np.bool_] = np.zeros((height, width, 3), dtype=np.bool_) self.map_c = self.__as_cdata() - def __as_cdata(self) -> Any: + def __as_cdata(self) -> Any: # noqa: ANN401 return ffi.new( "struct TCOD_Map*", ( self.width, self.height, self.width * self.height, - ffi.from_buffer("struct TCOD_MapCell*", self.__buffer), + ffi.from_buffer("struct TCOD_MapCell*", self._buffer), ), ) @property def transparent(self) -> NDArray[np.bool_]: - buffer: np.ndarray[Any, np.dtype[np.bool_]] = self.__buffer[:, :, 0] + """A boolean array of transparent cells.""" + buffer: np.ndarray[Any, np.dtype[np.bool_]] = self._buffer[:, :, 0] return buffer.T if self._order == "F" else buffer @property def walkable(self) -> NDArray[np.bool_]: - buffer: np.ndarray[Any, np.dtype[np.bool_]] = self.__buffer[:, :, 1] + """A boolean array of walkable cells.""" + buffer: np.ndarray[Any, np.dtype[np.bool_]] = self._buffer[:, :, 1] return buffer.T if self._order == "F" else buffer @property def fov(self) -> NDArray[np.bool_]: - buffer: np.ndarray[Any, np.dtype[np.bool_]] = self.__buffer[:, :, 2] + """A boolean array of the cells lit by :any:'compute_fov'.""" + buffer: np.ndarray[Any, np.dtype[np.bool_]] = self._buffer[:, :, 2] return buffer.T if self._order == "F" else buffer def compute_fov( @@ -118,7 +116,7 @@ def compute_fov( x: int, y: int, radius: int = 0, - light_walls: bool = True, + light_walls: bool = True, # noqa: FBT001, FBT002 algorithm: int = tcod.constants.FOV_RESTRICTIVE, ) -> None: """Compute a field-of-view on the current instance. @@ -137,8 +135,8 @@ def compute_fov( """ if not (0 <= x < self.width and 0 <= y < self.height): warnings.warn( - "Index ({}, {}) is outside of this maps shape ({}, {})." - "\nThis will raise an error in future versions.".format(x, y, self.width, self.height), + f"Index ({x}, {y}) is outside of this maps shape ({self.width}, {self.height})." + "\nThis will raise an error in future versions.", RuntimeWarning, stacklevel=2, ) @@ -146,18 +144,21 @@ def compute_fov( lib.TCOD_map_compute_fov(self.map_c, x, y, radius, light_walls, algorithm) def __setstate__(self, state: dict[str, Any]) -> None: - if "_Map__buffer" not in state: # deprecated - # remove this check on major version update - self.__buffer = np.zeros((state["height"], state["width"], 3), dtype=np.bool_) - self.__buffer[:, :, 0] = state["buffer"] & 0x01 - self.__buffer[:, :, 1] = state["buffer"] & 0x02 - self.__buffer[:, :, 2] = state["buffer"] & 0x04 + """Unpickle this instance.""" + if "_Map__buffer" in state: # Deprecated since 19.6 + state["_buffer"] = state.pop("_Map__buffer") + if "buffer" in state: # Deprecated + self._buffer = np.zeros((state["height"], state["width"], 3), dtype=np.bool_) + self._buffer[:, :, 0] = state["buffer"] & 0x01 + self._buffer[:, :, 1] = state["buffer"] & 0x02 + self._buffer[:, :, 2] = state["buffer"] & 0x04 del state["buffer"] state["_order"] = "F" self.__dict__.update(state) self.map_c = self.__as_cdata() def __getstate__(self) -> dict[str, Any]: + """Pickle this instance.""" state = self.__dict__.copy() del state["map_c"] return state @@ -167,7 +168,7 @@ def compute_fov( transparency: ArrayLike, pov: tuple[int, int], radius: int = 0, - light_walls: bool = True, + light_walls: bool = True, # noqa: FBT001, FBT002 algorithm: int = tcod.constants.FOV_RESTRICTIVE, ) -> NDArray[np.bool_]: """Return a boolean mask of the area covered by a field-of-view. @@ -232,15 +233,16 @@ def compute_fov( conditions. """ transparency = np.asarray(transparency) - if len(transparency.shape) != 2: - raise TypeError("transparency must be an array of 2 dimensions" " (shape is %r)" % transparency.shape) + if len(transparency.shape) != 2: # noqa: PLR2004 + msg = f"transparency must be an array of 2 dimensions (shape is {transparency.shape!r})" + raise TypeError(msg) if isinstance(pov, int): msg = "The tcod.map.compute_fov function has changed. The `x` and `y` parameters should now be given as a single tuple." raise TypeError(msg) if not (0 <= pov[0] < transparency.shape[0] and 0 <= pov[1] < transparency.shape[1]): warnings.warn( - "Given pov index {!r} is outside the array of shape {!r}." - "\nThis will raise an error in future versions.".format(pov, transparency.shape), + f"Given pov index {pov!r} is outside the array of shape {transparency.shape!r}." + "\nThis will raise an error in future versions.", RuntimeWarning, stacklevel=2, ) @@ -257,6 +259,6 @@ def compute_fov( ffi.from_buffer("struct TCOD_MapCell*", map_buffer), ), ) - map_buffer["transparent"] = transparency + map_buffer["transparent"] = transparency # type: ignore[call-overload] lib.TCOD_map_compute_fov(map_cdata, pov[1], pov[0], radius, light_walls, algorithm) - return map_buffer["fov"] # type: ignore + return map_buffer["fov"] # type: ignore[no-any-return,call-overload] diff --git a/tcod/noise.py b/tcod/noise.py index 92b7636e..161dfc26 100644 --- a/tcod/noise.py +++ b/tcod/noise.py @@ -12,7 +12,7 @@ ... algorithm=tcod.noise.Algorithm.SIMPLEX, ... seed=42, ... ) - >>> samples = noise[tcod.noise.grid(shape=(5, 4), scale=0.25, origin=(0, 0))] + >>> samples = noise[tcod.noise.grid(shape=(5, 4), scale=0.25, offset=(0, 0))] >>> samples # Samples are a grid of floats between -1.0 and 1.0 array([[ 0. , -0.55046356, -0.76072866, -0.7088647 , -0.68165785], [-0.27523372, -0.7205134 , -0.74057037, -0.43919194, -0.29195625], @@ -31,20 +31,24 @@ [ 76, 54, 85, 144, 164], [ 63, 94, 159, 209, 203]], dtype=uint8) """ + from __future__ import annotations import enum import warnings -from typing import Any, Sequence +from typing import TYPE_CHECKING, Any, Literal import numpy as np -from numpy.typing import ArrayLike, NDArray -from typing_extensions import Literal import tcod.constants import tcod.random from tcod.cffi import ffi, lib +if TYPE_CHECKING: + from collections.abc import Sequence + + from numpy.typing import ArrayLike, NDArray + class Algorithm(enum.IntEnum): """Libtcod noise algorithms. @@ -62,6 +66,7 @@ class Algorithm(enum.IntEnum): """Wavelet noise.""" def __repr__(self) -> str: + """Return the string representation for this algorithm.""" return f"tcod.noise.Algorithm.{self.name}" @@ -84,13 +89,14 @@ class Implementation(enum.IntEnum): """Turbulence noise implementation.""" def __repr__(self) -> str: + """Return the string representation for this implementation.""" return f"tcod.noise.Implementation.{self.name}" def __getattr__(name: str) -> Implementation: if name in Implementation.__members__: warnings.warn( - f"'tcod.noise.{name}' is deprecated," f" use 'tcod.noise.Implementation.{name}' instead.", + f"'tcod.noise.{name}' is deprecated, use 'tcod.noise.Implementation.{name}' instead.", FutureWarning, stacklevel=2, ) @@ -122,7 +128,7 @@ class Noise: noise_c (CData): A cffi pointer to a TCOD_noise_t object. """ - def __init__( + def __init__( # noqa: PLR0913 self, dimensions: int, algorithm: int = Algorithm.SIMPLEX, @@ -132,7 +138,8 @@ def __init__( octaves: float = 4, seed: int | tcod.random.Random | None = None, ) -> None: - if not 0 < dimensions <= 4: + """Initialize and seed the noise object.""" + if not 0 < dimensions <= 4: # noqa: PLR2004 msg = f"dimensions must be in range 0 < n <= 4, got {dimensions}" raise ValueError(msg) self._seed = seed @@ -156,16 +163,17 @@ def __rng_from_seed(seed: None | int | tcod.random.Random) -> tcod.random.Random return seed def __repr__(self) -> str: + """Return the string representation of this noise instance.""" parameters = [ f"dimensions={self.dimensions}", f"algorithm={self.algorithm!r}", f"implementation={Implementation(self.implementation)!r}", ] - if self.hurst != 0.5: + if self.hurst != 0.5: # noqa: PLR2004 # Default value parameters.append(f"hurst={self.hurst}") - if self.lacunarity != 2: + if self.lacunarity != 2: # noqa: PLR2004 # Default value parameters.append(f"lacunarity={self.lacunarity}") - if self.octaves != 4: + if self.octaves != 4: # noqa: PLR2004 # Default value parameters.append(f"octaves={self.octaves}") if self._seed is not None: parameters.append(f"seed={self._seed}") @@ -173,10 +181,12 @@ def __repr__(self) -> str: @property def dimensions(self) -> int: + """Number of dimensions supported by this noise generator.""" return int(self._tdl_noise_c.dimensions) @property def algorithm(self) -> int: + """Current selected algorithm. Can be changed.""" noise_type = self.noise_c.noise_type return Algorithm(noise_type) if noise_type else Algorithm.SIMPLEX @@ -186,25 +196,29 @@ def algorithm(self, value: int) -> None: @property def implementation(self) -> int: + """Current selected implementation. Can be changed.""" return Implementation(self._tdl_noise_c.implementation) @implementation.setter def implementation(self, value: int) -> None: - if not 0 <= value < 3: + if not 0 <= value < 3: # noqa: PLR2004 msg = f"{value!r} is not a valid implementation. " raise ValueError(msg) self._tdl_noise_c.implementation = value @property def hurst(self) -> float: + """Noise hurst exponent. Can be changed.""" return float(self.noise_c.H) @property def lacunarity(self) -> float: + """Noise lacunarity. Can be changed.""" return float(self.noise_c.lacunarity) @property def octaves(self) -> float: + """Level of detail on fBm and turbulence implementations. Can be changed.""" return float(self._tdl_noise_c.octaves) @octaves.setter @@ -232,10 +246,9 @@ def __getitem__(self, indexes: Any) -> NDArray[np.float32]: if not isinstance(indexes, tuple): indexes = (indexes,) if len(indexes) > self.dimensions: - raise IndexError( - "This noise generator has %i dimensions, but was indexed with %i." % (self.dimensions, len(indexes)) - ) - indexes = np.broadcast_arrays(*indexes) + msg = f"This noise generator has {self.dimensions} dimensions, but was indexed with {len(indexes)}." + raise IndexError(msg) + indexes = list(np.broadcast_arrays(*indexes)) c_input = [ffi.NULL, ffi.NULL, ffi.NULL, ffi.NULL] for i, index in enumerate(indexes): if index.dtype.type == np.object_: @@ -244,13 +257,16 @@ def __getitem__(self, indexes: Any) -> NDArray[np.float32]: indexes[i] = np.ascontiguousarray(index, dtype=np.float32) c_input[i] = ffi.from_buffer("float*", indexes[i]) + c_input_tuple = tuple(c_input) + assert len(c_input_tuple) == 4 # noqa: PLR2004 + out: NDArray[np.float32] = np.empty(indexes[0].shape, dtype=np.float32) if self.implementation == Implementation.SIMPLE: lib.TCOD_noise_get_vectorized( self.noise_c, self.algorithm, out.size, - *c_input, + *c_input_tuple, ffi.from_buffer("float*", out), ) elif self.implementation == Implementation.FBM: @@ -259,7 +275,7 @@ def __getitem__(self, indexes: Any) -> NDArray[np.float32]: self.algorithm, self.octaves, out.size, - *c_input, + *c_input_tuple, ffi.from_buffer("float*", out), ) elif self.implementation == Implementation.TURBULENCE: @@ -268,11 +284,12 @@ def __getitem__(self, indexes: Any) -> NDArray[np.float32]: self.algorithm, self.octaves, out.size, - *c_input, + *c_input_tuple, ffi.from_buffer("float*", out), ) else: - raise TypeError("Unexpected %r" % self.implementation) + msg = f"Unexpected {self.implementation!r}" + raise TypeError(msg) return out @@ -335,17 +352,18 @@ def sample_ogrid(self, ogrid: Sequence[ArrayLike]) -> NDArray[np.float32]: return out def __getstate__(self) -> dict[str, Any]: + """Support picking this instance.""" state = self.__dict__.copy() - if self.dimensions < 4 and self.noise_c.waveletTileData == ffi.NULL: + if self.dimensions < 4 and self.noise_c.waveletTileData == ffi.NULL: # noqa: PLR2004 # Trigger a side effect of wavelet, so that copies will be synced. saved_algo = self.algorithm self.algorithm = tcod.constants.NOISE_WAVELET self.get_point() self.algorithm = saved_algo - waveletTileData = None + waveletTileData = None # noqa: N806 if self.noise_c.waveletTileData != ffi.NULL: - waveletTileData = list(self.noise_c.waveletTileData[0 : 32 * 32 * 32]) + waveletTileData = list(self.noise_c.waveletTileData[0 : 32 * 32 * 32]) # noqa: N806 state["_waveletTileData"] = waveletTileData state["noise_c"] = { @@ -366,6 +384,7 @@ def __getstate__(self) -> dict[str, Any]: return state def __setstate__(self, state: dict[str, Any]) -> None: + """Unpickle this instance.""" if isinstance(state, tuple): # deprecated format return self._setstate_old(state) # unpack wavelet tile data if it exists @@ -385,7 +404,7 @@ def __setstate__(self, state: dict[str, Any]) -> None: self.__dict__.update(state) return None - def _setstate_old(self, state: Any) -> None: + def _setstate_old(self, state: tuple[Any, ...]) -> None: self._random = state[0] self.noise_c = ffi.new("struct TCOD_Noise*") self.noise_c.ndim = state[3] @@ -405,9 +424,11 @@ def _setstate_old(self, state: Any) -> None: def grid( shape: tuple[int, ...], scale: tuple[float, ...] | float, - origin: tuple[int, ...] | None = None, + origin: tuple[float, ...] | None = None, indexing: Literal["ij", "xy"] = "xy", -) -> tuple[NDArray[Any], ...]: + *, + offset: tuple[float, ...] | None = None, +) -> tuple[NDArray[np.number], ...]: """Generate a mesh-grid of sample points to use with noise sampling. Args: @@ -420,6 +441,11 @@ def grid( If `None` then the `origin` will be zero on each axis. `origin` is not scaled by the `scale` parameter. indexing: Passed to :any:`numpy.meshgrid`. + offset: The offset into the shape to generate. + Similar to `origin` but is scaled by the `scale` parameter. + Can be multiples of `shape` to index noise samples by chunk. + + .. versionadded:: 19.2 Returns: A sparse mesh-grid to be passed into a :class:`Noise` instance. @@ -428,14 +454,14 @@ def grid( >>> noise = tcod.noise.Noise(dimensions=2, seed=42) - # Common case for ij-indexed arrays. + # Common case for ij-indexed arrays >>> noise[tcod.noise.grid(shape=(3, 5), scale=0.25, indexing="ij")] array([[ 0. , -0.27523372, -0.40398532, -0.50773406, -0.64945626], [-0.55046356, -0.7205134 , -0.57662135, -0.2643614 , -0.12529983], [-0.76072866, -0.74057037, -0.33160293, 0.24446318, 0.5346834 ]], dtype=float32) - # Transpose an xy-indexed array to get a standard order="F" result. + # Transpose an xy-indexed array to get a standard order="F" result >>> noise[tcod.noise.grid(shape=(4, 5), scale=(0.5, 0.25), origin=(1.0, 1.0))].T array([[ 0.52655405, 0.25038874, -0.03488023, -0.18455243, -0.16333057], [-0.5037453 , -0.75348294, -0.73630923, -0.35063767, 0.18149695], @@ -443,9 +469,26 @@ def grid( [-0.7057655 , -0.5817767 , -0.22774395, 0.02399864, -0.07006818]], dtype=float32) + # Can sample noise by chunk using the offset keyword + >>> noise[tcod.noise.grid(shape=(3, 5), scale=0.25, indexing="ij", offset=(0, 0))] + array([[ 0. , -0.27523372, -0.40398532, -0.50773406, -0.64945626], + [-0.55046356, -0.7205134 , -0.57662135, -0.2643614 , -0.12529983], + [-0.76072866, -0.74057037, -0.33160293, 0.24446318, 0.5346834 ]], + dtype=float32) + >>> noise[tcod.noise.grid(shape=(3, 5), scale=0.25, indexing="ij", offset=(3, 0))] + array([[-0.7088647 , -0.43919194, 0.12860827, 0.6390255 , 0.80402255], + [-0.68165785, -0.29195625, 0.2864191 , 0.5922846 , 0.52655405], + [-0.7841389 , -0.46131462, 0.0159424 , 0.17141782, -0.04198273]], + dtype=float32) + >>> noise[tcod.noise.grid(shape=(3, 5), scale=0.25, indexing="ij", offset=(6, 0))] + array([[-0.779634 , -0.60696834, -0.27446985, -0.23233278, -0.5037453 ], + [-0.5474089 , -0.54476213, -0.42235228, -0.49519652, -0.7101793 ], + [-0.28291094, -0.4326369 , -0.5227732 , -0.69655263, -0.81221616]], + dtype=float32) + .. versionadded:: 12.2 """ - if isinstance(scale, float): + if isinstance(scale, (int, float)): scale = (scale,) * len(shape) if origin is None: origin = (0,) * len(shape) @@ -455,5 +498,14 @@ def grid( if len(shape) != len(origin): msg = "shape must have the same length as origin" raise TypeError(msg) - indexes = (np.arange(i_shape) * i_scale + i_origin for i_shape, i_scale, i_origin in zip(shape, scale, origin)) + if offset is not None: + if len(shape) != len(offset): + msg = "shape must have the same length as offset" + raise TypeError(msg) + origin = tuple( + i_origin + i_scale * i_offset for i_scale, i_offset, i_origin in zip(scale, offset, origin, strict=True) + ) + indexes = ( + np.arange(i_shape) * i_scale + i_origin for i_shape, i_scale, i_origin in zip(shape, scale, origin, strict=True) + ) return tuple(np.meshgrid(*indexes, copy=False, sparse=True, indexing=indexing)) diff --git a/tcod/path.c b/tcod/path.c index 873e2dae..8f4f2fc6 100644 --- a/tcod/path.c +++ b/tcod/path.c @@ -315,8 +315,8 @@ int compute_heuristic(const struct PathfinderHeuristic* __restrict heuristic, in default: return 0; } - int diagonal = heuristic->diagonal != 0 ? MIN(x, y) : 0; - int straight = MAX(x, y) - diagonal; + int diagonal = heuristic->diagonal != 0 ? TCOD_MIN(x, y) : 0; + int straight = TCOD_MAX(x, y) - diagonal; return (straight * heuristic->cardinal + diagonal * heuristic->diagonal + w * heuristic->w + z * heuristic->z); } void path_compute_add_edge( @@ -454,7 +454,7 @@ static int update_frontier_from_distance_iterator( int dist = get_array_int(dist_map, dimension, index); return TCOD_frontier_push(frontier, index, dist, dist); } - for (int i = 0; i < dist_map->shape[dimension];) { + for (int i = 0; i < dist_map->shape[dimension]; ++i) { index[dimension] = i; int err = update_frontier_from_distance_iterator(frontier, dist_map, dimension + 1, index); if (err) { return err; } diff --git a/tcod/path.h b/tcod/path.h index dc87cae3..75754d1b 100644 --- a/tcod/path.h +++ b/tcod/path.h @@ -13,7 +13,7 @@ extern "C" { /** * Common NumPy data types. */ -enum NP_Type { +typedef enum NP_Type { np_undefined = 0, np_int8, np_int16, @@ -26,23 +26,23 @@ enum NP_Type { np_float16, np_float32, np_float64, -}; +} NP_Type; /** * A simple 4D NumPy array ctype. */ -struct NArray { - enum NP_Type type; +typedef struct NArray { + NP_Type type; int8_t ndim; char* __restrict data; ptrdiff_t shape[5]; // TCOD_PATHFINDER_MAX_DIMENSIONS + 1 ptrdiff_t strides[5]; // TCOD_PATHFINDER_MAX_DIMENSIONS + 1 -}; +} NArray; struct PathfinderRule { /** Rule condition, could be uninitialized zeros. */ - struct NArray condition; + NArray condition; /** Edge cost map, required. */ - struct NArray cost; + NArray cost; /** Number of edge rules in `edge_array`. */ int edge_count; /** Example of 2D edges: [i, j, cost, i_2, j_2, cost_2, ...] */ @@ -131,7 +131,7 @@ int path_compute( parameters. */ ptrdiff_t get_travel_path( - int8_t ndim, const struct NArray* __restrict travel_map, const int* __restrict start, int* __restrict out); + int8_t ndim, const NArray* __restrict travel_map, const int* __restrict start, int* __restrict out); /** Update the priority of nodes on the frontier and sort them. */ @@ -142,7 +142,7 @@ int update_frontier_heuristic( Assumes no heuristic is active. */ -int rebuild_frontier_from_distance(struct TCOD_Frontier* __restrict frontier, const struct NArray* __restrict dist_map); +int rebuild_frontier_from_distance(struct TCOD_Frontier* __restrict frontier, const NArray* __restrict dist_map); /** Return true if `index[frontier->ndim]` is a node in `frontier`. */ diff --git a/tcod/path.py b/tcod/path.py index 84d54138..0717ddee 100644 --- a/tcod/path.py +++ b/tcod/path.py @@ -15,44 +15,50 @@ All path-finding functions now respect the NumPy array shape (if a NumPy array is used.) """ + from __future__ import annotations import functools import itertools import warnings -from typing import Any, Callable +from typing import TYPE_CHECKING, Any, Final, Literal import numpy as np -from numpy.typing import ArrayLike, NDArray -from typing_extensions import Literal +from typing_extensions import Self +import tcod.map from tcod._internal import _check from tcod.cffi import ffi, lib +if TYPE_CHECKING: + from collections.abc import Callable, Sequence + + from numpy.typing import ArrayLike, DTypeLike, NDArray -@ffi.def_extern() # type: ignore -def _pycall_path_old(x1: int, y1: int, x2: int, y2: int, handle: Any) -> float: - """Libtcodpy style callback, needs to preserve the old userData issue.""" - func, userData = ffi.from_handle(handle) - return func(x1, y1, x2, y2, userData) # type: ignore +@ffi.def_extern() # type: ignore[untyped-decorator] +def _pycall_path_old(x1: int, y1: int, x2: int, y2: int, handle: Any) -> float: # noqa: ANN401 + """Libtcodpy style callback, needs to preserve the old userdata issue.""" + func, userdata = ffi.from_handle(handle) + return func(x1, y1, x2, y2, userdata) # type: ignore[no-any-return] -@ffi.def_extern() # type: ignore -def _pycall_path_simple(x1: int, y1: int, x2: int, y2: int, handle: Any) -> float: + +@ffi.def_extern() # type: ignore[untyped-decorator] +def _pycall_path_simple(x1: int, y1: int, x2: int, y2: int, handle: Any) -> float: # noqa: ANN401 """Does less and should run faster, just calls the handle function.""" - return ffi.from_handle(handle)(x1, y1, x2, y2) # type: ignore + return ffi.from_handle(handle)(x1, y1, x2, y2) # type: ignore[no-any-return] -@ffi.def_extern() # type: ignore -def _pycall_path_swap_src_dest(x1: int, y1: int, x2: int, y2: int, handle: Any) -> float: +@ffi.def_extern() # type: ignore[untyped-decorator] +def _pycall_path_swap_src_dest(x1: int, y1: int, x2: int, y2: int, handle: Any) -> float: # noqa: ANN401 """A TDL function dest comes first to match up with a dest only call.""" - return ffi.from_handle(handle)(x2, y2, x1, y1) # type: ignore + return ffi.from_handle(handle)(x2, y2, x1, y1) # type: ignore[no-any-return] -@ffi.def_extern() # type: ignore -def _pycall_path_dest_only(x1: int, y1: int, x2: int, y2: int, handle: Any) -> float: +@ffi.def_extern() # type: ignore[untyped-decorator] +def _pycall_path_dest_only(_x1: int, _y1: int, x2: int, y2: int, handle: Any) -> float: # noqa: ANN401 """A TDL function which samples the dest coordinate only.""" - return ffi.from_handle(handle)(x2, y2) # type: ignore + return ffi.from_handle(handle)(x2, y2) # type: ignore[no-any-return] def _get_path_cost_func( @@ -60,8 +66,8 @@ def _get_path_cost_func( ) -> Callable[[int, int, int, int, Any], float]: """Return a properly cast PathCostArray callback.""" if not ffi: - return lambda x1, y1, x2, y2, _: 0 - return ffi.cast("TCOD_path_func_t", ffi.addressof(lib, name)) # type: ignore + return lambda _x1, _y1, _x2, _y2, _: 0 + return ffi.cast("TCOD_path_func_t", ffi.addressof(lib, name)) # type: ignore[no-any-return] class _EdgeCostFunc: @@ -74,7 +80,7 @@ class _EdgeCostFunc: _CALLBACK_P = lib._pycall_path_old - def __init__(self, userdata: Any, shape: tuple[int, int]) -> None: + def __init__(self, userdata: object, shape: tuple[int, int]) -> None: self._userdata = userdata self.shape = shape @@ -83,11 +89,7 @@ def get_tcod_path_ffi(self) -> tuple[Any, Any, tuple[int, int]]: return self._CALLBACK_P, ffi.new_handle(self._userdata), self.shape def __repr__(self) -> str: - return "{}({!r}, shape={!r})".format( - self.__class__.__name__, - self._userdata, - self.shape, - ) + return f"{self.__class__.__name__}({self._userdata!r}, shape={self.shape!r})" class EdgeCostCallback(_EdgeCostFunc): @@ -110,18 +112,19 @@ def __init__( callback: Callable[[int, int, int, int], float], shape: tuple[int, int], ) -> None: + """Initialize this callback.""" self.callback = callback super().__init__(callback, shape) -class NodeCostArray(np.ndarray): # type: ignore +class NodeCostArray(np.ndarray): """Calculate cost from a numpy array of nodes. `array` is a NumPy array holding the path-cost of each node. A cost of 0 means the node is blocking. """ - _C_ARRAY_CALLBACKS = { + _C_ARRAY_CALLBACKS: Final = { np.float32: ("float*", _get_path_cost_func("PathCostArrayFloat32")), np.bool_: ("int8_t*", _get_path_cost_func("PathCostArrayInt8")), np.int8: ("int8_t*", _get_path_cost_func("PathCostArrayInt8")), @@ -132,23 +135,23 @@ class NodeCostArray(np.ndarray): # type: ignore np.uint32: ("uint32_t*", _get_path_cost_func("PathCostArrayUInt32")), } - def __new__(cls, array: ArrayLike) -> NodeCostArray: + def __new__(cls, array: ArrayLike) -> Self: """Validate a numpy array and setup a C callback.""" - self = np.asarray(array).view(cls) - return self + return np.asarray(array).view(cls) def __repr__(self) -> str: + """Return the string representation of this object.""" return f"{self.__class__.__name__}({repr(self.view(np.ndarray))!r})" def get_tcod_path_ffi(self) -> tuple[Any, Any, tuple[int, int]]: - if len(self.shape) != 2: + if len(self.shape) != 2: # noqa: PLR2004 msg = f"Array must have a 2d shape, shape is {self.shape!r}" raise ValueError(msg) if self.dtype.type not in self._C_ARRAY_CALLBACKS: msg = f"dtype must be one of {self._C_ARRAY_CALLBACKS.keys()!r}, dtype is {self.dtype.type!r}" raise ValueError(msg) - array_type, callback = self._C_ARRAY_CALLBACKS[self.dtype.type] + _array_type, callback = self._C_ARRAY_CALLBACKS[self.dtype.type] userdata = ffi.new( "struct PathCostArray*", (ffi.cast("char*", self.ctypes.data), self.strides), @@ -159,13 +162,13 @@ def get_tcod_path_ffi(self) -> tuple[Any, Any, tuple[int, int]]: class _PathFinder: """A class sharing methods used by AStar and Dijkstra.""" - def __init__(self, cost: Any, diagonal: float = 1.41) -> None: + def __init__(self, cost: tcod.map.Map | ArrayLike | _EdgeCostFunc, diagonal: float = 1.41) -> None: self.cost = cost self.diagonal = diagonal self._path_c: Any = None self._callback = self._userdata = None - if hasattr(self.cost, "map_c"): + if isinstance(self.cost, tcod.map.Map): self.shape = self.cost.width, self.cost.height self._path_c = ffi.gc( self._path_new_using_map(self.cost.map_c, diagonal), @@ -173,9 +176,9 @@ def __init__(self, cost: Any, diagonal: float = 1.41) -> None: ) return - if not hasattr(self.cost, "get_tcod_path_ffi"): + if not isinstance(self.cost, _EdgeCostFunc): assert not callable(self.cost), ( - "Any callback alone is missing shape information. " "Wrap your callback in tcod.path.EdgeCostCallback" + "Any callback alone is missing shape information. Wrap your callback in tcod.path.EdgeCostCallback" ) self.cost = NodeCostArray(self.cost) @@ -198,7 +201,7 @@ def __init__(self, cost: Any, diagonal: float = 1.41) -> None: def __repr__(self) -> str: return f"{self.__class__.__name__}(cost={self.cost!r}, diagonal={self.diagonal!r})" - def __getstate__(self) -> Any: + def __getstate__(self) -> dict[str, Any]: state = self.__dict__.copy() del state["_path_c"] del state["shape"] @@ -206,9 +209,9 @@ def __getstate__(self) -> Any: del state["_userdata"] return state - def __setstate__(self, state: Any) -> None: + def __setstate__(self, state: dict[str, Any]) -> None: self.__dict__.update(state) - self.__init__(self.cost, self.diagonal) # type: ignore + _PathFinder.__init__(self, self.cost, self.diagonal) _path_new_using_map = lib.TCOD_path_new_using_map _path_new_using_function = lib.TCOD_path_new_using_function @@ -241,7 +244,7 @@ def get_path(self, start_x: int, start_y: int, goal_x: int, goal_y: int) -> list path = [] x = ffi.new("int[2]") y = x + 1 - while lib.TCOD_path_walk(self._path_c, x, y, False): + while lib.TCOD_path_walk(self._path_c, x, y, False): # noqa: FBT003 path.append((x[0], y[0])) return path @@ -290,7 +293,7 @@ def get_path(self, x: int, y: int) -> list[tuple[int, int]]: def maxarray( shape: tuple[int, ...], - dtype: Any = np.int32, + dtype: DTypeLike = np.int32, order: Literal["C", "F"] = "C", ) -> NDArray[Any]: """Return a new array filled with the maximum finite value for `dtype`. @@ -325,7 +328,7 @@ def _export_dict(array: NDArray[Any]) -> dict[str, Any]: } -def _export(array: NDArray[Any]) -> Any: +def _export(array: NDArray[np.integer]) -> Any: # noqa: ANN401 """Convert a NumPy array into a cffi object.""" return ffi.new("struct NArray*", _export_dict(array)) @@ -333,8 +336,9 @@ def _export(array: NDArray[Any]) -> Any: def _compile_cost_edges(edge_map: ArrayLike) -> tuple[NDArray[np.intc], int]: """Return an edge_cost array using an integer map.""" edge_map = np.array(edge_map, copy=True) - if edge_map.ndim != 2: - raise ValueError("edge_map must be 2 dimensional. (Got %i)" % edge_map.ndim) + if edge_map.ndim != 2: # noqa: PLR2004 + msg = f"edge_map must be 2 dimensional. (Got {edge_map.ndim})" + raise ValueError(msg) edge_center = edge_map.shape[0] // 2, edge_map.shape[1] // 2 edge_map[edge_center] = 0 edge_map[edge_map < 0] = 0 @@ -348,15 +352,15 @@ def _compile_cost_edges(edge_map: ArrayLike) -> tuple[NDArray[np.intc], int]: return c_edges, len(edge_array) -def dijkstra2d( +def dijkstra2d( # noqa: PLR0913 distance: ArrayLike, cost: ArrayLike, cardinal: int | None = None, diagonal: int | None = None, *, - edge_map: Any = None, - out: np.ndarray | None = ..., # type: ignore -) -> NDArray[Any]: + edge_map: ArrayLike | None = None, + out: NDArray[np.integer] | None = ..., # type: ignore[assignment, unused-ignore] +) -> NDArray[np.integer]: """Return the computed distance of all nodes on a 2D Dijkstra grid. `distance` is an input array of node distances. Is this often an @@ -472,7 +476,7 @@ def dijkstra2d( Added `out` parameter. Now returns the output array. """ dist: NDArray[Any] = np.asarray(distance) - if out is ...: + if out is ...: # type: ignore[comparison-overlap] out = dist warnings.warn( "No `out` parameter was given. " @@ -524,11 +528,11 @@ def _compile_bool_edges(edge_map: ArrayLike) -> tuple[Any, int]: def hillclimb2d( distance: ArrayLike, start: tuple[int, int], - cardinal: bool | None = None, - diagonal: bool | None = None, + cardinal: bool | None = None, # noqa: FBT001 + diagonal: bool | None = None, # noqa: FBT001 *, - edge_map: Any = None, -) -> NDArray[Any]: + edge_map: ArrayLike | None = None, +) -> NDArray[np.intc]: """Return a path on a grid from `start` to the lowest point. `distance` should be a fully computed distance array. This kind of array @@ -573,7 +577,7 @@ def hillclimb2d( c_edges, n_edges = _compile_bool_edges(edge_map) func = functools.partial(lib.hillclimb2d, c_dist, x, y, n_edges, c_edges) else: - func = functools.partial(lib.hillclimb2d_basic, c_dist, x, y, cardinal, diagonal) + func = functools.partial(lib.hillclimb2d_basic, c_dist, x, y, bool(cardinal), bool(diagonal)) length = _check(func(ffi.NULL)) path: np.ndarray[Any, np.dtype[np.intc]] = np.ndarray((length, 2), dtype=np.intc) c_path = ffi.from_buffer("int*", path) @@ -581,7 +585,7 @@ def hillclimb2d( return path -def _world_array(shape: tuple[int, ...], dtype: Any = np.int32) -> NDArray[Any]: +def _world_array(shape: tuple[int, ...], dtype: DTypeLike = np.int32) -> NDArray[Any]: """Return an array where ``ij == arr[ij]``.""" return np.ascontiguousarray( np.transpose( @@ -595,7 +599,7 @@ def _world_array(shape: tuple[int, ...], dtype: Any = np.int32) -> NDArray[Any]: ) -def _as_hashable(obj: np.ndarray[Any, Any] | None) -> Any | None: +def _as_hashable(obj: NDArray[Any] | None) -> object | None: """Return NumPy arrays as a more hashable form.""" if obj is None: return obj @@ -665,12 +669,13 @@ class CustomGraph: """ def __init__(self, shape: tuple[int, ...], *, order: str = "C") -> None: + """Initialize the custom graph.""" self._shape = self._shape_c = tuple(shape) self._ndim = len(self._shape) self._order = order if self._order == "F": self._shape_c = self._shape_c[::-1] - if not 0 < self._ndim <= 4: + if not 0 < self._ndim <= 4: # noqa: PLR2004 msg = "Graph dimensions must be 1 <= n <= 4." raise TypeError(msg) self._graph: dict[tuple[Any, ...], dict[str, Any]] = {} @@ -751,7 +756,8 @@ def add_edge( edge_dir = tuple(edge_dir) cost = np.asarray(cost) if len(edge_dir) != self._ndim: - raise TypeError("edge_dir must have exactly %i items, got %r" % (self._ndim, edge_dir)) + msg = f"edge_dir must have exactly {self._ndim} items, got {edge_dir!r}" + raise TypeError(msg) if edge_cost <= 0: msg = f"edge_cost must be greater than zero, got {edge_cost!r}" raise ValueError(msg) @@ -786,7 +792,7 @@ def add_edge( def add_edges( self, *, - edge_map: ArrayLike, + edge_map: ArrayLike | NDArray[np.integer], cost: NDArray[Any], condition: ArrayLike | None = None, ) -> None: @@ -885,7 +891,8 @@ def add_edges( if edge_map.ndim < self._ndim: edge_map = np.asarray(edge_map[(np.newaxis,) * (self._ndim - edge_map.ndim)]) if edge_map.ndim != self._ndim: - raise TypeError("edge_map must must match graph dimensions (%i). (Got %i)" % (self.ndim, edge_map.ndim)) + msg = f"edge_map must must match graph dimensions ({self.ndim}). (Got {edge_map.ndim})" + raise TypeError(msg) if self._order == "F": # edge_map needs to be converted into C. # The other parameters are converted by the add_edge method. @@ -897,9 +904,17 @@ def add_edges( edge_costs = edge_map[edge_nz] edge_array = np.transpose(edge_nz) edge_array -= edge_center - for edge, edge_cost in zip(edge_array, edge_costs): - edge = tuple(edge) - self.add_edge(edge, edge_cost, cost=cost, condition=condition) + for edge, edge_cost in zip( + edge_array, + edge_costs, + strict=True, + ): + self.add_edge( + tuple(edge.tolist()), + edge_cost, + cost=cost, + condition=condition, + ) def set_heuristic(self, *, cardinal: int = 0, diagonal: int = 0, z: int = 0, w: int = 0) -> None: """Set a pathfinder heuristic so that pathfinding can done with A*. @@ -961,7 +976,7 @@ def set_heuristic(self, *, cardinal: int = 0, diagonal: int = 0, z: int = 0, w: raise ValueError(msg) self._heuristic = (cardinal, diagonal, z, w) - def _compile_rules(self) -> Any: + def _compile_rules(self) -> Any: # noqa: ANN401 """Compile this graph into a C struct array.""" if not self._edge_rules_p: self._edge_rules_keep_alive = [] @@ -986,12 +1001,12 @@ def _compile_rules(self) -> Any: def _resolve(self, pathfinder: Pathfinder) -> None: """Run the pathfinding algorithm for this graph.""" - rules, keep_alive = self._compile_rules() + rules, _keep_alive = self._compile_rules() _check( lib.path_compute( pathfinder._frontier_p, - pathfinder._distance_p, - pathfinder._travel_p, + _export(pathfinder._distance), + _export(pathfinder._travel), len(rules), rules, pathfinder._heuristic_p, @@ -1032,8 +1047,9 @@ class SimpleGraph: """ def __init__(self, *, cost: ArrayLike, cardinal: int, diagonal: int, greed: int = 1) -> None: + """Initialize the graph.""" cost = np.asarray(cost) - if cost.ndim != 2: + if cost.ndim != 2: # noqa: PLR2004 msg = f"The cost array must e 2 dimensional, array of shape {cost.shape!r} given." raise TypeError(msg) if greed <= 0: @@ -1054,10 +1070,12 @@ def __init__(self, *, cost: ArrayLike, cardinal: int, diagonal: int, greed: int @property def ndim(self) -> int: + """Number of dimensions.""" return 2 @property def shape(self) -> tuple[int, int]: + """Shape of this graph.""" return self._shape @property @@ -1091,13 +1109,12 @@ class Pathfinder: """ def __init__(self, graph: CustomGraph | SimpleGraph) -> None: + """Initialize the pathfinder from a graph.""" self._graph = graph self._order = graph._order self._frontier_p = ffi.gc(lib.TCOD_frontier_new(self._graph._ndim), lib.TCOD_frontier_delete) self._distance = maxarray(self._graph._shape_c) self._travel = _world_array(self._graph._shape_c) - self._distance_p = _export(self._distance) - self._travel_p = _export(self._travel) self._heuristic: tuple[int, int, int, int, tuple[int, ...]] | None = None self._heuristic_p: Any = ffi.NULL @@ -1145,7 +1162,7 @@ def traversal(self) -> NDArray[Any]: >>> i, j = (3, 3) # Starting index. >>> path = [(i, j)] # List of nodes from the start to the root. >>> while not (pf.traversal[i, j] == (i, j)).all(): - ... i, j = pf.traversal[i, j] + ... i, j = pf.traversal[i, j].tolist() ... path.append((i, j)) >>> path # Slower. [(3, 3), (2, 2), (1, 1), (0, 0)] @@ -1166,8 +1183,22 @@ def traversal(self) -> NDArray[Any]: def clear(self) -> None: """Reset the pathfinder to its initial state. - This sets all values on the :any:`distance` array to their maximum - value. + This sets all values on the :any:`distance` array to their maximum value. + + Example:: + + >>> import tcod.path + >>> graph = tcod.path.SimpleGraph( + ... cost=np.ones((5, 5), np.int8), cardinal=2, diagonal=3, + ... ) + >>> pf = tcod.path.Pathfinder(graph) + >>> pf.add_root((0, 0)) + >>> pf.path_to((2, 2)).tolist() + [[0, 0], [1, 1], [2, 2]] + >>> pf.clear() # Reset Pathfinder to its initial state + >>> pf.add_root((0, 2)) + >>> pf.path_to((2, 2)).tolist() + [[0, 2], [1, 2], [2, 2]] """ self._distance[...] = np.iinfo(self._distance.dtype).max self._travel = _world_array(self._graph._shape_c) @@ -1186,7 +1217,8 @@ def add_root(self, index: tuple[int, ...], value: int = 0) -> None: if self._order == "F": # Convert to ij indexing order. index = index[::-1] if len(index) != self._distance.ndim: - raise TypeError("Index must be %i items, got %r" % (self._distance.ndim, index)) + msg = f"Index must be {self._distance.ndim} items, got {index!r}" + raise TypeError(msg) self._distance[index] = value self._update_heuristic(None) lib.TCOD_frontier_push(self._frontier_p, index, value, value) @@ -1219,10 +1251,24 @@ def rebuild_frontier(self) -> None: After you are finished editing :any:`distance` you must call this function before calling :any:`resolve` or any function which calls :any:`resolve` implicitly such as :any:`path_from` or :any:`path_to`. + + Example:: + + >>> import tcod.path + >>> graph = tcod.path.SimpleGraph( + ... cost=np.ones((5, 5), np.int8), cardinal=2, diagonal=3, + ... ) + >>> pf = tcod.path.Pathfinder(graph) + >>> pf.distance[:, 0] = 0 # Set roots along entire left edge + >>> pf.rebuild_frontier() + >>> pf.path_to((0, 2)).tolist() # Finds best path from [:, 0] + [[0, 0], [0, 1], [0, 2]] + >>> pf.path_to((4, 2)).tolist() + [[4, 0], [4, 1], [4, 2]] """ lib.TCOD_frontier_clear(self._frontier_p) self._update_heuristic(None) - _check(lib.rebuild_frontier_from_distance(self._frontier_p, self._distance_p)) + _check(lib.rebuild_frontier_from_distance(self._frontier_p, _export(self._distance))) def resolve(self, goal: tuple[int, ...] | None = None) -> None: """Manually run the pathfinder algorithm. @@ -1273,17 +1319,18 @@ def resolve(self, goal: tuple[int, ...] | None = None) -> None: if goal is not None: goal = tuple(goal) # Check for bad input. if len(goal) != self._distance.ndim: - raise TypeError("Goal must be %i items, got %r" % (self._distance.ndim, goal)) + msg = f"Goal must be {self._distance.ndim} items, got {goal!r}" + raise TypeError(msg) if self._order == "F": # Goal is now ij indexed for the rest of this function. goal = goal[::-1] - if self._distance[goal] != np.iinfo(self._distance.dtype).max: + if self._distance[goal] != np.iinfo(self._distance.dtype).max: # noqa: SIM102 if not lib.frontier_has_index(self._frontier_p, goal): return self._update_heuristic(goal) self._graph._resolve(self) - def path_from(self, index: tuple[int, ...]) -> NDArray[Any]: + def path_from(self, index: tuple[int, ...]) -> NDArray[np.intc]: """Return the shortest path from `index` to the nearest root. The returned array is of shape `(length, ndim)` where `length` is the @@ -1320,23 +1367,25 @@ def path_from(self, index: tuple[int, ...]) -> NDArray[Any]: """ index = tuple(index) # Check for bad input. if len(index) != self._graph._ndim: - raise TypeError("Index must be %i items, got %r" % (self._distance.ndim, index)) + msg = f"Index must be {self._distance.ndim} items, got {index!r}" + raise TypeError(msg) self.resolve(index) if self._order == "F": # Convert to ij indexing order. index = index[::-1] - length = _check(lib.get_travel_path(self._graph._ndim, self._travel_p, index, ffi.NULL)) + _travel_p = _export(self._travel) + length = _check(lib.get_travel_path(self._graph._ndim, _travel_p, index, ffi.NULL)) path: np.ndarray[Any, np.dtype[np.intc]] = np.ndarray((length, self._graph._ndim), dtype=np.intc) _check( lib.get_travel_path( self._graph._ndim, - self._travel_p, + _travel_p, index, ffi.from_buffer("int*", path), ) ) return path[:, ::-1] if self._order == "F" else path - def path_to(self, index: tuple[int, ...]) -> NDArray[Any]: + def path_to(self, index: tuple[int, ...]) -> NDArray[np.intc]: """Return the shortest path from the nearest root to `index`. See :any:`path_from`. @@ -1363,3 +1412,146 @@ def path_to(self, index: tuple[int, ...]) -> NDArray[Any]: [] """ return self.path_from(index)[::-1] + + +def path2d( # noqa: C901, PLR0912, PLR0913 + cost: ArrayLike, + *, + start_points: Sequence[tuple[int, int]], + end_points: Sequence[tuple[int, int]], + cardinal: int, + diagonal: int | None = None, + check_bounds: bool = True, +) -> NDArray[np.intc]: + """Return a path between `start_points` and `end_points`. + + If `start_points` or `end_points` has only one item then this is equivalent to A*. + Otherwise it is equivalent to Dijkstra. + + If multiple `start_points` or `end_points` are given then the single shortest path between them is returned. + + Points placed on nodes with a cost of 0 are treated as always reachable from adjacent nodes. + + Args: + cost: A 2D array of integers with the cost of each node. + start_points: A sequence of one or more starting points indexing `cost`. + end_points: A sequence of one or more ending points indexing `cost`. + cardinal: The relative cost to move a cardinal direction. + diagonal: The relative cost to move a diagonal direction. + `None` or `0` will disable diagonal movement. + check_bounds: If `False` then out-of-bounds points are silently ignored. + If `True` (default) then out-of-bounds points raise :any:`IndexError`. + + Returns: + A `(length, 2)` array of indexes of the path including the start and end points. + If there is no path then an array with zero items will be returned. + + Example:: + + # Note: coordinates in this example are (i, j), or (y, x) + >>> cost = np.array([ + ... [1, 0, 1, 1, 1, 0, 1], + ... [1, 0, 1, 1, 1, 0, 1], + ... [1, 0, 1, 0, 1, 0, 1], + ... [1, 1, 1, 1, 1, 0, 1], + ... ]) + + # Endpoints are reachable even when endpoints are on blocked nodes + >>> tcod.path.path2d(cost, start_points=[(0, 0)], end_points=[(2, 3)], cardinal=70, diagonal=99) + array([[0, 0], + [1, 0], + [2, 0], + [3, 1], + [2, 2], + [2, 3]], dtype=int...) + + # Unreachable endpoints return a zero length array + >>> tcod.path.path2d(cost, start_points=[(0, 0)], end_points=[(3, 6)], cardinal=70, diagonal=99) + array([], shape=(0, 2), dtype=int...) + >>> tcod.path.path2d(cost, start_points=[(0, 0), (3, 0)], end_points=[(0, 6), (3, 6)], cardinal=70, diagonal=99) + array([], shape=(0, 2), dtype=int...) + >>> tcod.path.path2d(cost, start_points=[], end_points=[], cardinal=70, diagonal=99) + array([], shape=(0, 2), dtype=int...) + + # Overlapping endpoints return a single step + >>> tcod.path.path2d(cost, start_points=[(0, 0)], end_points=[(0, 0)], cardinal=70, diagonal=99) + array([[0, 0]], dtype=int32) + + # Multiple endpoints return the shortest path + >>> tcod.path.path2d( + ... cost, start_points=[(0, 0)], end_points=[(1, 3), (3, 3), (2, 2), (2, 4)], cardinal=70, diagonal=99) + array([[0, 0], + [1, 0], + [2, 0], + [3, 1], + [2, 2]], dtype=int...) + >>> tcod.path.path2d( + ... cost, start_points=[(0, 0), (0, 2)], end_points=[(1, 3), (3, 3), (2, 2), (2, 4)], cardinal=70, diagonal=99) + array([[0, 2], + [1, 3]], dtype=int...) + >>> tcod.path.path2d(cost, start_points=[(0, 0), (0, 2)], end_points=[(3, 2)], cardinal=1) + array([[0, 2], + [1, 2], + [2, 2], + [3, 2]], dtype=int...) + + # Checking for out-of-bounds points may be toggled + >>> tcod.path.path2d(cost, start_points=[(0, 0)], end_points=[(-1, -1), (3, 1)], cardinal=1) + Traceback (most recent call last): + ... + IndexError: End point (-1, -1) is out-of-bounds of cost shape (4, 7) + >>> tcod.path.path2d(cost, start_points=[(0, 0)], end_points=[(-1, -1), (3, 1)], cardinal=1, check_bounds=False) + array([[0, 0], + [1, 0], + [2, 0], + [3, 0], + [3, 1]], dtype=int...) + + .. versionadded:: 18.1 + """ + cost = np.copy(cost) # Copy array to later modify nodes to be always reachable + + # Check bounds of endpoints + if check_bounds: + for points, name in [(start_points, "start"), (end_points, "end")]: + for i, j in points: + if not (0 <= i < cost.shape[0] and 0 <= j < cost.shape[1]): + msg = f"{name.capitalize()} point {(i, j)!r} is out-of-bounds of cost shape {cost.shape!r}" + raise IndexError(msg) + else: + start_points = [(i, j) for i, j in start_points if 0 <= i < cost.shape[0] and 0 <= j < cost.shape[1]] + end_points = [(i, j) for i, j in end_points if 0 <= i < cost.shape[0] and 0 <= j < cost.shape[1]] + + if not start_points or not end_points: + return np.zeros((0, 2), dtype=np.intc) # Missing endpoints + + # Check if endpoints can be manipulated to use A* for a one-to-many computation + reversed_path = False + if len(end_points) == 1 and len(start_points) > 1: + # Swap endpoints to ensure single start point as the A* goal + reversed_path = True + start_points, end_points = end_points, start_points + + for ij in start_points: + cost[ij] = 1 # Enforce reachability of endpoint + + graph = SimpleGraph(cost=cost, cardinal=cardinal, diagonal=diagonal or 0) + pf = Pathfinder(graph) + for ij in end_points: + pf.add_root(ij) + + if len(start_points) == 1: # Compute A* from possibly multiple roots to one goal + out = pf.path_from(start_points[0]) + if pf.distance[start_points[0]] == np.iinfo(pf.distance.dtype).max: + return np.zeros((0, 2), dtype=np.intc) # Unreachable endpoint + if reversed_path: + out = out[::-1] + return out + + # Crude Dijkstra implementation until issues with Pathfinder are fixed + pf.resolve(None) + best_distance, best_ij = min((pf.distance[ij], ij) for ij in start_points) + if best_distance == np.iinfo(pf.distance.dtype).max: + return np.zeros((0, 2), dtype=np.intc) # All endpoints unreachable + + return hillclimb2d(pf.distance, best_ij, cardinal=bool(cardinal), diagonal=bool(diagonal)) diff --git a/tcod/random.py b/tcod/random.py index b574cab8..1b7b16c7 100644 --- a/tcod/random.py +++ b/tcod/random.py @@ -6,16 +6,22 @@ However, you will need to use these generators to get deterministic results from the :any:`Noise` and :any:`BSP` classes. """ + from __future__ import annotations import os import random import warnings -from typing import Any, Hashable +from typing import TYPE_CHECKING, Any + +from typing_extensions import deprecated import tcod.constants from tcod.cffi import ffi, lib +if TYPE_CHECKING: + from collections.abc import Hashable + MERSENNE_TWISTER = tcod.constants.RNG_MT COMPLEMENTARY_MULTIPLY_WITH_CARRY = tcod.constants.RNG_CMWC MULTIPLY_WITH_CARRY = tcod.constants.RNG_CMWC @@ -80,7 +86,7 @@ def __init__( ) @classmethod - def _new_from_cdata(cls, cdata: Any) -> Random: + def _new_from_cdata(cls, cdata: Any) -> Random: # noqa: ANN401 """Return a new instance encapsulating this cdata.""" self: Random = object.__new__(cls) self.random_c = cdata @@ -110,7 +116,7 @@ def uniform(self, low: float, high: float) -> float: """ return float(lib.TCOD_random_get_double(self.random_c, low, high)) - def guass(self, mu: float, sigma: float) -> float: + def gauss(self, mu: float, sigma: float) -> float: """Return a random number using Gaussian distribution. Args: @@ -119,10 +125,17 @@ def guass(self, mu: float, sigma: float) -> float: Returns: float: A random float. + + .. versionchanged:: 16.2 + Renamed from `guass` to `gauss`. """ return float(lib.TCOD_random_get_gaussian_double(self.random_c, mu, sigma)) - def inverse_guass(self, mu: float, sigma: float) -> float: + @deprecated("This is a typo, rename this to 'gauss'", category=FutureWarning) + def guass(self, mu: float, sigma: float) -> float: # noqa: D102 + return self.gauss(mu, sigma) + + def inverse_gauss(self, mu: float, sigma: float) -> float: """Return a random Gaussian number using the Box-Muller transform. Args: @@ -131,10 +144,17 @@ def inverse_guass(self, mu: float, sigma: float) -> float: Returns: float: A random float. + + .. versionchanged:: 16.2 + Renamed from `inverse_guass` to `inverse_gauss`. """ return float(lib.TCOD_random_get_gaussian_double_inv(self.random_c, mu, sigma)) - def __getstate__(self) -> Any: + @deprecated("This is a typo, rename this to 'inverse_gauss'", category=FutureWarning) + def inverse_guass(self, mu: float, sigma: float) -> float: # noqa: D102 + return self.inverse_gauss(mu, sigma) + + def __getstate__(self) -> dict[str, Any]: """Pack the self.random_c attribute into a portable state.""" state = self.__dict__.copy() state["random_c"] = { @@ -150,7 +170,7 @@ def __getstate__(self) -> Any: } return state - def __setstate__(self, state: Any) -> None: + def __setstate__(self, state: dict[str, Any]) -> None: """Create a new cdata object with the stored parameters.""" if "algo" in state["random_c"]: # Handle old/deprecated format. Covert to libtcod's new union type. diff --git a/tcod/render.py b/tcod/render.py index d01d8129..09d3a137 100644 --- a/tcod/render.py +++ b/tcod/render.py @@ -29,9 +29,7 @@ from __future__ import annotations -from typing import Any - -from typing_extensions import Final +from typing import Any, Final import tcod.console import tcod.sdl.render @@ -44,6 +42,7 @@ class SDLTilesetAtlas: """Prepares a tileset for rendering using SDL.""" def __init__(self, renderer: tcod.sdl.render.Renderer, tileset: tcod.tileset.Tileset) -> None: + """Initialize the tileset atlas.""" self._renderer = renderer self.tileset: Final[tcod.tileset.Tileset] = tileset """The tileset used to create this SDLTilesetAtlas.""" @@ -52,7 +51,7 @@ def __init__(self, renderer: tcod.sdl.render.Renderer, tileset: tcod.tileset.Til ) @classmethod - def _from_ref(cls, renderer_p: Any, atlas_p: Any) -> SDLTilesetAtlas: + def _from_ref(cls, renderer_p: Any, atlas_p: Any) -> SDLTilesetAtlas: # noqa: ANN401 self = object.__new__(cls) # Ignore Final reassignment type errors since this is an alternative constructor. # This could be a sign that the current constructor was badly implemented. @@ -66,6 +65,7 @@ class SDLConsoleRender: """Holds an internal cache console and texture which are used to optimized console rendering.""" def __init__(self, atlas: SDLTilesetAtlas) -> None: + """Initialize the console renderer.""" self.atlas: Final[SDLTilesetAtlas] = atlas """The SDLTilesetAtlas used to create this SDLConsoleRender. diff --git a/tcod/sdl/__init__.py b/tcod/sdl/__init__.py index 8133948b..22e82845 100644 --- a/tcod/sdl/__init__.py +++ b/tcod/sdl/__init__.py @@ -1,4 +1,5 @@ """tcod.sdl package.""" + from pkgutil import extend_path __path__ = extend_path(__path__, __name__) diff --git a/tcod/sdl/_internal.py b/tcod/sdl/_internal.py index 00dc868c..db67fa2e 100644 --- a/tcod/sdl/_internal.py +++ b/tcod/sdl/_internal.py @@ -1,14 +1,20 @@ """tcod.sdl private functions.""" + from __future__ import annotations import logging -import sys as _sys +import sys from dataclasses import dataclass -from types import TracebackType -from typing import Any, Callable, TypeVar +from typing import TYPE_CHECKING, Any, NoReturn, Protocol, TypeVar, overload, runtime_checkable + +from typing_extensions import Self from tcod.cffi import ffi, lib +if TYPE_CHECKING: + from collections.abc import Callable + from types import TracebackType + T = TypeVar("T") logger = logging.getLogger("tcod.sdl") @@ -57,13 +63,93 @@ def __exit__( ) -> bool: if exc_type is None: return False - if _sys.version_info < (3, 8): - return False - _sys.unraisablehook(_UnraisableHookArgs(exc_type, value, traceback, None, self.obj)) # type: ignore[arg-type] + sys.unraisablehook(_UnraisableHookArgs(exc_type, value, traceback, None, self.obj)) return True -@ffi.def_extern() # type: ignore +@runtime_checkable +class PropertyPointer(Protocol): + """Methods for classes which support pointers being set to properties.""" + + @classmethod + def _from_property_pointer(cls, raw_cffi_pointer: Any, /) -> Self: # noqa: ANN401 + """Convert a raw pointer to this class.""" + ... + + def _as_property_pointer(self) -> Any: # noqa: ANN401 + """Return a CFFI pointer for this object.""" + ... + + +class Properties: + """SDL properties interface.""" + + def __init__(self, p: Any | None = None) -> None: # noqa: ANN401 + """Create new properties or use an existing pointer.""" + if p is None: + self.p = ffi.gc( + ffi.cast("SDL_PropertiesID", _check_int(lib.SDL_CreateProperties(), failure=0)), + lib.SDL_DestroyProperties, + ) + else: + self.p = p + + @overload + def __getitem__(self, key: tuple[str, type[bool]], /) -> bool: ... + @overload + def __getitem__(self, key: tuple[str, type[int]], /) -> int: ... + @overload + def __getitem__(self, key: tuple[str, type[float]], /) -> float: ... + @overload + def __getitem__(self, key: tuple[str, type[str]], /) -> str: ... + + def __getitem__(self, key: tuple[str, type[Any]], /) -> Any: + """Get a typed value from this property.""" + key_, type_ = key + name = key_.encode("utf-8") + match lib.SDL_GetPropertyType(self.p, name): + case lib.SDL_PROPERTY_TYPE_STRING: + assert type_ is str + return str(ffi.string(lib.SDL_GetStringProperty(self.p, name, ffi.NULL)), encoding="utf-8") + case lib.SDL_PROPERTY_TYPE_NUMBER: + assert type_ is int + return int(lib.SDL_GetNumberProperty(self.p, name, 0)) + case lib.SDL_PROPERTY_TYPE_FLOAT: + assert type_ is float + return float(lib.SDL_GetFloatProperty(self.p, name, 0.0)) + case lib.SDL_PROPERTY_TYPE_BOOLEAN: + assert type_ is bool + return bool(lib.SDL_GetBooleanProperty(self.p, name, False)) # noqa: FBT003 + case lib.SDL_PROPERTY_TYPE_POINTER: + assert isinstance(type_, PropertyPointer) + return type_._from_property_pointer(lib.SDL_GetPointerProperty(self.p, name, ffi.NULL)) + case lib.SDL_PROPERTY_TYPE_INVALID: + raise KeyError("Invalid type.") # noqa: EM101, TRY003 + case _: + raise AssertionError + + def __setitem__(self, key: tuple[str, type[T]], value: T, /) -> None: + """Assign a property.""" + key_, type_ = key + name = key_.encode("utf-8") + if type_ is str: + assert isinstance(value, str) + lib.SDL_SetStringProperty(self.p, name, value.encode("utf-8")) + elif type_ is int: + assert isinstance(value, int) + lib.SDL_SetNumberProperty(self.p, name, value) + elif type_ is float: + assert isinstance(value, (int, float)) + lib.SDL_SetFloatProperty(self.p, name, value) + elif type_ is bool: + lib.SDL_SetFloatProperty(self.p, name, bool(value)) + else: + assert isinstance(type_, PropertyPointer) + assert isinstance(value, PropertyPointer) + lib.SDL_SetPointerProperty(self.p, name, value._as_property_pointer()) + + +@ffi.def_extern() # type: ignore[untyped-decorator] def _sdl_log_output_function(_userdata: None, category: int, priority: int, message_p: Any) -> None: # noqa: ANN401 """Pass logs sent by SDL to Python's logging system.""" message = str(ffi.string(message_p), encoding="utf-8") @@ -75,14 +161,28 @@ def _get_error() -> str: return str(ffi.string(lib.SDL_GetError()), encoding="utf-8") -def _check(result: int) -> int: +def _check(result: bool, /) -> bool: # noqa: FBT001 """Check if an SDL function returned without errors, and raise an exception if it did.""" - if result < 0: + if not result: raise RuntimeError(_get_error()) return result -def _check_p(result: Any) -> Any: +def _check_int(result: int, /, failure: int) -> int: + """Check if an SDL function returned without errors, and raise an exception if it did.""" + if result == failure: + raise RuntimeError(_get_error()) + return result + + +def _check_float(result: float, /, failure: float) -> float: + """Check if an SDL function returned without errors, and raise an exception if it did.""" + if result == failure: + raise RuntimeError(_get_error()) + return result + + +def _check_p(result: Any) -> Any: # noqa: ANN401 """Check if an SDL function returned NULL, and raise an exception if it did.""" if not result: raise RuntimeError(_get_error()) @@ -90,13 +190,7 @@ def _check_p(result: Any) -> Any: def _compiled_version() -> tuple[int, int, int]: - return int(lib.SDL_MAJOR_VERSION), int(lib.SDL_MINOR_VERSION), int(lib.SDL_PATCHLEVEL) - - -def _linked_version() -> tuple[int, int, int]: - sdl_version = ffi.new("SDL_version*") - lib.SDL_GetVersion(sdl_version) - return int(sdl_version.major), int(sdl_version.minor), int(sdl_version.patch) + return int(lib.SDL_MAJOR_VERSION), int(lib.SDL_MINOR_VERSION), int(lib.SDL_MICRO_VERSION) def _version_at_least(required: tuple[int, int, int]) -> None: @@ -113,13 +207,13 @@ def _required_version(required: tuple[int, int, int]) -> Callable[[T], T]: if required <= _compiled_version(): return lambda x: x - def replacement(*_args: Any, **_kwargs: Any) -> Any: + def replacement(*_args: object, **_kwargs: object) -> NoReturn: msg = f"This feature requires SDL version {required}, but tcod was compiled with version {_compiled_version()}" raise RuntimeError(msg) - return lambda x: replacement # type: ignore[return-value] + return lambda _: replacement # type: ignore[return-value] -lib.SDL_LogSetOutputFunction(lib._sdl_log_output_function, ffi.NULL) +lib.SDL_SetLogOutputFunction(lib._sdl_log_output_function, ffi.NULL) if __debug__: - lib.SDL_LogSetAllPriority(lib.SDL_LOG_PRIORITY_VERBOSE) + lib.SDL_SetLogPriorities(lib.SDL_LOG_PRIORITY_VERBOSE) diff --git a/tcod/sdl/audio.py b/tcod/sdl/audio.py index 8b9addab..568e107e 100644 --- a/tcod/sdl/audio.py +++ b/tcod/sdl/audio.py @@ -1,65 +1,67 @@ -"""SDL2 audio playback and recording tools. +"""SDL audio playback and recording tools. This module includes SDL's low-level audio API and a naive implementation of an SDL mixer. If you have experience with audio mixing then you might be better off writing your own mixer or modifying the existing one which was written using Python/Numpy. This module is designed to integrate with the wider Python ecosystem. -It leaves the loading to sound samples to other libraries like -`SoundFile `_. +It leaves the loading to sound samples to other libraries such as +`soundfile `_. Example:: - # Synchronous audio example using SDL's low-level API. + # Synchronous audio example import time import soundfile # pip install soundfile import tcod.sdl.audio - device = tcod.sdl.audio.open() # Open the default output device. - sound, sample_rate = soundfile.read("example_sound.wav", dtype="float32") # Load an audio sample using SoundFile. - converted = device.convert(sound, sample_rate) # Convert this sample to the format expected by the device. - device.queue_audio(converted) # Play audio synchronously by appending it to the device buffer. + device = tcod.sdl.get_default_playback().open() # Open the default output device - while device.queued_samples: # Wait until device is done playing. - time.sleep(0.001) - -Example:: + # AudioDevice's can be opened again to form a hierarchy + # This can be used to give music and sound effects their own configuration + device_music = device.open() + device_music.gain = 0 # Mute music + device_effects = device.open() + device_effects.gain = 10 ** (-6 / 10) # -6dB - # Asynchronous audio example using BasicMixer. - import time - - import soundfile # pip install soundfile - import tcod.sdl.audio + sound, sample_rate = soundfile.read("example_sound.wav", dtype="float32") # Load an audio sample using SoundFile + stream = device_effects.new_stream(format=sound.dtype, frequency=sample_rate, channels=sound.shape[1]) + stream.queue_audio(sound) # Play audio by appending it to the audio stream + stream.flush() - mixer = tcod.sdl.audio.BasicMixer(tcod.sdl.audio.open()) # Setup BasicMixer with the default audio output. - sound, sample_rate = soundfile.read("example_sound.wav") # Load an audio sample using SoundFile. - sound = mixer.device.convert(sound, sample_rate) # Convert this sample to the format expected by the device. - channel = mixer.play(sound) # Start asynchronous playback, audio is mixed on a separate Python thread. - while channel.busy: # Wait until the sample is done playing. + while stream.queued_samples: # Wait until stream is finished time.sleep(0.001) .. versionadded:: 13.5 """ + from __future__ import annotations +import contextlib import enum import sys import threading -import time -from types import TracebackType -from typing import Any, Callable, Hashable, Iterator +import weakref +from dataclasses import dataclass +from typing import TYPE_CHECKING, Any, Final, Literal, NamedTuple import numpy as np -from numpy.typing import ArrayLike, DTypeLike, NDArray -from typing_extensions import Final, Literal, Self +from typing_extensions import Self, deprecated import tcod.sdl.sys from tcod.cffi import ffi, lib -from tcod.sdl._internal import _check, _get_error, _ProtectedContext +from tcod.sdl._internal import _check, _check_float, _check_int, _check_p + +if TYPE_CHECKING: + import builtins + from collections.abc import Callable, Hashable, Iterable, Iterator + from types import TracebackType + + from numpy.typing import ArrayLike, DTypeLike, NDArray -def _get_format(format: DTypeLike) -> int: +def _get_format(format: DTypeLike, /) -> int: # noqa: A002 """Return a SDL_AudioFormat bit-field from a NumPy dtype.""" dt: Any = np.dtype(format) assert dt.fields is None @@ -76,33 +78,33 @@ def _get_format(format: DTypeLike) -> int: return int( bitsize - | (lib.SDL_AUDIO_MASK_DATATYPE * is_float) - | (lib.SDL_AUDIO_MASK_ENDIAN * (byteorder == ">")) + | (lib.SDL_AUDIO_MASK_FLOAT * is_float) + | (lib.SDL_AUDIO_MASK_BIG_ENDIAN * (byteorder == ">")) | (lib.SDL_AUDIO_MASK_SIGNED * is_signed) ) -def _dtype_from_format(format: int) -> np.dtype[Any]: +def _dtype_from_format(format: int, /) -> np.dtype[Any]: # noqa: A002 """Return a dtype from a SDL_AudioFormat. - >>> _dtype_from_format(tcod.lib.AUDIO_F32LSB) + >>> _dtype_from_format(tcod.lib.SDL_AUDIO_F32LE) dtype('float32') - >>> _dtype_from_format(tcod.lib.AUDIO_F32MSB) + >>> _dtype_from_format(tcod.lib.SDL_AUDIO_F32BE) dtype('>f4') - >>> _dtype_from_format(tcod.lib.AUDIO_S16LSB) + >>> _dtype_from_format(tcod.lib.SDL_AUDIO_S16LE) dtype('int16') - >>> _dtype_from_format(tcod.lib.AUDIO_S16MSB) + >>> _dtype_from_format(tcod.lib.SDL_AUDIO_S16BE) dtype('>i2') - >>> _dtype_from_format(tcod.lib.AUDIO_U16LSB) - dtype('uint16') - >>> _dtype_from_format(tcod.lib.AUDIO_U16MSB) - dtype('>u2') + >>> _dtype_from_format(tcod.lib.SDL_AUDIO_S8) + dtype('int8') + >>> _dtype_from_format(tcod.lib.SDL_AUDIO_U8) + dtype('uint8') """ bitsize = format & lib.SDL_AUDIO_MASK_BITSIZE assert bitsize % 8 == 0 byte_size = bitsize // 8 - byteorder = ">" if format & lib.SDL_AUDIO_MASK_ENDIAN else "<" - if format & lib.SDL_AUDIO_MASK_DATATYPE: + byteorder = ">" if format & lib.SDL_AUDIO_MASK_BIG_ENDIAN else "<" + if format & lib.SDL_AUDIO_MASK_FLOAT: kind = "f" elif format & lib.SDL_AUDIO_MASK_SIGNED: kind = "i" @@ -111,12 +113,34 @@ def _dtype_from_format(format: int) -> np.dtype[Any]: return np.dtype(f"{byteorder}{kind}{byte_size}") +def _silence_value_for_format(dtype: DTypeLike, /) -> int: + """Return the silence value for the given dtype format.""" + return int(lib.SDL_GetSilenceValueForFormat(_get_format(dtype))) + + +class _AudioSpec(NamedTuple): + """Named tuple for `SDL_AudioSpec`.""" + + format: int + channels: int + frequency: int + + @classmethod + def from_c(cls, c_spec_p: Any) -> Self: # noqa: ANN401 + return cls(int(c_spec_p.format), int(c_spec_p.channels), int(c_spec_p.freq)) + + @property + def _dtype(self) -> np.dtype[Any]: + return _dtype_from_format(self.format) + + def convert_audio( in_sound: ArrayLike, in_rate: int, *, out_rate: int, out_format: DTypeLike, out_channels: int -) -> NDArray[Any]: +) -> NDArray[np.number]: """Convert an audio sample into a format supported by this device. - Returns the converted array. This might be a reference to the input array if no conversion was needed. + Returns the converted array in the shape `(sample, channel)`. + This will reference the input array data if no conversion was needed. Args: in_sound: The input ArrayLike sound sample. Input format and channels are derived from the array. @@ -125,6 +149,14 @@ def convert_audio( out_format: The output format of the converted array. out_channels: The number of audio channels of the output array. + Examples:: + + >>> tcod.sdl.audio.convert_audio(np.zeros(5), 44100, out_rate=44100, out_format=np.uint8, out_channels=1).T + array([[128, 128, 128, 128, 128]], dtype=uint8) + >>> tcod.sdl.audio.convert_audio(np.zeros(3), 22050, out_rate=44100, out_format=np.int8, out_channels=2).T + array([[0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0]], dtype=int8) + .. versionadded:: 13.6 .. versionchanged:: 16.0 @@ -136,22 +168,33 @@ def convert_audio( in_array: NDArray[Any] = np.asarray(in_sound) if len(in_array.shape) == 1: in_array = in_array[:, np.newaxis] - if len(in_array.shape) != 2: + elif len(in_array.shape) != 2: # noqa: PLR2004 msg = f"Expected a 1 or 2 ndim input, got {in_array.shape} instead." raise TypeError(msg) - cvt = ffi.new("SDL_AudioCVT*") - in_channels = in_array.shape[1] - in_format = _get_format(in_array.dtype) - out_sdl_format = _get_format(out_format) + in_spec = _AudioSpec(format=_get_format(in_array.dtype), channels=in_array.shape[1], frequency=in_rate) + out_spec = _AudioSpec(format=_get_format(out_format), channels=out_channels, frequency=out_rate) + if in_spec == out_spec: + return in_array # No conversion needed + + out_buffer = ffi.new("uint8_t**") + out_length = ffi.new("int*") try: - if ( - _check(lib.SDL_BuildAudioCVT(cvt, in_format, in_channels, in_rate, out_sdl_format, out_channels, out_rate)) - == 0 - ): - return in_array # No conversion needed. + _check( + lib.SDL_ConvertAudioSamples( + [in_spec], + ffi.from_buffer("const uint8_t*", in_array), + len(in_array) * in_array.itemsize, + [out_spec], + out_buffer, + out_length, + ) + ) + return ( + np.frombuffer(ffi.buffer(out_buffer[0], out_length[0]), dtype=out_format).reshape(-1, out_channels).copy() + ) except RuntimeError as exc: if ( # SDL now only supports float32, but later versions may add more support for more formats. - exc.args[0] == "Invalid source format" + exc.args[0] == "Parameter 'src_spec->format' is invalid" and np.issubdtype(in_array.dtype, np.floating) and in_array.dtype != np.float32 ): @@ -163,68 +206,82 @@ def convert_audio( out_channels=out_channels, ) raise - # Upload to the SDL_AudioCVT buffer. - cvt.len = in_array.itemsize * in_array.size - out_buffer = cvt.buf = ffi.new("uint8_t[]", cvt.len * cvt.len_mult) - np.frombuffer(ffi.buffer(out_buffer[0 : cvt.len]), dtype=in_array.dtype).reshape(in_array.shape)[:] = in_array - - _check(lib.SDL_ConvertAudio(cvt)) - out_array: NDArray[Any] = ( - np.frombuffer(ffi.buffer(out_buffer[0 : cvt.len_cvt]), dtype=out_format).reshape(-1, out_channels).copy() - ) - return out_array + finally: + lib.SDL_free(out_buffer[0]) class AudioDevice: """An SDL audio device. - Open new audio devices using :any:`tcod.sdl.audio.open`. + Example:: - When you use this object directly the audio passed to :any:`queue_audio` is always played synchronously. - For more typical asynchronous audio you should pass an AudioDevice to :any:`BasicMixer`. + device = tcod.sdl.audio.get_default_playback().open() # Open a common audio device .. versionchanged:: 16.0 Can now be used as a context which will close the device on exit. + + .. versionchanged:: 19.0 + Removed `spec` and `callback` attribute. + + `queued_samples`, `queue_audio`, and `dequeue_audio` moved to :any:`AudioStream` class. + """ + __slots__ = ( + "__weakref__", + "_device_id", + "buffer_bytes", + "buffer_samples", + "channels", + "device_id", + "format", + "frequency", + "is_capture", + "is_physical", + "silence", + ) + def __init__( self, - device_id: int, - capture: bool, - spec: Any, # SDL_AudioSpec* + device_id: Any, # noqa: ANN401 + /, ) -> None: + """Initialize the class from a raw `SDL_AudioDeviceID`.""" assert device_id >= 0 - assert ffi.typeof(spec) is ffi.typeof("SDL_AudioSpec*") - assert spec - self.device_id: Final[int] = device_id + assert ffi.typeof(device_id) is ffi.typeof("SDL_AudioDeviceID"), ffi.typeof(device_id) + spec = ffi.new("SDL_AudioSpec*") + samples = ffi.new("int*") + _check(lib.SDL_GetAudioDeviceFormat(device_id, spec, samples)) + self._device_id: object = device_id + self.device_id: Final[int] = int(device_id) """The SDL device identifier used for SDL C functions.""" - self.spec: Final[Any] = spec - """The SDL_AudioSpec as a CFFI object.""" self.frequency: Final[int] = spec.freq """The audio device sound frequency.""" - self.is_capture: Final[bool] = capture + self.is_capture: Final[bool] = bool(not lib.SDL_IsAudioDevicePlayback(device_id)) """True if this is a recording device instead of an output device.""" self.format: Final[np.dtype[Any]] = _dtype_from_format(spec.format) """The format used for audio samples with this device.""" self.channels: Final[int] = int(spec.channels) """The number of audio channels for this device.""" - self.silence: float = int(spec.silence) + self.silence: float = int(lib.SDL_GetSilenceValueForFormat(spec.format)) """The value of silence, according to SDL.""" - self.buffer_samples: Final[int] = int(spec.samples) + self.buffer_samples: Final[int] = int(samples[0]) """The size of the audio buffer in samples.""" - self.buffer_bytes: Final[int] = int(spec.size) + self.buffer_bytes: Final[int] = int(self.format.itemsize * self.channels * self.buffer_samples) """The size of the audio buffer in bytes.""" - self._handle: Any | None = None - self._callback: Callable[[AudioDevice, NDArray[Any]], None] = self.__default_callback + self.is_physical: Final[bool] = bool(lib.SDL_IsAudioDevicePhysical(device_id)) + """True of this is a physical device, or False if this is a logical device. + + .. versionadded:: 19.0 + """ def __repr__(self) -> str: """Return a representation of this device.""" - if self.stopped: - return f"<{self.__class__.__name__}() stopped=True>" items = [ f"{self.__class__.__name__}(device_id={self.device_id})", f"frequency={self.frequency}", f"is_capture={self.is_capture}", + f"is_physical={self.is_physical}", f"format={self.format}", f"channels={self.channels}", f"buffer_samples={self.buffer_samples}", @@ -234,24 +291,56 @@ def __repr__(self) -> str: if self.silence: items.append(f"silence={self.silence}") - if self._handle is not None: - items.append(f"callback={self._callback}") return f"""<{" ".join(items)}>""" @property - def callback(self) -> Callable[[AudioDevice, NDArray[Any]], None]: - """If the device was opened with a callback enabled, then you may get or set the callback with this attribute.""" - if self._handle is None: - msg = "This AudioDevice was opened without a callback." - raise TypeError(msg) - return self._callback + def name(self) -> str: + """Name of the device. - @callback.setter - def callback(self, new_callback: Callable[[AudioDevice, NDArray[Any]], None]) -> None: - if self._handle is None: - msg = "This AudioDevice was opened without a callback." - raise TypeError(msg) - self._callback = new_callback + .. versionadded:: 19.0 + """ + return str(ffi.string(_check_p(lib.SDL_GetAudioDeviceName(self.device_id))), encoding="utf-8") + + @property + def gain(self) -> float: + """Get or set the logical audio device gain. + + Default is 1.0 but can be set higher or zero. + + .. versionadded:: 19.0 + """ + return _check_float(lib.SDL_GetAudioDeviceGain(self.device_id), failure=-1.0) + + @gain.setter + def gain(self, value: float, /) -> None: + _check(lib.SDL_SetAudioDeviceGain(self.device_id, value)) + + def open( + self, + format: DTypeLike | None = None, # noqa: A002 + channels: int | None = None, + frequency: int | None = None, + ) -> Self: + """Open a new logical audio device for this device. + + .. versionadded:: 19.0 + + .. seealso:: + https://wiki.libsdl.org/SDL3/SDL_OpenAudioDevice + """ + new_spec = _AudioSpec( + format=_get_format(format if format is not None else self.format), + channels=channels if channels is not None else self.channels, + frequency=frequency if frequency is not None else self.frequency, + ) + return self.__class__( + ffi.gc( + ffi.cast( + "SDL_AudioDeviceID", _check_int(lib.SDL_OpenAudioDevice(self.device_id, (new_spec,)), failure=0) + ), + lib.SDL_CloseAudioDevice, + ) + ) @property def _sample_size(self) -> int: @@ -259,20 +348,26 @@ def _sample_size(self) -> int: return self.format.itemsize * self.channels @property + @deprecated("This is no longer used by the SDL3 API") def stopped(self) -> bool: - """Is True if the device has failed or was closed.""" - if not hasattr(self, "device_id"): - return True - return bool(lib.SDL_GetAudioDeviceStatus(self.device_id) == lib.SDL_AUDIO_STOPPED) + """Is True if the device has failed or was closed. + + .. deprecated:: 19.0 + No longer used by the SDL3 API. + """ + return bool(not hasattr(self, "device_id")) @property def paused(self) -> bool: """Get or set the device paused state.""" - return bool(lib.SDL_GetAudioDeviceStatus(self.device_id) != lib.SDL_AUDIO_PLAYING) + return bool(lib.SDL_AudioDevicePaused(self.device_id)) @paused.setter def paused(self, value: bool) -> None: - lib.SDL_PauseAudioDevice(self.device_id, value) + if value: + _check(lib.SDL_PauseAudioDevice(self.device_id)) + else: + _check(lib.SDL_ResumeAudioDevice(self.device_id)) def _verify_array_format(self, samples: NDArray[Any]) -> NDArray[Any]: if samples.dtype != self.format: @@ -280,15 +375,15 @@ def _verify_array_format(self, samples: NDArray[Any]) -> NDArray[Any]: raise TypeError(msg) return samples - def _convert_array(self, samples_: ArrayLike) -> NDArray[Any]: + def _convert_array(self, samples_: ArrayLike) -> NDArray[np.number]: if isinstance(samples_, np.ndarray): samples_ = self._verify_array_format(samples_) - samples: NDArray[Any] = np.asarray(samples_, dtype=self.format) - if len(samples.shape) < 2: + samples: NDArray[np.number] = np.asarray(samples_, dtype=self.format) + if len(samples.shape) < 2: # noqa: PLR2004 samples = samples[:, np.newaxis] return np.ascontiguousarray(np.broadcast_to(samples, (samples.shape[0], self.channels)), dtype=self.format) - def convert(self, sound: ArrayLike, rate: int | None = None) -> NDArray[Any]: + def convert(self, sound: ArrayLike, rate: int | None = None) -> NDArray[np.number]: """Convert an audio sample into a format supported by this device. Returns the converted array. This might be a reference to the input array if no conversion was needed. @@ -314,50 +409,28 @@ def convert(self, sound: ArrayLike, rate: int | None = None) -> NDArray[Any]: out_rate=self.frequency, ) - @property - def _queued_bytes(self) -> int: - """The current amount of bytes remaining in the audio queue.""" - return int(lib.SDL_GetQueuedAudioSize(self.device_id)) - - @property - def queued_samples(self) -> int: - """The current amount of samples remaining in the audio queue.""" - return self._queued_bytes // self._sample_size - - def queue_audio(self, samples: ArrayLike) -> None: - """Append audio samples to the audio data queue.""" - assert not self.is_capture - samples = self._convert_array(samples) - buffer = ffi.from_buffer(samples) - lib.SDL_QueueAudio(self.device_id, buffer, len(buffer)) - - def dequeue_audio(self) -> NDArray[Any]: - """Return the audio buffer from a capture stream.""" - assert self.is_capture - out_samples = self._queued_bytes // self._sample_size - out = np.empty((out_samples, self.channels), self.format) - buffer = ffi.from_buffer(out) - bytes_returned = lib.SDL_DequeueAudio(self.device_id, buffer, len(buffer)) - samples_returned = bytes_returned // self._sample_size - assert samples_returned == out_samples - return out - - def __del__(self) -> None: - self.close() - def close(self) -> None: """Close this audio device. Using this object after it has been closed is invalid.""" if not hasattr(self, "device_id"): return - lib.SDL_CloseAudioDevice(self.device_id) - del self.device_id + ffi.release(self._device_id) + del self._device_id + @deprecated("Use contextlib.closing if you want to close this device after a context.") def __enter__(self) -> Self: - """Return self and enter a managed context.""" + """Return self and enter a managed context. + + .. deprecated:: 19.0 + Use :func:`contextlib.closing` if you want to close this device after a context. + """ return self def __exit__( - self, type: type[BaseException] | None, value: BaseException | None, traceback: TracebackType | None + self, + _type: builtins.type[BaseException] | None, # Explicit builtins prefix to disambiguate Sphinx cross-reference + _value: BaseException | None, + _traceback: TracebackType | None, + /, ) -> None: """Close the device when exiting the context.""" self.close() @@ -366,6 +439,264 @@ def __exit__( def __default_callback(device: AudioDevice, stream: NDArray[Any]) -> None: stream[...] = device.silence + def new_stream( + self, + format: DTypeLike, # noqa: A002 + channels: int, + frequency: int, + ) -> AudioStream: + """Create, bind, and return a new :any:`AudioStream` for this device. + + .. versionadded:: 19.0 + """ + new_stream = AudioStream.new(format=format, channels=channels, frequency=frequency) + self.bind((new_stream,)) + return new_stream + + def bind(self, streams: Iterable[AudioStream], /) -> None: + """Bind one or more :any:`AudioStream`'s to this device. + + .. seealso:: + https://wiki.libsdl.org/SDL3/SDL_BindAudioStreams + """ + streams = list(streams) + _check(lib.SDL_BindAudioStreams(self.device_id, [s._stream_p for s in streams], len(streams))) + + +@dataclass(frozen=True) +class AudioStreamCallbackData: + """Data provided to AudioStream callbacks. + + .. versionadded:: 19.0 + """ + + additional_bytes: int + """Amount of bytes needed to fulfill the request of the caller. Can be zero.""" + additional_samples: int + """Amount of samples needed to fulfill the request of the caller. Can be zero.""" + total_bytes: int + """Amount of bytes requested or provided by the caller.""" + total_samples: int + """Amount of samples requested or provided by the caller.""" + + +_audio_stream_get_callbacks: dict[AudioStream, Callable[[AudioStream, AudioStreamCallbackData], Any]] = {} +_audio_stream_put_callbacks: dict[AudioStream, Callable[[AudioStream, AudioStreamCallbackData], Any]] = {} + +_audio_stream_registry: weakref.WeakValueDictionary[int, AudioStream] = weakref.WeakValueDictionary() + + +class AudioStream: + """An SDL audio stream. + + This class is commonly created with :any:`AudioDevice.new_stream` which creates a new stream bound to the device. + + .. versionadded:: 19.0 + """ + + __slots__ = ("__weakref__", "_stream_p") + + _stream_p: Any + + def __new__( # noqa: PYI034 + cls, + stream_p: Any, # noqa: ANN401 + /, + ) -> AudioStream: + """Return an AudioStream for the provided `SDL_AudioStream*` C pointer.""" + assert ffi.typeof(stream_p) is ffi.typeof("SDL_AudioStream*"), ffi.typeof(stream_p) + stream_int = int(ffi.cast("intptr_t", stream_p)) + self = super().__new__(cls) + self._stream_p = stream_p + return _audio_stream_registry.setdefault(stream_int, self) + + @classmethod + def new( # noqa: PLR0913 + cls, + format: DTypeLike, # noqa: A002 + channels: int, + frequency: int, + out_format: DTypeLike | None = None, + out_channels: int | None = None, + out_frequency: int | None = None, + ) -> Self: + """Create a new unbound AudioStream.""" + in_spec = _AudioSpec(format=_get_format(format), channels=channels, frequency=frequency) + out_spec = _AudioSpec( + format=_get_format(out_format) if out_format is not None else in_spec.format, + channels=out_channels if out_channels is not None else channels, + frequency=out_frequency if out_frequency is not None else frequency, + ) + return cls(ffi.gc(_check_p(lib.SDL_CreateAudioStream((in_spec,), (out_spec,))), lib.SDL_DestroyAudioStream)) + + def close(self) -> None: + """Close this AudioStream and release its resources.""" + if not hasattr(self, "_stream_p"): + return + self.getter_callback = None + self.putter_callback = None + ffi.release(self._stream_p) + + def unbind(self) -> None: + """Unbind this stream from its currently bound device.""" + lib.SDL_UnbindAudioStream(self._stream_p) + + @property + @contextlib.contextmanager + def _lock(self) -> Iterator[None]: + """Lock context for this stream.""" + try: + lib.SDL_LockAudioStream(self._stream_p) + yield + finally: + lib.SDL_UnlockAudioStream(self._stream_p) + + @property + def _src_spec(self) -> _AudioSpec: + c_spec = ffi.new("SDL_AudioSpec*") + _check(lib.SDL_GetAudioStreamFormat(self._stream_p, c_spec, ffi.NULL)) + return _AudioSpec.from_c(c_spec) + + @property + def _src_sample_size(self) -> int: + spec = self._src_spec + return spec._dtype.itemsize * spec.channels + + @property + def _dst_sample_size(self) -> int: + spec = self._dst_spec + return spec._dtype.itemsize * spec.channels + + @property + def _dst_spec(self) -> _AudioSpec: + c_spec = ffi.new("SDL_AudioSpec*") + _check(lib.SDL_GetAudioStreamFormat(self._stream_p, ffi.NULL, c_spec)) + return _AudioSpec.from_c(c_spec) + + @property + def queued_bytes(self) -> int: + """The current amount of bytes remaining in the audio queue.""" + return _check_int(lib.SDL_GetAudioStreamQueued(self._stream_p), failure=-1) + + @property + def queued_samples(self) -> int: + """The estimated amount of samples remaining in the audio queue.""" + return self.queued_bytes // self._src_sample_size + + @property + def available_bytes(self) -> int: + """The current amount of converted data in this audio stream.""" + return _check_int(lib.SDL_GetAudioStreamAvailable(self._stream_p), failure=-1) + + @property + def available_samples(self) -> int: + """The current amount of converted samples in this audio stream.""" + return self.available_bytes // self._dst_sample_size + + def queue_audio(self, samples: ArrayLike) -> None: + """Append audio samples to the audio data queue.""" + with self._lock: + src_spec = self._src_spec + src_format = _dtype_from_format(src_spec.format) + if isinstance(samples, np.ndarray) and samples.dtype != src_format: + msg = f"Expected an array of dtype {src_format}, got {samples.dtype} instead." + raise TypeError(msg) + samples = np.asarray(samples, dtype=src_format) + if len(samples.shape) < 2: # noqa: PLR2004 + samples = samples[:, np.newaxis] + samples = np.ascontiguousarray( + np.broadcast_to(samples, (samples.shape[0], src_spec.channels)), dtype=src_format + ) + buffer = ffi.from_buffer(samples) + _check(lib.SDL_PutAudioStreamData(self._stream_p, buffer, len(buffer))) + + def flush(self) -> None: + """Ensure all queued data is available. + + This may queue silence to the end of the stream. + + .. seealso:: + https://wiki.libsdl.org/SDL3/SDL_FlushAudioStream + """ + _check(lib.SDL_FlushAudioStream(self._stream_p)) + + def dequeue_audio(self) -> NDArray[Any]: + """Return the converted output audio from this stream.""" + with self._lock: + dst_spec = self._dst_spec + out_samples = self.available_samples + out = np.empty((out_samples, dst_spec.channels), _dtype_from_format(dst_spec.format)) + buffer = ffi.from_buffer(out) + bytes_returned = _check_int(lib.SDL_GetAudioStreamData(self._stream_p, buffer, len(buffer)), failure=-1) + samples_returned = bytes_returned // self._dst_sample_size + return out[:samples_returned] + + @property + def gain(self) -> float: + """Get or set the audio stream gain. + + Default is 1.0 but can be set higher or zero. + """ + return _check_float(lib.SDL_GetAudioStreamGain(self._stream_p), failure=-1.0) + + @gain.setter + def gain(self, value: float, /) -> None: + _check(lib.SDL_SetAudioStreamGain(self._stream_p, value)) + + @property + def frequency_ratio(self) -> float: + """Get or set the frequency ratio, affecting the speed and pitch of the stream. + + Higher values play the audio faster. + + Default is 1.0. + """ + return _check_float(lib.SDL_GetAudioStreamFrequencyRatio(self._stream_p), failure=-1.0) + + @frequency_ratio.setter + def frequency_ratio(self, value: float, /) -> None: + _check(lib.SDL_SetAudioStreamFrequencyRatio(self._stream_p, value)) + + @property + def getter_callback(self) -> Callable[[AudioStream, AudioStreamCallbackData], Any] | None: + """Get or assign the stream get-callback for this stream. + + .. seealso:: + https://wiki.libsdl.org/SDL3/SDL_SetAudioStreamGetCallback + """ + return _audio_stream_get_callbacks.get(self) + + @getter_callback.setter + def getter_callback(self, callback: Callable[[AudioStream, AudioStreamCallbackData], Any] | None, /) -> None: + if callback is None: + _check(lib.SDL_SetAudioStreamGetCallback(self._stream_p, ffi.NULL, ffi.NULL)) + _audio_stream_get_callbacks.pop(self, None) + else: + _audio_stream_get_callbacks[self] = callback + _check( + lib.SDL_SetAudioStreamGetCallback(self._stream_p, lib._sdl_audio_stream_callback, ffi.cast("void*", 0)) + ) + + @property + def putter_callback(self) -> Callable[[AudioStream, AudioStreamCallbackData], Any] | None: + """Get or assign the stream put-callback for this stream. + + .. seealso:: + https://wiki.libsdl.org/SDL3/SDL_SetAudioStreamPutCallback + """ + return _audio_stream_put_callbacks.get(self) + + @putter_callback.setter + def putter_callback(self, callback: Callable[[AudioStream, AudioStreamCallbackData], Any] | None, /) -> None: + if callback is None: + _check(lib.SDL_SetAudioStreamPutCallback(self._stream_p, ffi.NULL, ffi.NULL)) + _audio_stream_put_callbacks.pop(self, None) + else: + _audio_stream_put_callbacks[self] = callback + _check( + lib.SDL_SetAudioStreamPutCallback(self._stream_p, lib._sdl_audio_stream_callback, ffi.cast("void*", 1)) + ) + class _LoopSoundFunc: def __init__(self, sound: NDArray[Any], loops: int, on_end: Callable[[Channel], None] | None) -> None: @@ -393,6 +724,7 @@ class Channel: """The :any:`BasicMixer` is channel belongs to.""" def __init__(self) -> None: + """Initialize this channel with generic attributes.""" self._lock = threading.RLock() self.volume: float | tuple[float, ...] = 1.0 self.sound_queue: list[NDArray[Any]] = [] @@ -466,37 +798,53 @@ def stop(self) -> None: self.fadeout(0.0005) -class BasicMixer(threading.Thread): +@deprecated( + "Changes in the SDL3 API have made this classes usefulness questionable." + "\nThis class should be replaced with custom streams." +) +class BasicMixer: """An SDL sound mixer implemented in Python and Numpy. + Example:: + + import time + + import soundfile # pip install soundfile + import tcod.sdl.audio + + device = tcod.sdl.audio.get_default_playback().open() + mixer = tcod.sdl.audio.BasicMixer(device) # Setup BasicMixer with the default audio output + sound, sample_rate = soundfile.read("example_sound.wav") # Load an audio sample using SoundFile + sound = mixer.device.convert(sound, sample_rate) # Convert this sample to the format expected by the device + channel = mixer.play(sound) # Start asynchronous playback, audio is mixed on a separate Python thread + while channel.busy: # Wait until the sample is done playing + time.sleep(0.001) + + .. versionadded:: 13.6 + + .. versionchanged:: 19.0 + Added `frequency` and `channels` parameters. + + .. deprecated:: 19.0 + Changes in the SDL3 API have made this classes usefulness questionable. + This class should be replaced with custom streams. """ - def __init__(self, device: AudioDevice) -> None: + def __init__(self, device: AudioDevice, *, frequency: int | None = None, channels: int | None = None) -> None: + """Initialize this mixer using the provided device.""" self.channels: dict[Hashable, Channel] = {} - assert device.format == np.float32 - super().__init__(daemon=True) self.device = device """The :any:`AudioDevice`""" + self._frequency = frequency if frequency is not None else device.frequency + self._channels = channels if channels is not None else device.channels self._lock = threading.RLock() - self._running = True - self.start() - - def run(self) -> None: - buffer = np.full( - (self.device.buffer_samples, self.device.channels), self.device.silence, dtype=self.device.format - ) - while self._running: - if self.device._queued_bytes > 0: - time.sleep(0.001) - continue - self._on_stream(buffer) - self.device.queue_audio(buffer) - buffer[:] = self.device.silence + self._stream = device.new_stream(format=np.float32, frequency=self._frequency, channels=self._channels) + self._stream.getter_callback = self._on_stream def close(self) -> None: """Shutdown this mixer, all playing audio will be abruptly stopped.""" - self._running = False + self._stream.close() def get_channel(self, key: Hashable) -> Channel: """Return a channel tied to with the given key. @@ -549,47 +897,100 @@ def stop(self) -> None: for channel in self.channels.values(): channel.stop() - def _on_stream(self, stream: NDArray[Any]) -> None: + def _on_stream(self, audio_stream: AudioStream, data: AudioStreamCallbackData) -> None: """Called to fill the audio buffer.""" + if data.additional_samples <= 0: + return + stream: NDArray[np.float32] = np.zeros((data.additional_samples, self._channels), dtype=np.float32) with self._lock: for channel in list(self.channels.values()): channel._on_mix(stream) + audio_stream.queue_audio(stream) + +@ffi.def_extern() # type: ignore[untyped-decorator] +def _sdl_audio_stream_callback(userdata: Any, stream_p: Any, additional_amount: int, total_amount: int, /) -> None: # noqa: ANN401 + """Handle audio device callbacks.""" + stream = AudioStream(stream_p) + is_put_callback = bool(userdata) + callback = (_audio_stream_put_callbacks if is_put_callback else _audio_stream_get_callbacks).get(stream) + if callback is None: + return + sample_size = stream._dst_sample_size if is_put_callback else stream._src_sample_size + callback( + stream, + AudioStreamCallbackData( + additional_bytes=additional_amount, + additional_samples=additional_amount // sample_size, + total_bytes=total_amount, + total_samples=total_amount // sample_size, + ), + ) -class _AudioCallbackUserdata: - device: AudioDevice +def get_devices() -> dict[str, AudioDevice]: + """Iterate over the available audio output devices. + + .. versionchanged:: 19.0 + Now returns a dictionary of :any:`AudioDevice`. + """ + tcod.sdl.sys.init(tcod.sdl.sys.Subsystem.AUDIO) + count = ffi.new("int[1]") + devices_array = ffi.gc(lib.SDL_GetAudioPlaybackDevices(count), lib.SDL_free) + return { + device.name: device + for device in (AudioDevice(ffi.cast("SDL_AudioDeviceID", p)) for p in devices_array[0 : count[0]]) + } -@ffi.def_extern() # type: ignore -def _sdl_audio_callback(userdata: Any, stream: Any, length: int) -> None: # noqa: ANN401 - """Handle audio device callbacks.""" - data: _AudioCallbackUserdata = ffi.from_handle(userdata) - device = data.device - buffer = np.frombuffer(ffi.buffer(stream, length), dtype=device.format).reshape(-1, device.channels) - with _ProtectedContext(device): - device._callback(device, buffer) +def get_capture_devices() -> dict[str, AudioDevice]: + """Iterate over the available audio capture devices. -def _get_devices(capture: bool) -> Iterator[str]: - """Get audio devices from SDL_GetAudioDeviceName.""" - with tcod.sdl.sys._ScopeInit(tcod.sdl.sys.Subsystem.AUDIO): - device_count = lib.SDL_GetNumAudioDevices(capture) - for i in range(device_count): - yield str(ffi.string(lib.SDL_GetAudioDeviceName(i, capture)), encoding="utf-8") + .. versionchanged:: 19.0 + Now returns a dictionary of :any:`AudioDevice`. + """ + tcod.sdl.sys.init(tcod.sdl.sys.Subsystem.AUDIO) + count = ffi.new("int[1]") + devices_array = ffi.gc(lib.SDL_GetAudioRecordingDevices(count), lib.SDL_free) + return { + device.name: device + for device in (AudioDevice(ffi.cast("SDL_AudioDeviceID", p)) for p in devices_array[0 : count[0]]) + } + + +def get_default_playback() -> AudioDevice: + """Return the default playback device. + + Example:: + + playback_device = tcod.sdl.audio.get_default_playback().open() + + .. versionadded:: 19.0 + """ + tcod.sdl.sys.init(tcod.sdl.sys.Subsystem.AUDIO) + return AudioDevice(ffi.cast("SDL_AudioDeviceID", lib.SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK)) -def get_devices() -> Iterator[str]: - """Iterate over the available audio output devices.""" - yield from _get_devices(capture=False) +def get_default_recording() -> AudioDevice: + """Return the default recording device. + Example:: -def get_capture_devices() -> Iterator[str]: - """Iterate over the available audio capture devices.""" - yield from _get_devices(capture=True) + recording_device = tcod.sdl.audio.get_default_recording().open() + + .. versionadded:: 19.0 + """ + tcod.sdl.sys.init(tcod.sdl.sys.Subsystem.AUDIO) + return AudioDevice(ffi.cast("SDL_AudioDeviceID", lib.SDL_AUDIO_DEVICE_DEFAULT_RECORDING)) +@deprecated("This is no longer used", category=FutureWarning) class AllowedChanges(enum.IntFlag): - """Which parameters are allowed to be changed when the values given are not supported.""" + """Which parameters are allowed to be changed when the values given are not supported. + + .. deprecated:: 19.0 + This is no longer used. + """ NONE = 0 """""" @@ -605,15 +1006,18 @@ class AllowedChanges(enum.IntFlag): """""" -def open( +@deprecated( + "This is an outdated method.\nUse 'tcod.sdl.audio.get_default_playback().open()' instead.", category=FutureWarning +) +def open( # noqa: A001, PLR0913 name: str | None = None, - capture: bool = False, + capture: bool = False, # noqa: FBT001, FBT002 *, frequency: int = 44100, - format: DTypeLike = np.float32, + format: DTypeLike = np.float32, # noqa: A002 channels: int = 2, - samples: int = 0, - allowed_changes: AllowedChanges = AllowedChanges.NONE, + samples: int = 0, # noqa: ARG001 + allowed_changes: AllowedChanges = AllowedChanges.NONE, # noqa: ARG001 paused: bool = False, callback: None | Literal[True] | Callable[[AudioDevice, NDArray[Any]], None] = None, ) -> AudioDevice: @@ -625,65 +1029,47 @@ def open( frequency: The desired sample rate to open the device with. format: The data format to use for samples as a NumPy dtype. channels: The number of speakers for the device. 1, 2, 4, or 6 are typical options. - samples: The desired size of the audio buffer, must be a power of two. - allowed_changes: - By default if the hardware does not support the desired format than SDL will transparently convert between - formats for you. - Otherwise you can specify which parameters are allowed to be changed to fit the hardware better. + samples: This parameter is ignored. + allowed_changes: This parameter is ignored. paused: If True then the device will begin in a paused state. It can then be unpaused by assigning False to :any:`AudioDevice.paused`. - callback: - If None then this device will be opened in push mode and you'll have to use :any:`AudioDevice.queue_audio` - to send audio data or :any:`AudioDevice.dequeue_audio` to receive it. - If a callback is given then you can change it later, but you can not enable or disable the callback on an - opened device. - If True then a default callback which plays silence will be used, this is useful if you need the audio - device before your callback is ready. + callback: An optional callback to use, this is deprecated. If a callback is given then it will be called with the `AudioDevice` and a Numpy buffer of the data stream. This callback will be run on a separate thread. - Exceptions not handled by the callback become unraiseable and will be handled by :any:`sys.unraisablehook`. - .. seealso:: - https://wiki.libsdl.org/SDL_AudioSpec - https://wiki.libsdl.org/SDL_OpenAudioDevice + .. versionchanged:: 19.0 + SDL3 returns audio devices differently, exact formatting is set with :any:`AudioDevice.new_stream` instead. + `samples` and `allowed_changes` are ignored. + + .. deprecated:: 19.0 + This is an outdated method. + Use :any:`AudioDevice.open` instead, for example: + ``tcod.sdl.audio.get_default_playback().open()`` """ tcod.sdl.sys.init(tcod.sdl.sys.Subsystem.AUDIO) - desired = ffi.new( - "SDL_AudioSpec*", - { - "freq": frequency, - "format": _get_format(format), - "channels": channels, - "samples": samples, - "callback": ffi.NULL, - "userdata": ffi.NULL, - }, - ) - callback_data = _AudioCallbackUserdata() - if callback is not None: - handle = ffi.new_handle(callback_data) - desired.callback = lib._sdl_audio_callback - desired.userdata = handle + if name is None: + device = get_default_playback() if not capture else get_default_recording() else: - handle = None - - obtained = ffi.new("SDL_AudioSpec*") - device_id: int = lib.SDL_OpenAudioDevice( - ffi.NULL if name is None else name.encode("utf-8"), - capture, - desired, - obtained, - allowed_changes, - ) - assert device_id >= 0, _get_error() - device = AudioDevice(device_id, capture, obtained) - if callback is not None: - callback_data.device = device - device._handle = handle - if callback is not True: - device._callback = callback + device = (get_devices() if not capture else get_capture_devices())[name] + assert device.is_capture is capture + device = device.open(frequency=frequency, format=format, channels=channels) device.paused = paused + + if callback is not None and callback is not True: + stream = device.new_stream(format=format, channels=channels, frequency=frequency) + + def _get_callback(stream: AudioStream, data: AudioStreamCallbackData) -> None: + if data.additional_samples <= 0: + return + buffer = np.full( + (data.additional_samples, channels), fill_value=_silence_value_for_format(format), dtype=format + ) + callback(device, buffer) + stream.queue_audio(buffer) + + stream.getter_callback = _get_callback + return device diff --git a/tcod/sdl/constants.py b/tcod/sdl/constants.py new file mode 100644 index 00000000..39d766f4 --- /dev/null +++ b/tcod/sdl/constants.py @@ -0,0 +1,489 @@ +"""SDL private constants.""" + +SDL_PRILL_PREFIX = "ll" +SDL_PRILLX = Ellipsis +SDL_FUNCTION = "???" +SDL_PROP_THREAD_CREATE_ENTRY_FUNCTION_POINTER = "SDL.thread.create.entry_function" +SDL_PROP_THREAD_CREATE_NAME_STRING = "SDL.thread.create.name" +SDL_PROP_THREAD_CREATE_USERDATA_POINTER = "SDL.thread.create.userdata" +SDL_PROP_THREAD_CREATE_STACKSIZE_NUMBER = "SDL.thread.create.stacksize" +SDL_PROP_IOSTREAM_WINDOWS_HANDLE_POINTER = "SDL.iostream.windows.handle" +SDL_PROP_IOSTREAM_STDIO_FILE_POINTER = "SDL.iostream.stdio.file" +SDL_PROP_IOSTREAM_FILE_DESCRIPTOR_NUMBER = "SDL.iostream.file_descriptor" +SDL_PROP_IOSTREAM_ANDROID_AASSET_POINTER = "SDL.iostream.android.aasset" +SDL_PROP_IOSTREAM_MEMORY_POINTER = "SDL.iostream.memory.base" +SDL_PROP_IOSTREAM_MEMORY_SIZE_NUMBER = "SDL.iostream.memory.size" +SDL_PROP_IOSTREAM_DYNAMIC_MEMORY_POINTER = "SDL.iostream.dynamic.memory" +SDL_PROP_IOSTREAM_DYNAMIC_CHUNKSIZE_NUMBER = "SDL.iostream.dynamic.chunksize" +SDL_PROP_SURFACE_SDR_WHITE_POINT_FLOAT = "SDL.surface.SDR_white_point" +SDL_PROP_SURFACE_HDR_HEADROOM_FLOAT = "SDL.surface.HDR_headroom" +SDL_PROP_SURFACE_TONEMAP_OPERATOR_STRING = "SDL.surface.tonemap" +SDL_PROP_SURFACE_HOTSPOT_X_NUMBER = "SDL.surface.hotspot.x" +SDL_PROP_SURFACE_HOTSPOT_Y_NUMBER = "SDL.surface.hotspot.y" +SDL_PROP_GLOBAL_VIDEO_WAYLAND_WL_DISPLAY_POINTER = "SDL.video.wayland.wl_display" +SDL_PROP_DISPLAY_HDR_ENABLED_BOOLEAN = "SDL.display.HDR_enabled" +SDL_PROP_DISPLAY_KMSDRM_PANEL_ORIENTATION_NUMBER = "SDL.display.KMSDRM.panel_orientation" +SDL_PROP_WINDOW_CREATE_ALWAYS_ON_TOP_BOOLEAN = "SDL.window.create.always_on_top" +SDL_PROP_WINDOW_CREATE_BORDERLESS_BOOLEAN = "SDL.window.create.borderless" +SDL_PROP_WINDOW_CREATE_FOCUSABLE_BOOLEAN = "SDL.window.create.focusable" +SDL_PROP_WINDOW_CREATE_EXTERNAL_GRAPHICS_CONTEXT_BOOLEAN = "SDL.window.create.external_graphics_context" +SDL_PROP_WINDOW_CREATE_FLAGS_NUMBER = "SDL.window.create.flags" +SDL_PROP_WINDOW_CREATE_FULLSCREEN_BOOLEAN = "SDL.window.create.fullscreen" +SDL_PROP_WINDOW_CREATE_HEIGHT_NUMBER = "SDL.window.create.height" +SDL_PROP_WINDOW_CREATE_HIDDEN_BOOLEAN = "SDL.window.create.hidden" +SDL_PROP_WINDOW_CREATE_HIGH_PIXEL_DENSITY_BOOLEAN = "SDL.window.create.high_pixel_density" +SDL_PROP_WINDOW_CREATE_MAXIMIZED_BOOLEAN = "SDL.window.create.maximized" +SDL_PROP_WINDOW_CREATE_MENU_BOOLEAN = "SDL.window.create.menu" +SDL_PROP_WINDOW_CREATE_METAL_BOOLEAN = "SDL.window.create.metal" +SDL_PROP_WINDOW_CREATE_MINIMIZED_BOOLEAN = "SDL.window.create.minimized" +SDL_PROP_WINDOW_CREATE_MODAL_BOOLEAN = "SDL.window.create.modal" +SDL_PROP_WINDOW_CREATE_MOUSE_GRABBED_BOOLEAN = "SDL.window.create.mouse_grabbed" +SDL_PROP_WINDOW_CREATE_OPENGL_BOOLEAN = "SDL.window.create.opengl" +SDL_PROP_WINDOW_CREATE_PARENT_POINTER = "SDL.window.create.parent" +SDL_PROP_WINDOW_CREATE_RESIZABLE_BOOLEAN = "SDL.window.create.resizable" +SDL_PROP_WINDOW_CREATE_TITLE_STRING = "SDL.window.create.title" +SDL_PROP_WINDOW_CREATE_TRANSPARENT_BOOLEAN = "SDL.window.create.transparent" +SDL_PROP_WINDOW_CREATE_TOOLTIP_BOOLEAN = "SDL.window.create.tooltip" +SDL_PROP_WINDOW_CREATE_UTILITY_BOOLEAN = "SDL.window.create.utility" +SDL_PROP_WINDOW_CREATE_VULKAN_BOOLEAN = "SDL.window.create.vulkan" +SDL_PROP_WINDOW_CREATE_WIDTH_NUMBER = "SDL.window.create.width" +SDL_PROP_WINDOW_CREATE_X_NUMBER = "SDL.window.create.x" +SDL_PROP_WINDOW_CREATE_Y_NUMBER = "SDL.window.create.y" +SDL_PROP_WINDOW_CREATE_COCOA_WINDOW_POINTER = "SDL.window.create.cocoa.window" +SDL_PROP_WINDOW_CREATE_COCOA_VIEW_POINTER = "SDL.window.create.cocoa.view" +SDL_PROP_WINDOW_CREATE_WAYLAND_SURFACE_ROLE_CUSTOM_BOOLEAN = "SDL.window.create.wayland.surface_role_custom" +SDL_PROP_WINDOW_CREATE_WAYLAND_CREATE_EGL_WINDOW_BOOLEAN = "SDL.window.create.wayland.create_egl_window" +SDL_PROP_WINDOW_CREATE_WAYLAND_WL_SURFACE_POINTER = "SDL.window.create.wayland.wl_surface" +SDL_PROP_WINDOW_CREATE_WIN32_HWND_POINTER = "SDL.window.create.win32.hwnd" +SDL_PROP_WINDOW_CREATE_WIN32_PIXEL_FORMAT_HWND_POINTER = "SDL.window.create.win32.pixel_format_hwnd" +SDL_PROP_WINDOW_CREATE_X11_WINDOW_NUMBER = "SDL.window.create.x11.window" +SDL_PROP_WINDOW_SHAPE_POINTER = "SDL.window.shape" +SDL_PROP_WINDOW_HDR_ENABLED_BOOLEAN = "SDL.window.HDR_enabled" +SDL_PROP_WINDOW_SDR_WHITE_LEVEL_FLOAT = "SDL.window.SDR_white_level" +SDL_PROP_WINDOW_HDR_HEADROOM_FLOAT = "SDL.window.HDR_headroom" +SDL_PROP_WINDOW_ANDROID_WINDOW_POINTER = "SDL.window.android.window" +SDL_PROP_WINDOW_ANDROID_SURFACE_POINTER = "SDL.window.android.surface" +SDL_PROP_WINDOW_UIKIT_WINDOW_POINTER = "SDL.window.uikit.window" +SDL_PROP_WINDOW_UIKIT_METAL_VIEW_TAG_NUMBER = "SDL.window.uikit.metal_view_tag" +SDL_PROP_WINDOW_UIKIT_OPENGL_FRAMEBUFFER_NUMBER = "SDL.window.uikit.opengl.framebuffer" +SDL_PROP_WINDOW_UIKIT_OPENGL_RENDERBUFFER_NUMBER = "SDL.window.uikit.opengl.renderbuffer" +SDL_PROP_WINDOW_UIKIT_OPENGL_RESOLVE_FRAMEBUFFER_NUMBER = "SDL.window.uikit.opengl.resolve_framebuffer" +SDL_PROP_WINDOW_KMSDRM_DEVICE_INDEX_NUMBER = "SDL.window.kmsdrm.dev_index" +SDL_PROP_WINDOW_KMSDRM_DRM_FD_NUMBER = "SDL.window.kmsdrm.drm_fd" +SDL_PROP_WINDOW_KMSDRM_GBM_DEVICE_POINTER = "SDL.window.kmsdrm.gbm_dev" +SDL_PROP_WINDOW_COCOA_WINDOW_POINTER = "SDL.window.cocoa.window" +SDL_PROP_WINDOW_COCOA_METAL_VIEW_TAG_NUMBER = "SDL.window.cocoa.metal_view_tag" +SDL_PROP_WINDOW_OPENVR_OVERLAY_ID = "SDL.window.openvr.overlay_id" +SDL_PROP_WINDOW_VIVANTE_DISPLAY_POINTER = "SDL.window.vivante.display" +SDL_PROP_WINDOW_VIVANTE_WINDOW_POINTER = "SDL.window.vivante.window" +SDL_PROP_WINDOW_VIVANTE_SURFACE_POINTER = "SDL.window.vivante.surface" +SDL_PROP_WINDOW_WIN32_HWND_POINTER = "SDL.window.win32.hwnd" +SDL_PROP_WINDOW_WIN32_HDC_POINTER = "SDL.window.win32.hdc" +SDL_PROP_WINDOW_WIN32_INSTANCE_POINTER = "SDL.window.win32.instance" +SDL_PROP_WINDOW_WAYLAND_DISPLAY_POINTER = "SDL.window.wayland.display" +SDL_PROP_WINDOW_WAYLAND_SURFACE_POINTER = "SDL.window.wayland.surface" +SDL_PROP_WINDOW_WAYLAND_VIEWPORT_POINTER = "SDL.window.wayland.viewport" +SDL_PROP_WINDOW_WAYLAND_EGL_WINDOW_POINTER = "SDL.window.wayland.egl_window" +SDL_PROP_WINDOW_WAYLAND_XDG_SURFACE_POINTER = "SDL.window.wayland.xdg_surface" +SDL_PROP_WINDOW_WAYLAND_XDG_TOPLEVEL_POINTER = "SDL.window.wayland.xdg_toplevel" +SDL_PROP_WINDOW_WAYLAND_XDG_TOPLEVEL_EXPORT_HANDLE_STRING = "SDL.window.wayland.xdg_toplevel_export_handle" +SDL_PROP_WINDOW_WAYLAND_XDG_POPUP_POINTER = "SDL.window.wayland.xdg_popup" +SDL_PROP_WINDOW_WAYLAND_XDG_POSITIONER_POINTER = "SDL.window.wayland.xdg_positioner" +SDL_PROP_WINDOW_X11_DISPLAY_POINTER = "SDL.window.x11.display" +SDL_PROP_WINDOW_X11_SCREEN_NUMBER = "SDL.window.x11.screen" +SDL_PROP_WINDOW_X11_WINDOW_NUMBER = "SDL.window.x11.window" +SDL_PROP_FILE_DIALOG_FILTERS_POINTER = "SDL.filedialog.filters" +SDL_PROP_FILE_DIALOG_NFILTERS_NUMBER = "SDL.filedialog.nfilters" +SDL_PROP_FILE_DIALOG_WINDOW_POINTER = "SDL.filedialog.window" +SDL_PROP_FILE_DIALOG_LOCATION_STRING = "SDL.filedialog.location" +SDL_PROP_FILE_DIALOG_MANY_BOOLEAN = "SDL.filedialog.many" +SDL_PROP_FILE_DIALOG_TITLE_STRING = "SDL.filedialog.title" +SDL_PROP_FILE_DIALOG_ACCEPT_STRING = "SDL.filedialog.accept" +SDL_PROP_FILE_DIALOG_CANCEL_STRING = "SDL.filedialog.cancel" +SDL_PROP_JOYSTICK_CAP_MONO_LED_BOOLEAN = "SDL.joystick.cap.mono_led" +SDL_PROP_JOYSTICK_CAP_RGB_LED_BOOLEAN = "SDL.joystick.cap.rgb_led" +SDL_PROP_JOYSTICK_CAP_PLAYER_LED_BOOLEAN = "SDL.joystick.cap.player_led" +SDL_PROP_JOYSTICK_CAP_RUMBLE_BOOLEAN = "SDL.joystick.cap.rumble" +SDL_PROP_JOYSTICK_CAP_TRIGGER_RUMBLE_BOOLEAN = "SDL.joystick.cap.trigger_rumble" +SDL_PROP_GAMEPAD_CAP_MONO_LED_BOOLEAN = Ellipsis +SDL_PROP_GAMEPAD_CAP_RGB_LED_BOOLEAN = Ellipsis +SDL_PROP_GAMEPAD_CAP_PLAYER_LED_BOOLEAN = Ellipsis +SDL_PROP_GAMEPAD_CAP_RUMBLE_BOOLEAN = Ellipsis +SDL_PROP_GAMEPAD_CAP_TRIGGER_RUMBLE_BOOLEAN = Ellipsis +SDL_PROP_TEXTINPUT_TYPE_NUMBER = "SDL.textinput.type" +SDL_PROP_TEXTINPUT_CAPITALIZATION_NUMBER = "SDL.textinput.capitalization" +SDL_PROP_TEXTINPUT_AUTOCORRECT_BOOLEAN = "SDL.textinput.autocorrect" +SDL_PROP_TEXTINPUT_MULTILINE_BOOLEAN = "SDL.textinput.multiline" +SDL_PROP_TEXTINPUT_ANDROID_INPUTTYPE_NUMBER = "SDL.textinput.android.inputtype" +SDL_PROP_GPU_DEVICE_CREATE_DEBUGMODE_BOOLEAN = "SDL.gpu.device.create.debugmode" +SDL_PROP_GPU_DEVICE_CREATE_PREFERLOWPOWER_BOOLEAN = "SDL.gpu.device.create.preferlowpower" +SDL_PROP_GPU_DEVICE_CREATE_NAME_STRING = "SDL.gpu.device.create.name" +SDL_PROP_GPU_DEVICE_CREATE_SHADERS_PRIVATE_BOOLEAN = "SDL.gpu.device.create.shaders.private" +SDL_PROP_GPU_DEVICE_CREATE_SHADERS_SPIRV_BOOLEAN = "SDL.gpu.device.create.shaders.spirv" +SDL_PROP_GPU_DEVICE_CREATE_SHADERS_DXBC_BOOLEAN = "SDL.gpu.device.create.shaders.dxbc" +SDL_PROP_GPU_DEVICE_CREATE_SHADERS_DXIL_BOOLEAN = "SDL.gpu.device.create.shaders.dxil" +SDL_PROP_GPU_DEVICE_CREATE_SHADERS_MSL_BOOLEAN = "SDL.gpu.device.create.shaders.msl" +SDL_PROP_GPU_DEVICE_CREATE_SHADERS_METALLIB_BOOLEAN = "SDL.gpu.device.create.shaders.metallib" +SDL_PROP_GPU_DEVICE_CREATE_D3D12_SEMANTIC_NAME_STRING = "SDL.gpu.device.create.d3d12.semantic" +SDL_PROP_GPU_COMPUTEPIPELINE_CREATE_NAME_STRING = "SDL.gpu.computepipeline.create.name" +SDL_PROP_GPU_GRAPHICSPIPELINE_CREATE_NAME_STRING = "SDL.gpu.graphicspipeline.create.name" +SDL_PROP_GPU_SAMPLER_CREATE_NAME_STRING = "SDL.gpu.sampler.create.name" +SDL_PROP_GPU_SHADER_CREATE_NAME_STRING = "SDL.gpu.shader.create.name" +SDL_PROP_GPU_TEXTURE_CREATE_D3D12_CLEAR_R_FLOAT = "SDL.gpu.texture.create.d3d12.clear.r" +SDL_PROP_GPU_TEXTURE_CREATE_D3D12_CLEAR_G_FLOAT = "SDL.gpu.texture.create.d3d12.clear.g" +SDL_PROP_GPU_TEXTURE_CREATE_D3D12_CLEAR_B_FLOAT = "SDL.gpu.texture.create.d3d12.clear.b" +SDL_PROP_GPU_TEXTURE_CREATE_D3D12_CLEAR_A_FLOAT = "SDL.gpu.texture.create.d3d12.clear.a" +SDL_PROP_GPU_TEXTURE_CREATE_D3D12_CLEAR_DEPTH_FLOAT = "SDL.gpu.texture.create.d3d12.clear.depth" +SDL_PROP_GPU_TEXTURE_CREATE_D3D12_CLEAR_STENCIL_NUMBER = "SDL.gpu.texture.create.d3d12.clear.stencil" +SDL_PROP_GPU_TEXTURE_CREATE_NAME_STRING = "SDL.gpu.texture.create.name" +SDL_PROP_GPU_BUFFER_CREATE_NAME_STRING = "SDL.gpu.buffer.create.name" +SDL_PROP_GPU_TRANSFERBUFFER_CREATE_NAME_STRING = "SDL.gpu.transferbuffer.create.name" +SDL_HINT_ALLOW_ALT_TAB_WHILE_GRABBED = "SDL_ALLOW_ALT_TAB_WHILE_GRABBED" +SDL_HINT_ANDROID_ALLOW_RECREATE_ACTIVITY = "SDL_ANDROID_ALLOW_RECREATE_ACTIVITY" +SDL_HINT_ANDROID_BLOCK_ON_PAUSE = "SDL_ANDROID_BLOCK_ON_PAUSE" +SDL_HINT_ANDROID_LOW_LATENCY_AUDIO = "SDL_ANDROID_LOW_LATENCY_AUDIO" +SDL_HINT_ANDROID_TRAP_BACK_BUTTON = "SDL_ANDROID_TRAP_BACK_BUTTON" +SDL_HINT_APP_ID = "SDL_APP_ID" +SDL_HINT_APP_NAME = "SDL_APP_NAME" +SDL_HINT_APPLE_TV_CONTROLLER_UI_EVENTS = "SDL_APPLE_TV_CONTROLLER_UI_EVENTS" +SDL_HINT_APPLE_TV_REMOTE_ALLOW_ROTATION = "SDL_APPLE_TV_REMOTE_ALLOW_ROTATION" +SDL_HINT_AUDIO_ALSA_DEFAULT_DEVICE = "SDL_AUDIO_ALSA_DEFAULT_DEVICE" +SDL_HINT_AUDIO_ALSA_DEFAULT_PLAYBACK_DEVICE = "SDL_AUDIO_ALSA_DEFAULT_PLAYBACK_DEVICE" +SDL_HINT_AUDIO_ALSA_DEFAULT_RECORDING_DEVICE = "SDL_AUDIO_ALSA_DEFAULT_RECORDING_DEVICE" +SDL_HINT_AUDIO_CATEGORY = "SDL_AUDIO_CATEGORY" +SDL_HINT_AUDIO_CHANNELS = "SDL_AUDIO_CHANNELS" +SDL_HINT_AUDIO_DEVICE_APP_ICON_NAME = "SDL_AUDIO_DEVICE_APP_ICON_NAME" +SDL_HINT_AUDIO_DEVICE_SAMPLE_FRAMES = "SDL_AUDIO_DEVICE_SAMPLE_FRAMES" +SDL_HINT_AUDIO_DEVICE_STREAM_NAME = "SDL_AUDIO_DEVICE_STREAM_NAME" +SDL_HINT_AUDIO_DEVICE_STREAM_ROLE = "SDL_AUDIO_DEVICE_STREAM_ROLE" +SDL_HINT_AUDIO_DISK_INPUT_FILE = "SDL_AUDIO_DISK_INPUT_FILE" +SDL_HINT_AUDIO_DISK_OUTPUT_FILE = "SDL_AUDIO_DISK_OUTPUT_FILE" +SDL_HINT_AUDIO_DISK_TIMESCALE = "SDL_AUDIO_DISK_TIMESCALE" +SDL_HINT_AUDIO_DRIVER = "SDL_AUDIO_DRIVER" +SDL_HINT_AUDIO_DUMMY_TIMESCALE = "SDL_AUDIO_DUMMY_TIMESCALE" +SDL_HINT_AUDIO_FORMAT = "SDL_AUDIO_FORMAT" +SDL_HINT_AUDIO_FREQUENCY = "SDL_AUDIO_FREQUENCY" +SDL_HINT_AUDIO_INCLUDE_MONITORS = "SDL_AUDIO_INCLUDE_MONITORS" +SDL_HINT_AUTO_UPDATE_JOYSTICKS = "SDL_AUTO_UPDATE_JOYSTICKS" +SDL_HINT_AUTO_UPDATE_SENSORS = "SDL_AUTO_UPDATE_SENSORS" +SDL_HINT_BMP_SAVE_LEGACY_FORMAT = "SDL_BMP_SAVE_LEGACY_FORMAT" +SDL_HINT_CAMERA_DRIVER = "SDL_CAMERA_DRIVER" +SDL_HINT_CPU_FEATURE_MASK = "SDL_CPU_FEATURE_MASK" +SDL_HINT_JOYSTICK_DIRECTINPUT = "SDL_JOYSTICK_DIRECTINPUT" +SDL_HINT_FILE_DIALOG_DRIVER = "SDL_FILE_DIALOG_DRIVER" +SDL_HINT_DISPLAY_USABLE_BOUNDS = "SDL_DISPLAY_USABLE_BOUNDS" +SDL_HINT_EMSCRIPTEN_ASYNCIFY = "SDL_EMSCRIPTEN_ASYNCIFY" +SDL_HINT_EMSCRIPTEN_CANVAS_SELECTOR = "SDL_EMSCRIPTEN_CANVAS_SELECTOR" +SDL_HINT_EMSCRIPTEN_KEYBOARD_ELEMENT = "SDL_EMSCRIPTEN_KEYBOARD_ELEMENT" +SDL_HINT_ENABLE_SCREEN_KEYBOARD = "SDL_ENABLE_SCREEN_KEYBOARD" +SDL_HINT_EVDEV_DEVICES = "SDL_EVDEV_DEVICES" +SDL_HINT_EVENT_LOGGING = "SDL_EVENT_LOGGING" +SDL_HINT_FORCE_RAISEWINDOW = "SDL_FORCE_RAISEWINDOW" +SDL_HINT_FRAMEBUFFER_ACCELERATION = "SDL_FRAMEBUFFER_ACCELERATION" +SDL_HINT_GAMECONTROLLERCONFIG = "SDL_GAMECONTROLLERCONFIG" +SDL_HINT_GAMECONTROLLERCONFIG_FILE = "SDL_GAMECONTROLLERCONFIG_FILE" +SDL_HINT_GAMECONTROLLERTYPE = "SDL_GAMECONTROLLERTYPE" +SDL_HINT_GAMECONTROLLER_IGNORE_DEVICES = "SDL_GAMECONTROLLER_IGNORE_DEVICES" +SDL_HINT_GAMECONTROLLER_IGNORE_DEVICES_EXCEPT = "SDL_GAMECONTROLLER_IGNORE_DEVICES_EXCEPT" +SDL_HINT_GAMECONTROLLER_SENSOR_FUSION = "SDL_GAMECONTROLLER_SENSOR_FUSION" +SDL_HINT_GDK_TEXTINPUT_DEFAULT_TEXT = "SDL_GDK_TEXTINPUT_DEFAULT_TEXT" +SDL_HINT_GDK_TEXTINPUT_DESCRIPTION = "SDL_GDK_TEXTINPUT_DESCRIPTION" +SDL_HINT_GDK_TEXTINPUT_MAX_LENGTH = "SDL_GDK_TEXTINPUT_MAX_LENGTH" +SDL_HINT_GDK_TEXTINPUT_SCOPE = "SDL_GDK_TEXTINPUT_SCOPE" +SDL_HINT_GDK_TEXTINPUT_TITLE = "SDL_GDK_TEXTINPUT_TITLE" +SDL_HINT_HIDAPI_LIBUSB = "SDL_HIDAPI_LIBUSB" +SDL_HINT_HIDAPI_LIBUSB_WHITELIST = "SDL_HIDAPI_LIBUSB_WHITELIST" +SDL_HINT_HIDAPI_UDEV = "SDL_HIDAPI_UDEV" +SDL_HINT_GPU_DRIVER = "SDL_GPU_DRIVER" +SDL_HINT_HIDAPI_ENUMERATE_ONLY_CONTROLLERS = "SDL_HIDAPI_ENUMERATE_ONLY_CONTROLLERS" +SDL_HINT_HIDAPI_IGNORE_DEVICES = "SDL_HIDAPI_IGNORE_DEVICES" +SDL_HINT_IME_IMPLEMENTED_UI = "SDL_IME_IMPLEMENTED_UI" +SDL_HINT_IOS_HIDE_HOME_INDICATOR = "SDL_IOS_HIDE_HOME_INDICATOR" +SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS = "SDL_JOYSTICK_ALLOW_BACKGROUND_EVENTS" +SDL_HINT_JOYSTICK_ARCADESTICK_DEVICES = "SDL_JOYSTICK_ARCADESTICK_DEVICES" +SDL_HINT_JOYSTICK_ARCADESTICK_DEVICES_EXCLUDED = "SDL_JOYSTICK_ARCADESTICK_DEVICES_EXCLUDED" +SDL_HINT_JOYSTICK_BLACKLIST_DEVICES = "SDL_JOYSTICK_BLACKLIST_DEVICES" +SDL_HINT_JOYSTICK_BLACKLIST_DEVICES_EXCLUDED = "SDL_JOYSTICK_BLACKLIST_DEVICES_EXCLUDED" +SDL_HINT_JOYSTICK_DEVICE = "SDL_JOYSTICK_DEVICE" +SDL_HINT_JOYSTICK_ENHANCED_REPORTS = "SDL_JOYSTICK_ENHANCED_REPORTS" +SDL_HINT_JOYSTICK_FLIGHTSTICK_DEVICES = "SDL_JOYSTICK_FLIGHTSTICK_DEVICES" +SDL_HINT_JOYSTICK_FLIGHTSTICK_DEVICES_EXCLUDED = "SDL_JOYSTICK_FLIGHTSTICK_DEVICES_EXCLUDED" +SDL_HINT_JOYSTICK_GAMEINPUT = "SDL_JOYSTICK_GAMEINPUT" +SDL_HINT_JOYSTICK_GAMECUBE_DEVICES = "SDL_JOYSTICK_GAMECUBE_DEVICES" +SDL_HINT_JOYSTICK_GAMECUBE_DEVICES_EXCLUDED = "SDL_JOYSTICK_GAMECUBE_DEVICES_EXCLUDED" +SDL_HINT_JOYSTICK_HIDAPI = "SDL_JOYSTICK_HIDAPI" +SDL_HINT_JOYSTICK_HIDAPI_COMBINE_JOY_CONS = "SDL_JOYSTICK_HIDAPI_COMBINE_JOY_CONS" +SDL_HINT_JOYSTICK_HIDAPI_GAMECUBE = "SDL_JOYSTICK_HIDAPI_GAMECUBE" +SDL_HINT_JOYSTICK_HIDAPI_GAMECUBE_RUMBLE_BRAKE = "SDL_JOYSTICK_HIDAPI_GAMECUBE_RUMBLE_BRAKE" +SDL_HINT_JOYSTICK_HIDAPI_JOY_CONS = "SDL_JOYSTICK_HIDAPI_JOY_CONS" +SDL_HINT_JOYSTICK_HIDAPI_JOYCON_HOME_LED = "SDL_JOYSTICK_HIDAPI_JOYCON_HOME_LED" +SDL_HINT_JOYSTICK_HIDAPI_LUNA = "SDL_JOYSTICK_HIDAPI_LUNA" +SDL_HINT_JOYSTICK_HIDAPI_NINTENDO_CLASSIC = "SDL_JOYSTICK_HIDAPI_NINTENDO_CLASSIC" +SDL_HINT_JOYSTICK_HIDAPI_PS3 = "SDL_JOYSTICK_HIDAPI_PS3" +SDL_HINT_JOYSTICK_HIDAPI_PS3_SIXAXIS_DRIVER = "SDL_JOYSTICK_HIDAPI_PS3_SIXAXIS_DRIVER" +SDL_HINT_JOYSTICK_HIDAPI_PS4 = "SDL_JOYSTICK_HIDAPI_PS4" +SDL_HINT_JOYSTICK_HIDAPI_PS4_REPORT_INTERVAL = "SDL_JOYSTICK_HIDAPI_PS4_REPORT_INTERVAL" +SDL_HINT_JOYSTICK_HIDAPI_PS5 = "SDL_JOYSTICK_HIDAPI_PS5" +SDL_HINT_JOYSTICK_HIDAPI_PS5_PLAYER_LED = "SDL_JOYSTICK_HIDAPI_PS5_PLAYER_LED" +SDL_HINT_JOYSTICK_HIDAPI_SHIELD = "SDL_JOYSTICK_HIDAPI_SHIELD" +SDL_HINT_JOYSTICK_HIDAPI_STADIA = "SDL_JOYSTICK_HIDAPI_STADIA" +SDL_HINT_JOYSTICK_HIDAPI_STEAM = "SDL_JOYSTICK_HIDAPI_STEAM" +SDL_HINT_JOYSTICK_HIDAPI_STEAM_HOME_LED = "SDL_JOYSTICK_HIDAPI_STEAM_HOME_LED" +SDL_HINT_JOYSTICK_HIDAPI_STEAMDECK = "SDL_JOYSTICK_HIDAPI_STEAMDECK" +SDL_HINT_JOYSTICK_HIDAPI_STEAM_HORI = "SDL_JOYSTICK_HIDAPI_STEAM_HORI" +SDL_HINT_JOYSTICK_HIDAPI_SWITCH = "SDL_JOYSTICK_HIDAPI_SWITCH" +SDL_HINT_JOYSTICK_HIDAPI_SWITCH_HOME_LED = "SDL_JOYSTICK_HIDAPI_SWITCH_HOME_LED" +SDL_HINT_JOYSTICK_HIDAPI_SWITCH_PLAYER_LED = "SDL_JOYSTICK_HIDAPI_SWITCH_PLAYER_LED" +SDL_HINT_JOYSTICK_HIDAPI_VERTICAL_JOY_CONS = "SDL_JOYSTICK_HIDAPI_VERTICAL_JOY_CONS" +SDL_HINT_JOYSTICK_HIDAPI_WII = "SDL_JOYSTICK_HIDAPI_WII" +SDL_HINT_JOYSTICK_HIDAPI_WII_PLAYER_LED = "SDL_JOYSTICK_HIDAPI_WII_PLAYER_LED" +SDL_HINT_JOYSTICK_HIDAPI_XBOX = "SDL_JOYSTICK_HIDAPI_XBOX" +SDL_HINT_JOYSTICK_HIDAPI_XBOX_360 = "SDL_JOYSTICK_HIDAPI_XBOX_360" +SDL_HINT_JOYSTICK_HIDAPI_XBOX_360_PLAYER_LED = "SDL_JOYSTICK_HIDAPI_XBOX_360_PLAYER_LED" +SDL_HINT_JOYSTICK_HIDAPI_XBOX_360_WIRELESS = "SDL_JOYSTICK_HIDAPI_XBOX_360_WIRELESS" +SDL_HINT_JOYSTICK_HIDAPI_XBOX_ONE = "SDL_JOYSTICK_HIDAPI_XBOX_ONE" +SDL_HINT_JOYSTICK_HIDAPI_XBOX_ONE_HOME_LED = "SDL_JOYSTICK_HIDAPI_XBOX_ONE_HOME_LED" +SDL_HINT_JOYSTICK_IOKIT = "SDL_JOYSTICK_IOKIT" +SDL_HINT_JOYSTICK_LINUX_CLASSIC = "SDL_JOYSTICK_LINUX_CLASSIC" +SDL_HINT_JOYSTICK_LINUX_DEADZONES = "SDL_JOYSTICK_LINUX_DEADZONES" +SDL_HINT_JOYSTICK_LINUX_DIGITAL_HATS = "SDL_JOYSTICK_LINUX_DIGITAL_HATS" +SDL_HINT_JOYSTICK_LINUX_HAT_DEADZONES = "SDL_JOYSTICK_LINUX_HAT_DEADZONES" +SDL_HINT_JOYSTICK_MFI = "SDL_JOYSTICK_MFI" +SDL_HINT_JOYSTICK_RAWINPUT = "SDL_JOYSTICK_RAWINPUT" +SDL_HINT_JOYSTICK_RAWINPUT_CORRELATE_XINPUT = "SDL_JOYSTICK_RAWINPUT_CORRELATE_XINPUT" +SDL_HINT_JOYSTICK_ROG_CHAKRAM = "SDL_JOYSTICK_ROG_CHAKRAM" +SDL_HINT_JOYSTICK_THREAD = "SDL_JOYSTICK_THREAD" +SDL_HINT_JOYSTICK_THROTTLE_DEVICES = "SDL_JOYSTICK_THROTTLE_DEVICES" +SDL_HINT_JOYSTICK_THROTTLE_DEVICES_EXCLUDED = "SDL_JOYSTICK_THROTTLE_DEVICES_EXCLUDED" +SDL_HINT_JOYSTICK_WGI = "SDL_JOYSTICK_WGI" +SDL_HINT_JOYSTICK_WHEEL_DEVICES = "SDL_JOYSTICK_WHEEL_DEVICES" +SDL_HINT_JOYSTICK_WHEEL_DEVICES_EXCLUDED = "SDL_JOYSTICK_WHEEL_DEVICES_EXCLUDED" +SDL_HINT_JOYSTICK_ZERO_CENTERED_DEVICES = "SDL_JOYSTICK_ZERO_CENTERED_DEVICES" +SDL_HINT_JOYSTICK_HAPTIC_AXES = "SDL_JOYSTICK_HAPTIC_AXES" +SDL_HINT_KEYCODE_OPTIONS = "SDL_KEYCODE_OPTIONS" +SDL_HINT_KMSDRM_DEVICE_INDEX = "SDL_KMSDRM_DEVICE_INDEX" +SDL_HINT_KMSDRM_REQUIRE_DRM_MASTER = "SDL_KMSDRM_REQUIRE_DRM_MASTER" +SDL_HINT_LOGGING = "SDL_LOGGING" +SDL_HINT_MAC_BACKGROUND_APP = "SDL_MAC_BACKGROUND_APP" +SDL_HINT_MAC_CTRL_CLICK_EMULATE_RIGHT_CLICK = "SDL_MAC_CTRL_CLICK_EMULATE_RIGHT_CLICK" +SDL_HINT_MAC_OPENGL_ASYNC_DISPATCH = "SDL_MAC_OPENGL_ASYNC_DISPATCH" +SDL_HINT_MAC_OPTION_AS_ALT = "SDL_MAC_OPTION_AS_ALT" +SDL_HINT_MAC_SCROLL_MOMENTUM = "SDL_MAC_SCROLL_MOMENTUM" +SDL_HINT_MAIN_CALLBACK_RATE = "SDL_MAIN_CALLBACK_RATE" +SDL_HINT_MOUSE_AUTO_CAPTURE = "SDL_MOUSE_AUTO_CAPTURE" +SDL_HINT_MOUSE_DOUBLE_CLICK_RADIUS = "SDL_MOUSE_DOUBLE_CLICK_RADIUS" +SDL_HINT_MOUSE_DOUBLE_CLICK_TIME = "SDL_MOUSE_DOUBLE_CLICK_TIME" +SDL_HINT_MOUSE_DEFAULT_SYSTEM_CURSOR = "SDL_MOUSE_DEFAULT_SYSTEM_CURSOR" +SDL_HINT_MOUSE_EMULATE_WARP_WITH_RELATIVE = "SDL_MOUSE_EMULATE_WARP_WITH_RELATIVE" +SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH = "SDL_MOUSE_FOCUS_CLICKTHROUGH" +SDL_HINT_MOUSE_NORMAL_SPEED_SCALE = "SDL_MOUSE_NORMAL_SPEED_SCALE" +SDL_HINT_MOUSE_RELATIVE_MODE_CENTER = "SDL_MOUSE_RELATIVE_MODE_CENTER" +SDL_HINT_MOUSE_RELATIVE_SPEED_SCALE = "SDL_MOUSE_RELATIVE_SPEED_SCALE" +SDL_HINT_MOUSE_RELATIVE_SYSTEM_SCALE = "SDL_MOUSE_RELATIVE_SYSTEM_SCALE" +SDL_HINT_MOUSE_RELATIVE_WARP_MOTION = "SDL_MOUSE_RELATIVE_WARP_MOTION" +SDL_HINT_MOUSE_RELATIVE_CURSOR_VISIBLE = "SDL_MOUSE_RELATIVE_CURSOR_VISIBLE" +SDL_HINT_MOUSE_TOUCH_EVENTS = "SDL_MOUSE_TOUCH_EVENTS" +SDL_HINT_MUTE_CONSOLE_KEYBOARD = "SDL_MUTE_CONSOLE_KEYBOARD" +SDL_HINT_NO_SIGNAL_HANDLERS = "SDL_NO_SIGNAL_HANDLERS" +SDL_HINT_OPENGL_LIBRARY = "SDL_OPENGL_LIBRARY" +SDL_HINT_EGL_LIBRARY = "SDL_EGL_LIBRARY" +SDL_HINT_OPENGL_ES_DRIVER = "SDL_OPENGL_ES_DRIVER" +SDL_HINT_OPENVR_LIBRARY = "SDL_OPENVR_LIBRARY" +SDL_HINT_ORIENTATIONS = "SDL_ORIENTATIONS" +SDL_HINT_POLL_SENTINEL = "SDL_POLL_SENTINEL" +SDL_HINT_PREFERRED_LOCALES = "SDL_PREFERRED_LOCALES" +SDL_HINT_QUIT_ON_LAST_WINDOW_CLOSE = "SDL_QUIT_ON_LAST_WINDOW_CLOSE" +SDL_HINT_RENDER_DIRECT3D_THREADSAFE = "SDL_RENDER_DIRECT3D_THREADSAFE" +SDL_HINT_RENDER_DIRECT3D11_DEBUG = "SDL_RENDER_DIRECT3D11_DEBUG" +SDL_HINT_RENDER_VULKAN_DEBUG = "SDL_RENDER_VULKAN_DEBUG" +SDL_HINT_RENDER_GPU_DEBUG = "SDL_RENDER_GPU_DEBUG" +SDL_HINT_RENDER_GPU_LOW_POWER = "SDL_RENDER_GPU_LOW_POWER" +SDL_HINT_RENDER_DRIVER = "SDL_RENDER_DRIVER" +SDL_HINT_RENDER_LINE_METHOD = "SDL_RENDER_LINE_METHOD" +SDL_HINT_RENDER_METAL_PREFER_LOW_POWER_DEVICE = "SDL_RENDER_METAL_PREFER_LOW_POWER_DEVICE" +SDL_HINT_RENDER_VSYNC = "SDL_RENDER_VSYNC" +SDL_HINT_RETURN_KEY_HIDES_IME = "SDL_RETURN_KEY_HIDES_IME" +SDL_HINT_ROG_GAMEPAD_MICE = "SDL_ROG_GAMEPAD_MICE" +SDL_HINT_ROG_GAMEPAD_MICE_EXCLUDED = "SDL_ROG_GAMEPAD_MICE_EXCLUDED" +SDL_HINT_RPI_VIDEO_LAYER = "SDL_RPI_VIDEO_LAYER" +SDL_HINT_SCREENSAVER_INHIBIT_ACTIVITY_NAME = "SDL_SCREENSAVER_INHIBIT_ACTIVITY_NAME" +SDL_HINT_SHUTDOWN_DBUS_ON_QUIT = "SDL_SHUTDOWN_DBUS_ON_QUIT" +SDL_HINT_STORAGE_TITLE_DRIVER = "SDL_STORAGE_TITLE_DRIVER" +SDL_HINT_STORAGE_USER_DRIVER = "SDL_STORAGE_USER_DRIVER" +SDL_HINT_THREAD_FORCE_REALTIME_TIME_CRITICAL = "SDL_THREAD_FORCE_REALTIME_TIME_CRITICAL" +SDL_HINT_THREAD_PRIORITY_POLICY = "SDL_THREAD_PRIORITY_POLICY" +SDL_HINT_TIMER_RESOLUTION = "SDL_TIMER_RESOLUTION" +SDL_HINT_TOUCH_MOUSE_EVENTS = "SDL_TOUCH_MOUSE_EVENTS" +SDL_HINT_TRACKPAD_IS_TOUCH_ONLY = "SDL_TRACKPAD_IS_TOUCH_ONLY" +SDL_HINT_TV_REMOTE_AS_JOYSTICK = "SDL_TV_REMOTE_AS_JOYSTICK" +SDL_HINT_VIDEO_ALLOW_SCREENSAVER = "SDL_VIDEO_ALLOW_SCREENSAVER" +SDL_HINT_VIDEO_DISPLAY_PRIORITY = "SDL_VIDEO_DISPLAY_PRIORITY" +SDL_HINT_VIDEO_DOUBLE_BUFFER = "SDL_VIDEO_DOUBLE_BUFFER" +SDL_HINT_VIDEO_DRIVER = "SDL_VIDEO_DRIVER" +SDL_HINT_VIDEO_DUMMY_SAVE_FRAMES = "SDL_VIDEO_DUMMY_SAVE_FRAMES" +SDL_HINT_VIDEO_EGL_ALLOW_GETDISPLAY_FALLBACK = "SDL_VIDEO_EGL_ALLOW_GETDISPLAY_FALLBACK" +SDL_HINT_VIDEO_FORCE_EGL = "SDL_VIDEO_FORCE_EGL" +SDL_HINT_VIDEO_MAC_FULLSCREEN_SPACES = "SDL_VIDEO_MAC_FULLSCREEN_SPACES" +SDL_HINT_VIDEO_MAC_FULLSCREEN_MENU_VISIBILITY = "SDL_VIDEO_MAC_FULLSCREEN_MENU_VISIBILITY" +SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS = "SDL_VIDEO_MINIMIZE_ON_FOCUS_LOSS" +SDL_HINT_VIDEO_OFFSCREEN_SAVE_FRAMES = "SDL_VIDEO_OFFSCREEN_SAVE_FRAMES" +SDL_HINT_VIDEO_SYNC_WINDOW_OPERATIONS = "SDL_VIDEO_SYNC_WINDOW_OPERATIONS" +SDL_HINT_VIDEO_WAYLAND_ALLOW_LIBDECOR = "SDL_VIDEO_WAYLAND_ALLOW_LIBDECOR" +SDL_HINT_VIDEO_WAYLAND_MODE_EMULATION = "SDL_VIDEO_WAYLAND_MODE_EMULATION" +SDL_HINT_VIDEO_WAYLAND_MODE_SCALING = "SDL_VIDEO_WAYLAND_MODE_SCALING" +SDL_HINT_VIDEO_WAYLAND_PREFER_LIBDECOR = "SDL_VIDEO_WAYLAND_PREFER_LIBDECOR" +SDL_HINT_VIDEO_WAYLAND_SCALE_TO_DISPLAY = "SDL_VIDEO_WAYLAND_SCALE_TO_DISPLAY" +SDL_HINT_VIDEO_WIN_D3DCOMPILER = "SDL_VIDEO_WIN_D3DCOMPILER" +SDL_HINT_VIDEO_X11_EXTERNAL_WINDOW_INPUT = "SDL_VIDEO_X11_EXTERNAL_WINDOW_INPUT" +SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR = "SDL_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR" +SDL_HINT_VIDEO_X11_NET_WM_PING = "SDL_VIDEO_X11_NET_WM_PING" +SDL_HINT_VIDEO_X11_NODIRECTCOLOR = "SDL_VIDEO_X11_NODIRECTCOLOR" +SDL_HINT_VIDEO_X11_SCALING_FACTOR = "SDL_VIDEO_X11_SCALING_FACTOR" +SDL_HINT_VIDEO_X11_VISUALID = "SDL_VIDEO_X11_VISUALID" +SDL_HINT_VIDEO_X11_WINDOW_VISUALID = "SDL_VIDEO_X11_WINDOW_VISUALID" +SDL_HINT_VIDEO_X11_XRANDR = "SDL_VIDEO_X11_XRANDR" +SDL_HINT_VITA_ENABLE_BACK_TOUCH = "SDL_VITA_ENABLE_BACK_TOUCH" +SDL_HINT_VITA_ENABLE_FRONT_TOUCH = "SDL_VITA_ENABLE_FRONT_TOUCH" +SDL_HINT_VITA_MODULE_PATH = "SDL_VITA_MODULE_PATH" +SDL_HINT_VITA_PVR_INIT = "SDL_VITA_PVR_INIT" +SDL_HINT_VITA_RESOLUTION = "SDL_VITA_RESOLUTION" +SDL_HINT_VITA_PVR_OPENGL = "SDL_VITA_PVR_OPENGL" +SDL_HINT_VITA_TOUCH_MOUSE_DEVICE = "SDL_VITA_TOUCH_MOUSE_DEVICE" +SDL_HINT_VULKAN_DISPLAY = "SDL_VULKAN_DISPLAY" +SDL_HINT_VULKAN_LIBRARY = "SDL_VULKAN_LIBRARY" +SDL_HINT_WAVE_FACT_CHUNK = "SDL_WAVE_FACT_CHUNK" +SDL_HINT_WAVE_CHUNK_LIMIT = "SDL_WAVE_CHUNK_LIMIT" +SDL_HINT_WAVE_RIFF_CHUNK_SIZE = "SDL_WAVE_RIFF_CHUNK_SIZE" +SDL_HINT_WAVE_TRUNCATION = "SDL_WAVE_TRUNCATION" +SDL_HINT_WINDOW_ACTIVATE_WHEN_RAISED = "SDL_WINDOW_ACTIVATE_WHEN_RAISED" +SDL_HINT_WINDOW_ACTIVATE_WHEN_SHOWN = "SDL_WINDOW_ACTIVATE_WHEN_SHOWN" +SDL_HINT_WINDOW_ALLOW_TOPMOST = "SDL_WINDOW_ALLOW_TOPMOST" +SDL_HINT_WINDOW_FRAME_USABLE_WHILE_CURSOR_HIDDEN = "SDL_WINDOW_FRAME_USABLE_WHILE_CURSOR_HIDDEN" +SDL_HINT_WINDOWS_CLOSE_ON_ALT_F4 = "SDL_WINDOWS_CLOSE_ON_ALT_F4" +SDL_HINT_WINDOWS_ENABLE_MENU_MNEMONICS = "SDL_WINDOWS_ENABLE_MENU_MNEMONICS" +SDL_HINT_WINDOWS_ENABLE_MESSAGELOOP = "SDL_WINDOWS_ENABLE_MESSAGELOOP" +SDL_HINT_WINDOWS_GAMEINPUT = "SDL_WINDOWS_GAMEINPUT" +SDL_HINT_WINDOWS_RAW_KEYBOARD = "SDL_WINDOWS_RAW_KEYBOARD" +SDL_HINT_WINDOWS_FORCE_SEMAPHORE_KERNEL = "SDL_WINDOWS_FORCE_SEMAPHORE_KERNEL" +SDL_HINT_WINDOWS_INTRESOURCE_ICON = "SDL_WINDOWS_INTRESOURCE_ICON" +SDL_HINT_WINDOWS_INTRESOURCE_ICON_SMALL = "SDL_WINDOWS_INTRESOURCE_ICON_SMALL" +SDL_HINT_WINDOWS_USE_D3D9EX = "SDL_WINDOWS_USE_D3D9EX" +SDL_HINT_WINDOWS_ERASE_BACKGROUND_MODE = "SDL_WINDOWS_ERASE_BACKGROUND_MODE" +SDL_HINT_X11_FORCE_OVERRIDE_REDIRECT = "SDL_X11_FORCE_OVERRIDE_REDIRECT" +SDL_HINT_X11_WINDOW_TYPE = "SDL_X11_WINDOW_TYPE" +SDL_HINT_X11_XCB_LIBRARY = "SDL_X11_XCB_LIBRARY" +SDL_HINT_XINPUT_ENABLED = "SDL_XINPUT_ENABLED" +SDL_HINT_ASSERT = "SDL_ASSERT" +SDL_HINT_PEN_MOUSE_EVENTS = "SDL_PEN_MOUSE_EVENTS" +SDL_HINT_PEN_TOUCH_EVENTS = "SDL_PEN_TOUCH_EVENTS" +SDL_PROP_APP_METADATA_NAME_STRING = "SDL.app.metadata.name" +SDL_PROP_APP_METADATA_VERSION_STRING = "SDL.app.metadata.version" +SDL_PROP_APP_METADATA_IDENTIFIER_STRING = "SDL.app.metadata.identifier" +SDL_PROP_APP_METADATA_CREATOR_STRING = "SDL.app.metadata.creator" +SDL_PROP_APP_METADATA_COPYRIGHT_STRING = "SDL.app.metadata.copyright" +SDL_PROP_APP_METADATA_URL_STRING = "SDL.app.metadata.url" +SDL_PROP_APP_METADATA_TYPE_STRING = "SDL.app.metadata.type" +SDL_PROP_PROCESS_CREATE_ARGS_POINTER = "SDL.process.create.args" +SDL_PROP_PROCESS_CREATE_ENVIRONMENT_POINTER = "SDL.process.create.environment" +SDL_PROP_PROCESS_CREATE_STDIN_NUMBER = "SDL.process.create.stdin_option" +SDL_PROP_PROCESS_CREATE_STDIN_POINTER = "SDL.process.create.stdin_source" +SDL_PROP_PROCESS_CREATE_STDOUT_NUMBER = "SDL.process.create.stdout_option" +SDL_PROP_PROCESS_CREATE_STDOUT_POINTER = "SDL.process.create.stdout_source" +SDL_PROP_PROCESS_CREATE_STDERR_NUMBER = "SDL.process.create.stderr_option" +SDL_PROP_PROCESS_CREATE_STDERR_POINTER = "SDL.process.create.stderr_source" +SDL_PROP_PROCESS_CREATE_STDERR_TO_STDOUT_BOOLEAN = "SDL.process.create.stderr_to_stdout" +SDL_PROP_PROCESS_CREATE_BACKGROUND_BOOLEAN = "SDL.process.create.background" +SDL_PROP_PROCESS_PID_NUMBER = "SDL.process.pid" +SDL_PROP_PROCESS_STDIN_POINTER = "SDL.process.stdin" +SDL_PROP_PROCESS_STDOUT_POINTER = "SDL.process.stdout" +SDL_PROP_PROCESS_STDERR_POINTER = "SDL.process.stderr" +SDL_PROP_PROCESS_BACKGROUND_BOOLEAN = "SDL.process.background" +SDL_SOFTWARE_RENDERER = "software" +SDL_PROP_RENDERER_CREATE_NAME_STRING = "SDL.renderer.create.name" +SDL_PROP_RENDERER_CREATE_WINDOW_POINTER = "SDL.renderer.create.window" +SDL_PROP_RENDERER_CREATE_SURFACE_POINTER = "SDL.renderer.create.surface" +SDL_PROP_RENDERER_CREATE_OUTPUT_COLORSPACE_NUMBER = "SDL.renderer.create.output_colorspace" +SDL_PROP_RENDERER_CREATE_PRESENT_VSYNC_NUMBER = "SDL.renderer.create.present_vsync" +SDL_PROP_RENDERER_CREATE_VULKAN_INSTANCE_POINTER = "SDL.renderer.create.vulkan.instance" +SDL_PROP_RENDERER_CREATE_VULKAN_SURFACE_NUMBER = "SDL.renderer.create.vulkan.surface" +SDL_PROP_RENDERER_CREATE_VULKAN_PHYSICAL_DEVICE_POINTER = "SDL.renderer.create.vulkan.physical_device" +SDL_PROP_RENDERER_CREATE_VULKAN_DEVICE_POINTER = "SDL.renderer.create.vulkan.device" +SDL_PROP_RENDERER_CREATE_VULKAN_GRAPHICS_QUEUE_FAMILY_INDEX_NUMBER = ( + "SDL.renderer.create.vulkan.graphics_queue_family_index" +) +SDL_PROP_RENDERER_CREATE_VULKAN_PRESENT_QUEUE_FAMILY_INDEX_NUMBER = ( + "SDL.renderer.create.vulkan.present_queue_family_index" +) +SDL_PROP_RENDERER_NAME_STRING = "SDL.renderer.name" +SDL_PROP_RENDERER_WINDOW_POINTER = "SDL.renderer.window" +SDL_PROP_RENDERER_SURFACE_POINTER = "SDL.renderer.surface" +SDL_PROP_RENDERER_VSYNC_NUMBER = "SDL.renderer.vsync" +SDL_PROP_RENDERER_MAX_TEXTURE_SIZE_NUMBER = "SDL.renderer.max_texture_size" +SDL_PROP_RENDERER_TEXTURE_FORMATS_POINTER = "SDL.renderer.texture_formats" +SDL_PROP_RENDERER_OUTPUT_COLORSPACE_NUMBER = "SDL.renderer.output_colorspace" +SDL_PROP_RENDERER_HDR_ENABLED_BOOLEAN = "SDL.renderer.HDR_enabled" +SDL_PROP_RENDERER_SDR_WHITE_POINT_FLOAT = "SDL.renderer.SDR_white_point" +SDL_PROP_RENDERER_HDR_HEADROOM_FLOAT = "SDL.renderer.HDR_headroom" +SDL_PROP_RENDERER_D3D9_DEVICE_POINTER = "SDL.renderer.d3d9.device" +SDL_PROP_RENDERER_D3D11_DEVICE_POINTER = "SDL.renderer.d3d11.device" +SDL_PROP_RENDERER_D3D11_SWAPCHAIN_POINTER = "SDL.renderer.d3d11.swap_chain" +SDL_PROP_RENDERER_D3D12_DEVICE_POINTER = "SDL.renderer.d3d12.device" +SDL_PROP_RENDERER_D3D12_SWAPCHAIN_POINTER = "SDL.renderer.d3d12.swap_chain" +SDL_PROP_RENDERER_D3D12_COMMAND_QUEUE_POINTER = "SDL.renderer.d3d12.command_queue" +SDL_PROP_RENDERER_VULKAN_INSTANCE_POINTER = "SDL.renderer.vulkan.instance" +SDL_PROP_RENDERER_VULKAN_SURFACE_NUMBER = "SDL.renderer.vulkan.surface" +SDL_PROP_RENDERER_VULKAN_PHYSICAL_DEVICE_POINTER = "SDL.renderer.vulkan.physical_device" +SDL_PROP_RENDERER_VULKAN_DEVICE_POINTER = "SDL.renderer.vulkan.device" +SDL_PROP_RENDERER_VULKAN_GRAPHICS_QUEUE_FAMILY_INDEX_NUMBER = "SDL.renderer.vulkan.graphics_queue_family_index" +SDL_PROP_RENDERER_VULKAN_PRESENT_QUEUE_FAMILY_INDEX_NUMBER = "SDL.renderer.vulkan.present_queue_family_index" +SDL_PROP_RENDERER_VULKAN_SWAPCHAIN_IMAGE_COUNT_NUMBER = "SDL.renderer.vulkan.swapchain_image_count" +SDL_PROP_RENDERER_GPU_DEVICE_POINTER = "SDL.renderer.gpu.device" +SDL_PROP_TEXTURE_CREATE_COLORSPACE_NUMBER = "SDL.texture.create.colorspace" +SDL_PROP_TEXTURE_CREATE_FORMAT_NUMBER = "SDL.texture.create.format" +SDL_PROP_TEXTURE_CREATE_ACCESS_NUMBER = "SDL.texture.create.access" +SDL_PROP_TEXTURE_CREATE_WIDTH_NUMBER = "SDL.texture.create.width" +SDL_PROP_TEXTURE_CREATE_HEIGHT_NUMBER = "SDL.texture.create.height" +SDL_PROP_TEXTURE_CREATE_SDR_WHITE_POINT_FLOAT = "SDL.texture.create.SDR_white_point" +SDL_PROP_TEXTURE_CREATE_HDR_HEADROOM_FLOAT = "SDL.texture.create.HDR_headroom" +SDL_PROP_TEXTURE_CREATE_D3D11_TEXTURE_POINTER = "SDL.texture.create.d3d11.texture" +SDL_PROP_TEXTURE_CREATE_D3D11_TEXTURE_U_POINTER = "SDL.texture.create.d3d11.texture_u" +SDL_PROP_TEXTURE_CREATE_D3D11_TEXTURE_V_POINTER = "SDL.texture.create.d3d11.texture_v" +SDL_PROP_TEXTURE_CREATE_D3D12_TEXTURE_POINTER = "SDL.texture.create.d3d12.texture" +SDL_PROP_TEXTURE_CREATE_D3D12_TEXTURE_U_POINTER = "SDL.texture.create.d3d12.texture_u" +SDL_PROP_TEXTURE_CREATE_D3D12_TEXTURE_V_POINTER = "SDL.texture.create.d3d12.texture_v" +SDL_PROP_TEXTURE_CREATE_METAL_PIXELBUFFER_POINTER = "SDL.texture.create.metal.pixelbuffer" +SDL_PROP_TEXTURE_CREATE_OPENGL_TEXTURE_NUMBER = "SDL.texture.create.opengl.texture" +SDL_PROP_TEXTURE_CREATE_OPENGL_TEXTURE_UV_NUMBER = "SDL.texture.create.opengl.texture_uv" +SDL_PROP_TEXTURE_CREATE_OPENGL_TEXTURE_U_NUMBER = "SDL.texture.create.opengl.texture_u" +SDL_PROP_TEXTURE_CREATE_OPENGL_TEXTURE_V_NUMBER = "SDL.texture.create.opengl.texture_v" +SDL_PROP_TEXTURE_CREATE_OPENGLES2_TEXTURE_NUMBER = "SDL.texture.create.opengles2.texture" +SDL_PROP_TEXTURE_CREATE_OPENGLES2_TEXTURE_UV_NUMBER = "SDL.texture.create.opengles2.texture_uv" +SDL_PROP_TEXTURE_CREATE_OPENGLES2_TEXTURE_U_NUMBER = "SDL.texture.create.opengles2.texture_u" +SDL_PROP_TEXTURE_CREATE_OPENGLES2_TEXTURE_V_NUMBER = "SDL.texture.create.opengles2.texture_v" +SDL_PROP_TEXTURE_CREATE_VULKAN_TEXTURE_NUMBER = "SDL.texture.create.vulkan.texture" +SDL_PROP_TEXTURE_COLORSPACE_NUMBER = "SDL.texture.colorspace" +SDL_PROP_TEXTURE_FORMAT_NUMBER = "SDL.texture.format" +SDL_PROP_TEXTURE_ACCESS_NUMBER = "SDL.texture.access" +SDL_PROP_TEXTURE_WIDTH_NUMBER = "SDL.texture.width" +SDL_PROP_TEXTURE_HEIGHT_NUMBER = "SDL.texture.height" +SDL_PROP_TEXTURE_SDR_WHITE_POINT_FLOAT = "SDL.texture.SDR_white_point" +SDL_PROP_TEXTURE_HDR_HEADROOM_FLOAT = "SDL.texture.HDR_headroom" +SDL_PROP_TEXTURE_D3D11_TEXTURE_POINTER = "SDL.texture.d3d11.texture" +SDL_PROP_TEXTURE_D3D11_TEXTURE_U_POINTER = "SDL.texture.d3d11.texture_u" +SDL_PROP_TEXTURE_D3D11_TEXTURE_V_POINTER = "SDL.texture.d3d11.texture_v" +SDL_PROP_TEXTURE_D3D12_TEXTURE_POINTER = "SDL.texture.d3d12.texture" +SDL_PROP_TEXTURE_D3D12_TEXTURE_U_POINTER = "SDL.texture.d3d12.texture_u" +SDL_PROP_TEXTURE_D3D12_TEXTURE_V_POINTER = "SDL.texture.d3d12.texture_v" +SDL_PROP_TEXTURE_OPENGL_TEXTURE_NUMBER = "SDL.texture.opengl.texture" +SDL_PROP_TEXTURE_OPENGL_TEXTURE_UV_NUMBER = "SDL.texture.opengl.texture_uv" +SDL_PROP_TEXTURE_OPENGL_TEXTURE_U_NUMBER = "SDL.texture.opengl.texture_u" +SDL_PROP_TEXTURE_OPENGL_TEXTURE_V_NUMBER = "SDL.texture.opengl.texture_v" +SDL_PROP_TEXTURE_OPENGL_TEXTURE_TARGET_NUMBER = "SDL.texture.opengl.target" +SDL_PROP_TEXTURE_OPENGL_TEX_W_FLOAT = "SDL.texture.opengl.tex_w" +SDL_PROP_TEXTURE_OPENGL_TEX_H_FLOAT = "SDL.texture.opengl.tex_h" +SDL_PROP_TEXTURE_OPENGLES2_TEXTURE_NUMBER = "SDL.texture.opengles2.texture" +SDL_PROP_TEXTURE_OPENGLES2_TEXTURE_UV_NUMBER = "SDL.texture.opengles2.texture_uv" +SDL_PROP_TEXTURE_OPENGLES2_TEXTURE_U_NUMBER = "SDL.texture.opengles2.texture_u" +SDL_PROP_TEXTURE_OPENGLES2_TEXTURE_V_NUMBER = "SDL.texture.opengles2.texture_v" +SDL_PROP_TEXTURE_OPENGLES2_TEXTURE_TARGET_NUMBER = "SDL.texture.opengles2.target" +SDL_PROP_TEXTURE_VULKAN_TEXTURE_NUMBER = "SDL.texture.vulkan.texture" diff --git a/tcod/sdl/joystick.py b/tcod/sdl/joystick.py index 8ab8637f..3466756a 100644 --- a/tcod/sdl/joystick.py +++ b/tcod/sdl/joystick.py @@ -2,17 +2,16 @@ .. versionadded:: 13.8 """ + from __future__ import annotations import enum -from typing import Any, ClassVar +from typing import Any, ClassVar, Final, Literal from weakref import WeakValueDictionary -from typing_extensions import Final, Literal - import tcod.sdl.sys from tcod.cffi import ffi, lib -from tcod.sdl._internal import _check, _check_p +from tcod.sdl._internal import _check, _check_int, _check_p _HAT_DIRECTIONS: dict[int, tuple[Literal[-1, 0, 1], Literal[-1, 0, 1]]] = { int(lib.SDL_HAT_CENTERED): (0, 0), @@ -30,54 +29,54 @@ class ControllerAxis(enum.IntEnum): """The standard axes for a game controller.""" - INVALID = int(lib.SDL_CONTROLLER_AXIS_INVALID) - LEFTX = int(lib.SDL_CONTROLLER_AXIS_LEFTX) + INVALID = int(lib.SDL_GAMEPAD_AXIS_INVALID) + LEFTX = int(lib.SDL_GAMEPAD_AXIS_LEFTX) """""" - LEFTY = int(lib.SDL_CONTROLLER_AXIS_LEFTY) + LEFTY = int(lib.SDL_GAMEPAD_AXIS_LEFTY) """""" - RIGHTX = int(lib.SDL_CONTROLLER_AXIS_RIGHTX) + RIGHTX = int(lib.SDL_GAMEPAD_AXIS_RIGHTX) """""" - RIGHTY = int(lib.SDL_CONTROLLER_AXIS_RIGHTY) + RIGHTY = int(lib.SDL_GAMEPAD_AXIS_RIGHTY) """""" - TRIGGERLEFT = int(lib.SDL_CONTROLLER_AXIS_TRIGGERLEFT) + TRIGGERLEFT = int(lib.SDL_GAMEPAD_AXIS_LEFT_TRIGGER) """""" - TRIGGERRIGHT = int(lib.SDL_CONTROLLER_AXIS_TRIGGERRIGHT) + TRIGGERRIGHT = int(lib.SDL_GAMEPAD_AXIS_RIGHT_TRIGGER) """""" class ControllerButton(enum.IntEnum): """The standard buttons for a game controller.""" - INVALID = int(lib.SDL_CONTROLLER_BUTTON_INVALID) - A = int(lib.SDL_CONTROLLER_BUTTON_A) + INVALID = int(lib.SDL_GAMEPAD_BUTTON_INVALID) + A = int(lib.SDL_GAMEPAD_BUTTON_SOUTH) """""" - B = int(lib.SDL_CONTROLLER_BUTTON_B) + B = int(lib.SDL_GAMEPAD_BUTTON_EAST) """""" - X = int(lib.SDL_CONTROLLER_BUTTON_X) + X = int(lib.SDL_GAMEPAD_BUTTON_WEST) """""" - Y = int(lib.SDL_CONTROLLER_BUTTON_Y) + Y = int(lib.SDL_GAMEPAD_BUTTON_NORTH) """""" - BACK = int(lib.SDL_CONTROLLER_BUTTON_BACK) + BACK = int(lib.SDL_GAMEPAD_BUTTON_BACK) """""" - GUIDE = int(lib.SDL_CONTROLLER_BUTTON_GUIDE) + GUIDE = int(lib.SDL_GAMEPAD_BUTTON_GUIDE) """""" - START = int(lib.SDL_CONTROLLER_BUTTON_START) + START = int(lib.SDL_GAMEPAD_BUTTON_START) """""" - LEFTSTICK = int(lib.SDL_CONTROLLER_BUTTON_LEFTSTICK) + LEFTSTICK = int(lib.SDL_GAMEPAD_BUTTON_LEFT_STICK) """""" - RIGHTSTICK = int(lib.SDL_CONTROLLER_BUTTON_RIGHTSTICK) + RIGHTSTICK = int(lib.SDL_GAMEPAD_BUTTON_RIGHT_STICK) """""" - LEFTSHOULDER = int(lib.SDL_CONTROLLER_BUTTON_LEFTSHOULDER) + LEFTSHOULDER = int(lib.SDL_GAMEPAD_BUTTON_LEFT_SHOULDER) """""" - RIGHTSHOULDER = int(lib.SDL_CONTROLLER_BUTTON_RIGHTSHOULDER) + RIGHTSHOULDER = int(lib.SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER) """""" - DPAD_UP = int(lib.SDL_CONTROLLER_BUTTON_DPAD_UP) + DPAD_UP = int(lib.SDL_GAMEPAD_BUTTON_DPAD_UP) """""" - DPAD_DOWN = int(lib.SDL_CONTROLLER_BUTTON_DPAD_DOWN) + DPAD_DOWN = int(lib.SDL_GAMEPAD_BUTTON_DPAD_DOWN) """""" - DPAD_LEFT = int(lib.SDL_CONTROLLER_BUTTON_DPAD_LEFT) + DPAD_LEFT = int(lib.SDL_GAMEPAD_BUTTON_DPAD_LEFT) """""" - DPAD_RIGHT = int(lib.SDL_CONTROLLER_BUTTON_DPAD_RIGHT) + DPAD_RIGHT = int(lib.SDL_GAMEPAD_BUTTON_DPAD_RIGHT) """""" MISC1 = 15 """""" @@ -93,29 +92,6 @@ class ControllerButton(enum.IntEnum): """""" -class Power(enum.IntEnum): - """The possible power states of a controller. - - .. seealso:: - :any:`Joystick.get_current_power` - """ - - UNKNOWN = int(lib.SDL_JOYSTICK_POWER_UNKNOWN) - """Power state is unknown.""" - EMPTY = int(lib.SDL_JOYSTICK_POWER_EMPTY) - """<= 5% power.""" - LOW = int(lib.SDL_JOYSTICK_POWER_LOW) - """<= 20% power.""" - MEDIUM = int(lib.SDL_JOYSTICK_POWER_MEDIUM) - """<= 70% power.""" - FULL = int(lib.SDL_JOYSTICK_POWER_FULL) - """<= 100% power.""" - WIRED = int(lib.SDL_JOYSTICK_POWER_WIRED) - """""" - MAX = int(lib.SDL_JOYSTICK_POWER_MAX) - """""" - - class Joystick: """A low-level SDL joystick. @@ -126,22 +102,23 @@ class Joystick: _by_instance_id: ClassVar[WeakValueDictionary[int, Joystick]] = WeakValueDictionary() """Currently opened joysticks.""" - def __init__(self, sdl_joystick_p: Any) -> None: + def __init__(self, sdl_joystick_p: Any) -> None: # noqa: ANN401 + """Wrap an SDL joystick C pointer.""" self.sdl_joystick_p: Final = sdl_joystick_p """The CFFI pointer to an SDL_Joystick struct.""" - self.axes: Final[int] = _check(lib.SDL_JoystickNumAxes(self.sdl_joystick_p)) + self.axes: Final[int] = _check_int(lib.SDL_GetNumJoystickAxes(self.sdl_joystick_p), failure=-1) """The total number of axes.""" - self.balls: Final[int] = _check(lib.SDL_JoystickNumBalls(self.sdl_joystick_p)) + self.balls: Final[int] = _check_int(lib.SDL_GetNumJoystickBalls(self.sdl_joystick_p), failure=-1) """The total number of trackballs.""" - self.buttons: Final[int] = _check(lib.SDL_JoystickNumButtons(self.sdl_joystick_p)) + self.buttons: Final[int] = _check_int(lib.SDL_GetNumJoystickButtons(self.sdl_joystick_p), failure=-1) """The total number of buttons.""" - self.hats: Final[int] = _check(lib.SDL_JoystickNumHats(self.sdl_joystick_p)) + self.hats: Final[int] = _check_int(lib.SDL_GetNumJoystickHats(self.sdl_joystick_p), failure=-1) """The total number of hats.""" - self.name: Final[str] = str(ffi.string(lib.SDL_JoystickName(self.sdl_joystick_p)), encoding="utf-8") + self.name: Final[str] = str(ffi.string(lib.SDL_GetJoystickName(self.sdl_joystick_p)), encoding="utf-8") """The name of this joystick.""" self.guid: Final[str] = self._get_guid() """The GUID of this joystick.""" - self.id: Final[int] = _check(lib.SDL_JoystickInstanceID(self.sdl_joystick_p)) + self.id: Final[int] = _check(lib.SDL_GetJoystickID(self.sdl_joystick_p)) """The instance ID of this joystick. This is not the same as the device ID.""" self._keep_alive: Any = None """The owner of this objects memory if this object does not own itself.""" @@ -149,9 +126,9 @@ def __init__(self, sdl_joystick_p: Any) -> None: self._by_instance_id[self.id] = self @classmethod - def _open(cls, device_index: int) -> Joystick: + def _open(cls, instance_id: int) -> Joystick: tcod.sdl.sys.init(tcod.sdl.sys.Subsystem.JOYSTICK) - p = _check_p(ffi.gc(lib.SDL_JoystickOpen(device_index), lib.SDL_JoystickClose)) + p = _check_p(ffi.gc(lib.SDL_OpenJoystick(instance_id), lib.SDL_CloseJoystick)) return cls(p) @classmethod @@ -159,39 +136,37 @@ def _from_instance_id(cls, instance_id: int) -> Joystick: return cls._by_instance_id[instance_id] def __eq__(self, other: object) -> bool: + """Return True if `self` and `other` refer to the same joystick.""" if isinstance(other, Joystick): return self.id == other.id return NotImplemented def __hash__(self) -> int: + """Return the joystick id as a hash.""" return hash(self.id) def _get_guid(self) -> str: guid_str = ffi.new("char[33]") - lib.SDL_JoystickGetGUIDString(lib.SDL_JoystickGetGUID(self.sdl_joystick_p), guid_str, len(guid_str)) + lib.SDL_GUIDToString(lib.SDL_GetJoystickGUID(self.sdl_joystick_p), guid_str, len(guid_str)) return str(ffi.string(guid_str), encoding="ascii") - def get_current_power(self) -> Power: - """Return the power level/state of this joystick. See :any:`Power`.""" - return Power(lib.SDL_JoystickCurrentPowerLevel(self.sdl_joystick_p)) - def get_axis(self, axis: int) -> int: """Return the raw value of `axis` in the range -32768 to 32767.""" - return int(lib.SDL_JoystickGetAxis(self.sdl_joystick_p, axis)) + return int(lib.SDL_GetJoystickAxis(self.sdl_joystick_p, axis)) def get_ball(self, ball: int) -> tuple[int, int]: """Return the values (delta_x, delta_y) of `ball` since the last poll.""" xy = ffi.new("int[2]") - _check(lib.SDL_JoystickGetBall(ball, xy, xy + 1)) + _check(lib.SDL_GetJoystickBall(self.sdl_joystick_p, ball, xy, xy + 1)) return int(xy[0]), int(xy[1]) def get_button(self, button: int) -> bool: """Return True if `button` is currently held.""" - return bool(lib.SDL_JoystickGetButton(self.sdl_joystick_p, button)) + return bool(lib.SDL_GetJoystickButton(self.sdl_joystick_p, button)) def get_hat(self, hat: int) -> tuple[Literal[-1, 0, 1], Literal[-1, 0, 1]]: """Return the direction of `hat` as (x, y). With (-1, -1) being in the upper-left.""" - return _HAT_DIRECTIONS[lib.SDL_JoystickGetHat(self.sdl_joystick_p, hat)] + return _HAT_DIRECTIONS[lib.SDL_GetJoystickHat(self.sdl_joystick_p, hat)] class GameController: @@ -200,16 +175,17 @@ class GameController: _by_instance_id: ClassVar[WeakValueDictionary[int, GameController]] = WeakValueDictionary() """Currently opened controllers.""" - def __init__(self, sdl_controller_p: Any) -> None: + def __init__(self, sdl_controller_p: Any) -> None: # noqa: ANN401 + """Wrap an SDL controller C pointer.""" self.sdl_controller_p: Final = sdl_controller_p - self.joystick: Final = Joystick(lib.SDL_GameControllerGetJoystick(self.sdl_controller_p)) + self.joystick: Final = Joystick(lib.SDL_GetGamepadJoystick(self.sdl_controller_p)) """The :any:`Joystick` associated with this controller.""" self.joystick._keep_alive = self.sdl_controller_p # This objects real owner needs to be kept alive. self._by_instance_id[self.joystick.id] = self @classmethod - def _open(cls, joystick_index: int) -> GameController: - return cls(_check_p(ffi.gc(lib.SDL_GameControllerOpen(joystick_index), lib.SDL_GameControllerClose))) + def _open(cls, instance_id: int) -> GameController: + return cls(_check_p(ffi.gc(lib.SDL_OpenGamepad(instance_id), lib.SDL_CloseGamepad))) @classmethod def _from_instance_id(cls, instance_id: int) -> GameController: @@ -217,7 +193,7 @@ def _from_instance_id(cls, instance_id: int) -> GameController: def get_button(self, button: ControllerButton) -> bool: """Return True if `button` is currently held.""" - return bool(lib.SDL_GameControllerGetButton(self.sdl_controller_p, button)) + return bool(lib.SDL_GetGamepadButton(self.sdl_controller_p, button)) def get_axis(self, axis: ControllerAxis) -> int: """Return the state of the given `axis`. @@ -225,156 +201,168 @@ def get_axis(self, axis: ControllerAxis) -> int: The state is usually a value from -32768 to 32767, with positive values towards the lower-right direction. Triggers have the range of 0 to 32767 instead. """ - return int(lib.SDL_GameControllerGetAxis(self.sdl_controller_p, axis)) + return int(lib.SDL_GetGamepadAxis(self.sdl_controller_p, axis)) def __eq__(self, other: object) -> bool: + """Return True if `self` and `other` are both controllers referring to the same joystick.""" if isinstance(other, GameController): return self.joystick.id == other.joystick.id return NotImplemented def __hash__(self) -> int: + """Return the joystick id as a hash.""" return hash(self.joystick.id) # These could exist as convenience functions, but the get_X functions are probably better. @property def _left_x(self) -> int: """Return the position of this axis (-32768 to 32767).""" - return int(lib.SDL_GameControllerGetAxis(self.sdl_controller_p, lib.SDL_CONTROLLER_AXIS_LEFTX)) + return int(lib.SDL_GetGamepadAxis(self.sdl_controller_p, lib.SDL_GAMEPAD_AXIS_LEFTX)) @property def _left_y(self) -> int: """Return the position of this axis (-32768 to 32767).""" - return int(lib.SDL_GameControllerGetAxis(self.sdl_controller_p, lib.SDL_CONTROLLER_AXIS_LEFTY)) + return int(lib.SDL_GetGamepadAxis(self.sdl_controller_p, lib.SDL_GAMEPAD_AXIS_LEFTY)) @property def _right_x(self) -> int: """Return the position of this axis (-32768 to 32767).""" - return int(lib.SDL_GameControllerGetAxis(self.sdl_controller_p, lib.SDL_CONTROLLER_AXIS_RIGHTX)) + return int(lib.SDL_GetGamepadAxis(self.sdl_controller_p, lib.SDL_GAMEPAD_AXIS_RIGHTX)) @property def _right_y(self) -> int: """Return the position of this axis (-32768 to 32767).""" - return int(lib.SDL_GameControllerGetAxis(self.sdl_controller_p, lib.SDL_CONTROLLER_AXIS_RIGHTY)) + return int(lib.SDL_GetGamepadAxis(self.sdl_controller_p, lib.SDL_GAMEPAD_AXIS_RIGHTY)) @property def _trigger_left(self) -> int: """Return the position of this trigger (0 to 32767).""" - return int(lib.SDL_GameControllerGetAxis(self.sdl_controller_p, lib.SDL_CONTROLLER_AXIS_TRIGGERLEFT)) + return int(lib.SDL_GetGamepadAxis(self.sdl_controller_p, lib.SDL_GAMEPAD_AXIS_LEFT_TRIGGER)) @property def _trigger_right(self) -> int: """Return the position of this trigger (0 to 32767).""" - return int(lib.SDL_GameControllerGetAxis(self.sdl_controller_p, lib.SDL_CONTROLLER_AXIS_TRIGGERRIGHT)) + return int(lib.SDL_GetGamepadAxis(self.sdl_controller_p, lib.SDL_GAMEPAD_AXIS_RIGHT_TRIGGER)) @property def _a(self) -> bool: """Return True if this button is held.""" - return bool(lib.SDL_GameControllerGetButton(self.sdl_controller_p, lib.SDL_CONTROLLER_BUTTON_A)) + return bool(lib.SDL_GetGamepadButton(self.sdl_controller_p, lib.SDL_GAMEPAD_BUTTON_SOUTH)) @property def _b(self) -> bool: """Return True if this button is held.""" - return bool(lib.SDL_GameControllerGetButton(self.sdl_controller_p, lib.SDL_CONTROLLER_BUTTON_B)) + return bool(lib.SDL_GetGamepadButton(self.sdl_controller_p, lib.SDL_GAMEPAD_BUTTON_EAST)) @property def _x(self) -> bool: """Return True if this button is held.""" - return bool(lib.SDL_GameControllerGetButton(self.sdl_controller_p, lib.SDL_CONTROLLER_BUTTON_X)) + return bool(lib.SDL_GetGamepadButton(self.sdl_controller_p, lib.SDL_GAMEPAD_BUTTON_WEST)) @property def _y(self) -> bool: """Return True if this button is held.""" - return bool(lib.SDL_GameControllerGetButton(self.sdl_controller_p, lib.SDL_CONTROLLER_BUTTON_Y)) + return bool(lib.SDL_GetGamepadButton(self.sdl_controller_p, lib.SDL_GAMEPAD_BUTTON_NORTH)) @property def _back(self) -> bool: """Return True if this button is held.""" - return bool(lib.SDL_GameControllerGetButton(self.sdl_controller_p, lib.SDL_CONTROLLER_BUTTON_BACK)) + return bool(lib.SDL_GetGamepadButton(self.sdl_controller_p, lib.SDL_GAMEPAD_BUTTON_BACK)) @property def _guide(self) -> bool: """Return True if this button is held.""" - return bool(lib.SDL_GameControllerGetButton(self.sdl_controller_p, lib.SDL_CONTROLLER_BUTTON_GUIDE)) + return bool(lib.SDL_GetGamepadButton(self.sdl_controller_p, lib.SDL_GAMEPAD_BUTTON_GUIDE)) @property def _start(self) -> bool: """Return True if this button is held.""" - return bool(lib.SDL_GameControllerGetButton(self.sdl_controller_p, lib.SDL_CONTROLLER_BUTTON_START)) + return bool(lib.SDL_GetGamepadButton(self.sdl_controller_p, lib.SDL_GAMEPAD_BUTTON_START)) @property def _left_stick(self) -> bool: """Return True if this button is held.""" - return bool(lib.SDL_GameControllerGetButton(self.sdl_controller_p, lib.SDL_CONTROLLER_BUTTON_LEFTSTICK)) + return bool(lib.SDL_GetGamepadButton(self.sdl_controller_p, lib.SDL_GAMEPAD_BUTTON_LEFT_STICK)) @property def _right_stick(self) -> bool: """Return True if this button is held.""" - return bool(lib.SDL_GameControllerGetButton(self.sdl_controller_p, lib.SDL_CONTROLLER_BUTTON_RIGHTSTICK)) + return bool(lib.SDL_GetGamepadButton(self.sdl_controller_p, lib.SDL_GAMEPAD_BUTTON_RIGHT_STICK)) @property def _left_shoulder(self) -> bool: """Return True if this button is held.""" - return bool(lib.SDL_GameControllerGetButton(self.sdl_controller_p, lib.SDL_CONTROLLER_BUTTON_LEFTSHOULDER)) + return bool(lib.SDL_GetGamepadButton(self.sdl_controller_p, lib.SDL_GAMEPAD_BUTTON_LEFT_SHOULDER)) @property def _right_shoulder(self) -> bool: """Return True if this button is held.""" - return bool(lib.SDL_GameControllerGetButton(self.sdl_controller_p, lib.SDL_CONTROLLER_BUTTON_RIGHTSHOULDER)) + return bool(lib.SDL_GetGamepadButton(self.sdl_controller_p, lib.SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER)) @property def _dpad(self) -> tuple[Literal[-1, 0, 1], Literal[-1, 0, 1]]: return ( - lib.SDL_GameControllerGetButton(self.sdl_controller_p, lib.SDL_CONTROLLER_BUTTON_DPAD_RIGHT) - - lib.SDL_GameControllerGetButton(self.sdl_controller_p, lib.SDL_CONTROLLER_BUTTON_DPAD_LEFT), - lib.SDL_GameControllerGetButton(self.sdl_controller_p, lib.SDL_CONTROLLER_BUTTON_DPAD_DOWN) - - lib.SDL_GameControllerGetButton(self.sdl_controller_p, lib.SDL_CONTROLLER_BUTTON_DPAD_UP), - ) + lib.SDL_GetGamepadButton(self.sdl_controller_p, lib.SDL_GAMEPAD_BUTTON_DPAD_RIGHT) + - lib.SDL_GetGamepadButton(self.sdl_controller_p, lib.SDL_GAMEPAD_BUTTON_DPAD_LEFT), + lib.SDL_GetGamepadButton(self.sdl_controller_p, lib.SDL_GAMEPAD_BUTTON_DPAD_DOWN) + - lib.SDL_GetGamepadButton(self.sdl_controller_p, lib.SDL_GAMEPAD_BUTTON_DPAD_UP), + ) # type: ignore[return-value] # Boolean math has predictable values @property def _misc1(self) -> bool: """Return True if this button is held.""" - return bool(lib.SDL_GameControllerGetButton(self.sdl_controller_p, lib.SDL_CONTROLLER_BUTTON_MISC1)) + return bool(lib.SDL_GetGamepadButton(self.sdl_controller_p, lib.SDL_GAMEPAD_BUTTON_MISC1)) @property def _paddle1(self) -> bool: """Return True if this button is held.""" - return bool(lib.SDL_GameControllerGetButton(self.sdl_controller_p, lib.SDL_CONTROLLER_BUTTON_PADDLE1)) + return bool(lib.SDL_GetGamepadButton(self.sdl_controller_p, lib.SDL_GAMEPAD_BUTTON_RIGHT_PADDLE1)) @property def _paddle2(self) -> bool: """Return True if this button is held.""" - return bool(lib.SDL_GameControllerGetButton(self.sdl_controller_p, lib.SDL_CONTROLLER_BUTTON_PADDLE2)) + return bool(lib.SDL_GetGamepadButton(self.sdl_controller_p, lib.SDL_GAMEPAD_BUTTON_LEFT_PADDLE1)) @property def _paddle3(self) -> bool: """Return True if this button is held.""" - return bool(lib.SDL_GameControllerGetButton(self.sdl_controller_p, lib.SDL_CONTROLLER_BUTTON_PADDLE3)) + return bool(lib.SDL_GetGamepadButton(self.sdl_controller_p, lib.SDL_GAMEPAD_BUTTON_RIGHT_PADDLE2)) @property def _paddle4(self) -> bool: """Return True if this button is held.""" - return bool(lib.SDL_GameControllerGetButton(self.sdl_controller_p, lib.SDL_CONTROLLER_BUTTON_PADDLE4)) + return bool(lib.SDL_GetGamepadButton(self.sdl_controller_p, lib.SDL_GAMEPAD_BUTTON_LEFT_PADDLE2)) @property def _touchpad(self) -> bool: """Return True if this button is held.""" - return bool(lib.SDL_GameControllerGetButton(self.sdl_controller_p, lib.SDL_CONTROLLER_BUTTON_TOUCHPAD)) + return bool(lib.SDL_GetGamepadButton(self.sdl_controller_p, lib.SDL_GAMEPAD_BUTTON_TOUCHPAD)) def init() -> None: """Initialize SDL's joystick and game controller subsystems.""" - tcod.sdl.sys.init(tcod.sdl.sys.Subsystem.JOYSTICK | tcod.sdl.sys.Subsystem.GAMECONTROLLER) + controller_systems = tcod.sdl.sys.Subsystem.JOYSTICK | tcod.sdl.sys.Subsystem.GAMECONTROLLER + if tcod.sdl.sys.Subsystem(lib.SDL_WasInit(controller_systems)) == controller_systems: + return # Already initialized + tcod.sdl.sys.init(controller_systems) + +def _get_instance_ids() -> list[int]: + """Return the instance IDs of all attached joysticks. -def _get_number() -> int: - """Return the number of attached joysticks.""" + SDL3's ``SDL_GetJoysticks`` returns an array of instance IDs, which is what + ``SDL_OpenJoystick``/``SDL_OpenGamepad``/``SDL_IsGamepad`` expect. These are not + contiguous device indices, so they must not be replaced with ``range``. + """ init() - return _check(lib.SDL_NumJoysticks()) + count = ffi.new("int*") + joysticks_p = _check_p(ffi.gc(lib.SDL_GetJoysticks(count), lib.SDL_free)) # SDL_JoystickID array + return [int(i) for i in joysticks_p[0 : count[0]]] def get_joysticks() -> list[Joystick]: """Return a list of all connected joystick devices.""" - return [Joystick._open(i) for i in range(_get_number())] + return [Joystick._open(instance_id) for instance_id in _get_instance_ids()] def get_controllers() -> list[GameController]: @@ -382,7 +370,7 @@ def get_controllers() -> list[GameController]: This ignores joysticks without a game controller mapping. """ - return [GameController._open(i) for i in range(_get_number()) if lib.SDL_IsGameController(i)] + return [GameController._open(instance_id) for instance_id in _get_instance_ids() if lib.SDL_IsGamepad(instance_id)] def _get_all() -> list[Joystick | GameController]: @@ -391,24 +379,33 @@ def _get_all() -> list[Joystick | GameController]: If the joystick has a controller mapping then it is returned as a :any:`GameController`. Otherwise it is returned as a :any:`Joystick`. """ - return [GameController._open(i) if lib.SDL_IsGameController(i) else Joystick._open(i) for i in range(_get_number())] + return [ + GameController._open(instance_id) if lib.SDL_IsGamepad(instance_id) else Joystick._open(instance_id) + for instance_id in _get_instance_ids() + ] -def joystick_event_state(new_state: bool | None = None) -> bool: +def joystick_event_state(new_state: bool | None = None) -> bool: # noqa: FBT001 """Check or set joystick event polling. .. seealso:: - https://wiki.libsdl.org/SDL_JoystickEventState + https://wiki.libsdl.org/SDL3/SDL_SetJoystickEventsEnabled """ - _OPTIONS = {None: lib.SDL_QUERY, False: lib.SDL_IGNORE, True: lib.SDL_ENABLE} - return bool(_check(lib.SDL_JoystickEventState(_OPTIONS[new_state]))) + if new_state is True: + lib.SDL_SetJoystickEventsEnabled(True) # noqa: FBT003 + elif new_state is False: + lib.SDL_SetJoystickEventsEnabled(False) # noqa: FBT003 + return lib.SDL_JoystickEventsEnabled() -def controller_event_state(new_state: bool | None = None) -> bool: +def controller_event_state(new_state: bool | None = None) -> bool: # noqa: FBT001 """Check or set game controller event polling. .. seealso:: - https://wiki.libsdl.org/SDL_GameControllerEventState + https://wiki.libsdl.org/SDL3/SDL_SetGamepadEventsEnabled """ - _OPTIONS = {None: lib.SDL_QUERY, False: lib.SDL_IGNORE, True: lib.SDL_ENABLE} - return bool(_check(lib.SDL_GameControllerEventState(_OPTIONS[new_state]))) + if new_state is True: + lib.SDL_SetGamepadEventsEnabled(True) # noqa: FBT003 + elif new_state is False: + lib.SDL_SetGamepadEventsEnabled(False) # noqa: FBT003 + return lib.SDL_GamepadEventsEnabled() diff --git a/tcod/sdl/mouse.py b/tcod/sdl/mouse.py index 6a93955a..c1dbff7f 100644 --- a/tcod/sdl/mouse.py +++ b/tcod/sdl/mouse.py @@ -6,24 +6,29 @@ .. versionadded:: 13.5 """ + from __future__ import annotations import enum -from typing import Any +from typing import TYPE_CHECKING, Any import numpy as np -from numpy.typing import ArrayLike, NDArray +from typing_extensions import deprecated import tcod.event import tcod.sdl.video from tcod.cffi import ffi, lib from tcod.sdl._internal import _check, _check_p +if TYPE_CHECKING: + from numpy.typing import ArrayLike, NDArray + class Cursor: """A cursor icon for use with :any:`set_cursor`.""" - def __init__(self, sdl_cursor_p: Any) -> None: + def __init__(self, sdl_cursor_p: Any) -> None: # noqa: ANN401 + """Wrap an SDL cursor C pointer.""" if ffi.typeof(sdl_cursor_p) is not ffi.typeof("struct SDL_Cursor*"): msg = f"Expected a {ffi.typeof('struct SDL_Cursor*')} type (was {ffi.typeof(sdl_cursor_p)})." raise TypeError(msg) @@ -32,13 +37,20 @@ def __init__(self, sdl_cursor_p: Any) -> None: raise TypeError(msg) self.p = sdl_cursor_p - def __eq__(self, other: Any) -> bool: - return bool(self.p == getattr(other, "p", None)) + def __eq__(self, other: object) -> bool: + """Return True if `self` is the same cursor as `other`.""" + if isinstance(other, Cursor): + return bool(self.p == getattr(other, "p", None)) + return NotImplemented + + def __hash__(self) -> int: + """Returns the hash of this objects C pointer.""" + return hash(self.p) @classmethod - def _claim(cls, sdl_cursor_p: Any) -> Cursor: + def _claim(cls, sdl_cursor_p: Any) -> Cursor: # noqa: ANN401 """Verify and wrap this pointer in a garbage collector before returning a Cursor.""" - return cls(ffi.gc(_check_p(sdl_cursor_p), lib.SDL_FreeCursor)) + return cls(ffi.gc(_check_p(sdl_cursor_p), lib.SDL_DestroyCursor)) class SystemCursor(enum.IntEnum): @@ -82,7 +94,7 @@ def new_cursor(data: NDArray[np.bool_], mask: NDArray[np.bool_], hot_xy: tuple[i :any:`set_cursor` https://wiki.libsdl.org/SDL_CreateCursor """ - if len(data.shape) != 2: + if len(data.shape) != 2: # noqa: PLR2004 msg = "Data and mask arrays must be 2D." raise TypeError(msg) if data.shape != mask.shape: @@ -145,7 +157,7 @@ def get_cursor() -> Cursor | None: return Cursor(cursor_p) if cursor_p else None -def capture(enable: bool) -> None: +def capture(enable: bool) -> None: # noqa: FBT001 """Enable or disable mouse capture to track the mouse outside of a window. It is highly recommended to read the related remarks section in the SDL docs before using this. @@ -171,19 +183,28 @@ def capture(enable: bool) -> None: _check(lib.SDL_CaptureMouse(enable)) -def set_relative_mode(enable: bool) -> None: +@deprecated("Set 'Window.relative_mouse_mode = value' instead.") +def set_relative_mode(enable: bool) -> None: # noqa: FBT001 """Enable or disable relative mouse mode which will lock and hide the mouse and only report mouse motion. .. seealso:: :any:`tcod.sdl.mouse.capture` - https://wiki.libsdl.org/SDL_SetRelativeMouseMode + https://wiki.libsdl.org/SDL_SetWindowRelativeMouseMode + + .. deprecated:: 19.0 + Replaced with :any:`tcod.sdl.video.Window.relative_mouse_mode` """ - _check(lib.SDL_SetRelativeMouseMode(enable)) + _check(lib.SDL_SetWindowRelativeMouseMode(lib.SDL_GetMouseFocus(), enable)) +@deprecated("Check 'Window.relative_mouse_mode' instead.") def get_relative_mode() -> bool: - """Return True if relative mouse mode is enabled.""" - return bool(lib.SDL_GetRelativeMouseMode()) + """Return True if relative mouse mode is enabled. + + .. deprecated:: 19.0 + Replaced with :any:`tcod.sdl.video.Window.relative_mouse_mode` + """ + return bool(lib.SDL_GetWindowRelativeMouseMode(lib.SDL_GetMouseFocus())) def get_global_state() -> tcod.event.MouseState: @@ -194,7 +215,7 @@ def get_global_state() -> tcod.event.MouseState: """ xy = ffi.new("int[2]") state = lib.SDL_GetGlobalMouseState(xy, xy + 1) - return tcod.event.MouseState((xy[0], xy[1]), state=state) + return tcod.event.MouseState(position=tcod.event.Point(xy[0], xy[1]), state=state) def get_relative_state() -> tcod.event.MouseState: @@ -205,7 +226,7 @@ def get_relative_state() -> tcod.event.MouseState: """ xy = ffi.new("int[2]") state = lib.SDL_GetRelativeMouseState(xy, xy + 1) - return tcod.event.MouseState((xy[0], xy[1]), state=state) + return tcod.event.MouseState(position=tcod.event.Point(xy[0], xy[1]), state=state) def get_state() -> tcod.event.MouseState: @@ -216,7 +237,7 @@ def get_state() -> tcod.event.MouseState: """ xy = ffi.new("int[2]") state = lib.SDL_GetMouseState(xy, xy + 1) - return tcod.event.MouseState((xy[0], xy[1]), state=state) + return tcod.event.MouseState(position=tcod.event.Point(xy[0], xy[1]), state=state) def get_focus() -> tcod.sdl.video.Window | None: @@ -235,7 +256,7 @@ def warp_in_window(window: tcod.sdl.video.Window, x: int, y: int) -> None: lib.SDL_WarpMouseInWindow(window.p, x, y) -def show(visible: bool | None = None) -> bool: +def show(visible: bool | None = None) -> bool: # noqa: FBT001 """Optionally show or hide the mouse cursor then return the state of the cursor. Args: @@ -246,5 +267,8 @@ def show(visible: bool | None = None) -> bool: .. versionadded:: 16.0 """ - _OPTIONS = {None: lib.SDL_QUERY, False: lib.SDL_DISABLE, True: lib.SDL_ENABLE} - return _check(lib.SDL_ShowCursor(_OPTIONS[visible])) == int(lib.SDL_ENABLE) + if visible is True: + lib.SDL_ShowCursor() + elif visible is False: + lib.SDL_HideCursor() + return lib.SDL_CursorVisible() diff --git a/tcod/sdl/render.py b/tcod/sdl/render.py index b5489f18..05147825 100644 --- a/tcod/sdl/render.py +++ b/tcod/sdl/render.py @@ -1,19 +1,25 @@ -"""SDL2 Rendering functionality. +"""SDL Rendering functionality. .. versionadded:: 13.4 """ + from __future__ import annotations import enum -from typing import Any +from typing import TYPE_CHECKING, Any, Final, Literal import numpy as np -from numpy.typing import NDArray -from typing_extensions import Final, Literal +from typing_extensions import deprecated +import tcod.sdl.constants import tcod.sdl.video from tcod.cffi import ffi, lib -from tcod.sdl._internal import _check, _check_p, _required_version +from tcod.sdl._internal import Properties, _check, _check_p + +if TYPE_CHECKING: + from collections.abc import Sequence + + from numpy.typing import NDArray class TextureAccess(enum.IntEnum): @@ -38,6 +44,26 @@ class RendererFlip(enum.IntFlag): """Flip the image vertically.""" +class LogicalPresentation(enum.IntEnum): + """SDL logical presentation modes. + + See https://wiki.libsdl.org/SDL3/SDL_RendererLogicalPresentation + + .. versionadded:: 19.0 + """ + + DISABLED = 0 + """""" + STRETCH = 1 + """""" + LETTERBOX = 2 + """""" + OVERSCAN = 3 + """""" + INTEGER_SCALE = 4 + """""" + + class BlendFactor(enum.IntEnum): """SDL blend factors. @@ -115,7 +141,20 @@ class BlendMode(enum.IntEnum): """""" -def compose_blend_mode( +class ScaleMode(enum.IntEnum): + """Texture scaling modes. + + .. versionadded:: 19.3 + """ + + NEAREST = lib.SDL_SCALEMODE_NEAREST + """Nearing neighbor.""" + LINEAR = lib.SDL_SCALEMODE_LINEAR + """Linier filtering.""" + # PIXELART = lib.SDL_SCALEMODE_PIXELART # Needs SDL 3.4 # noqa: ERA001 + + +def compose_blend_mode( # noqa: PLR0913 source_color_factor: BlendFactor, dest_color_factor: BlendFactor, color_operation: BlendOperation, @@ -148,22 +187,25 @@ class Texture: Create a new texture using :any:`Renderer.new_texture` or :any:`Renderer.upload_texture`. """ - def __init__(self, sdl_texture_p: Any, sdl_renderer_p: Any = None) -> None: + def __init__(self, sdl_texture_p: Any, sdl_renderer_p: Any = None) -> None: # noqa: ANN401 """Encapsulate an SDL_Texture pointer. This function is private.""" self.p = sdl_texture_p self._sdl_renderer_p = sdl_renderer_p # Keep alive. - query = self._query() - self.format: Final[int] = query[0] + + props = Properties(lib.SDL_GetTextureProperties(self.p)) + self.format: Final[int] = props[(tcod.sdl.constants.SDL_PROP_TEXTURE_FORMAT_NUMBER, int)] """Texture format, read only.""" - self.access: Final[TextureAccess] = TextureAccess(query[1]) + self.access: Final[TextureAccess] = TextureAccess( + props[(tcod.sdl.constants.SDL_PROP_TEXTURE_ACCESS_NUMBER, int)] + ) """Texture access mode, read only. .. versionchanged:: 13.5 Attribute is now a :any:`TextureAccess` value. """ - self.width: Final[int] = query[2] + self.width: Final[int] = props[(tcod.sdl.constants.SDL_PROP_TEXTURE_WIDTH_NUMBER, int)] """Texture pixel width, read only.""" - self.height: Final[int] = query[3] + self.height: Final[int] = props[(tcod.sdl.constants.SDL_PROP_TEXTURE_HEIGHT_NUMBER, int)] """Texture pixel height, read only.""" def __eq__(self, other: object) -> bool: @@ -172,12 +214,9 @@ def __eq__(self, other: object) -> bool: return bool(self.p == other.p) return NotImplemented - def _query(self) -> tuple[int, int, int, int]: - """Return (format, access, width, height).""" - format = ffi.new("uint32_t*") - buffer = ffi.new("int[3]") - lib.SDL_QueryTexture(self.p, format, buffer, buffer + 1, buffer + 2) - return int(format[0]), int(buffer[0]), int(buffer[1]), int(buffer[2]) + def __hash__(self) -> int: + """Return hash for the owned pointer.""" + return hash(self.p) def update(self, pixels: NDArray[Any], rect: tuple[int, int, int, int] | None = None) -> None: """Update the pixel data of this texture. @@ -228,6 +267,20 @@ def color_mod(self) -> tuple[int, int, int]: def color_mod(self, rgb: tuple[int, int, int]) -> None: _check(lib.SDL_SetTextureColorMod(self.p, rgb[0], rgb[1], rgb[2])) + @property + def scale_mode(self) -> ScaleMode: + """Get or set this textures :any:`ScaleMode`. + + .. versionadded:: 19.3 + """ + mode = ffi.new("SDL_ScaleMode*") + _check(lib.SDL_GetTextureScaleMode(self.p, mode)) + return ScaleMode(mode[0]) + + @scale_mode.setter + def scale_mode(self, value: ScaleMode, /) -> None: + _check(lib.SDL_SetTextureScaleMode(self.p, value)) + class _RestoreTargetContext: """A context manager which tracks the current render target and restores it on exiting.""" @@ -239,14 +292,14 @@ def __init__(self, renderer: Renderer) -> None: def __enter__(self) -> None: pass - def __exit__(self, *_: Any) -> None: + def __exit__(self, *_: object) -> None: _check(lib.SDL_SetRenderTarget(self.renderer.p, self.old_texture_p)) class Renderer: """SDL Renderer.""" - def __init__(self, sdl_renderer_p: Any) -> None: + def __init__(self, sdl_renderer_p: Any) -> None: # noqa: ANN401 """Encapsulate an SDL_Renderer pointer. This function is private.""" if ffi.typeof(sdl_renderer_p) is not ffi.typeof("struct SDL_Renderer*"): msg = f"Expected a {ffi.typeof('struct SDL_Window*')} type (was {ffi.typeof(sdl_renderer_p)})." @@ -262,7 +315,11 @@ def __eq__(self, other: object) -> bool: return bool(self.p == other.p) return NotImplemented - def copy( + def __hash__(self) -> int: + """Return hash for the owned pointer.""" + return hash(self.p) + + def copy( # noqa: PLR0913 self, texture: Texture, source: tuple[float, float, float, float] | None = None, @@ -286,7 +343,7 @@ def copy( Added the `angle`, `center`, and `flip` parameters. """ _check( - lib.SDL_RenderCopyExF( + lib.SDL_RenderTextureRotated( self.p, texture.p, (source,) if source is not None else ffi.NULL, @@ -307,7 +364,7 @@ def set_render_target(self, texture: Texture) -> _RestoreTargetContext: _check(lib.SDL_SetRenderTarget(self.p, texture.p)) return restore - def new_texture(self, width: int, height: int, *, format: int | None = None, access: int | None = None) -> Texture: + def new_texture(self, width: int, height: int, *, format: int | None = None, access: int | None = None) -> Texture: # noqa: A002 """Allocate and return a new Texture for this renderer. Args: @@ -318,13 +375,13 @@ def new_texture(self, width: int, height: int, *, format: int | None = None, acc See :any:`TextureAccess` for more options. """ if format is None: - format = 0 + format = 0 # noqa: A001 if access is None: access = int(lib.SDL_TEXTUREACCESS_STATIC) texture_p = ffi.gc(lib.SDL_CreateTexture(self.p, format, access, width, height), lib.SDL_DestroyTexture) return Texture(texture_p, self.p) - def upload_texture(self, pixels: NDArray[Any], *, format: int | None = None, access: int | None = None) -> Texture: + def upload_texture(self, pixels: NDArray[Any], *, format: int | None = None, access: int | None = None) -> Texture: # noqa: A002 """Return a new Texture from an array of pixels. Args: @@ -334,12 +391,12 @@ def upload_texture(self, pixels: NDArray[Any], *, format: int | None = None, acc See :any:`TextureAccess` for more options. """ if format is None: - assert len(pixels.shape) == 3 + assert len(pixels.shape) == 3 # noqa: PLR2004 assert pixels.dtype == np.uint8 - if pixels.shape[2] == 4: - format = int(lib.SDL_PIXELFORMAT_RGBA32) - elif pixels.shape[2] == 3: - format = int(lib.SDL_PIXELFORMAT_RGB24) + if pixels.shape[2] == 4: # noqa: PLR2004 + format = int(lib.SDL_PIXELFORMAT_RGBA32) # noqa: A001 + elif pixels.shape[2] == 3: # noqa: PLR2004 + format = int(lib.SDL_PIXELFORMAT_RGB24) # noqa: A001 else: msg = f"Can't determine the format required for an array of shape {pixels.shape}." raise TypeError(msg) @@ -360,7 +417,7 @@ def draw_color(self) -> tuple[int, int, int, int]: """ rgba = ffi.new("uint8_t[4]") _check(lib.SDL_GetRenderDrawColor(self.p, rgba, rgba + 1, rgba + 2, rgba + 3)) - return tuple(rgba) # type: ignore[return-value] + return tuple(rgba) @draw_color.setter def draw_color(self, rgba: tuple[int, int, int, int]) -> None: @@ -382,16 +439,16 @@ def draw_blend_mode(self, value: int) -> None: @property def output_size(self) -> tuple[int, int]: - """Get the (width, height) pixel resolution of the rendering context. + """Get the (width, height) pixel resolution of the current rendering context. .. seealso:: - https://wiki.libsdl.org/SDL_GetRendererOutputSize + https://wiki.libsdl.org/SDL3/SDL_GetCurrentRenderOutputSize .. versionadded:: 13.5 """ out = ffi.new("int[2]") - _check(lib.SDL_GetRendererOutputSize(self.p, out, out + 1)) - return out[0], out[1] + _check(lib.SDL_GetCurrentRenderOutputSize(self.p, out, out + 1)) + return tuple(out) @property def clip_rect(self) -> tuple[int, int, int, int] | None: @@ -401,98 +458,100 @@ def clip_rect(self) -> tuple[int, int, int, int] | None: .. versionadded:: 13.5 """ - if not lib.SDL_RenderIsClipEnabled(self.p): + if not lib.SDL_RenderClipEnabled(self.p): return None rect = ffi.new("SDL_Rect*") - lib.SDL_RenderGetClipRect(self.p, rect) + lib.SDL_GetRenderClipRect(self.p, rect) return rect.x, rect.y, rect.w, rect.h @clip_rect.setter def clip_rect(self, rect: tuple[int, int, int, int] | None) -> None: rect_p = ffi.NULL if rect is None else ffi.new("SDL_Rect*", rect) - _check(lib.SDL_RenderSetClipRect(self.p, rect_p)) + _check(lib.SDL_SetRenderClipRect(self.p, rect_p)) - @property - def integer_scaling(self) -> bool: - """Get or set if this renderer enforces integer scaling. + def set_logical_presentation(self, resolution: tuple[int, int], mode: LogicalPresentation) -> None: + """Set this renderers device independent resolution. .. seealso:: - https://wiki.libsdl.org/SDL_RenderSetIntegerScale + https://wiki.libsdl.org/SDL3/SDL_SetRenderLogicalPresentation - .. versionadded:: 13.5 + .. versionadded:: 19.0 """ - return bool(lib.SDL_RenderGetIntegerScale(self.p)) - - @integer_scaling.setter - def integer_scaling(self, enable: bool) -> None: - _check(lib.SDL_RenderSetIntegerScale(self.p, enable)) + width, height = resolution + _check(lib.SDL_SetRenderLogicalPresentation(self.p, width, height, mode)) @property - def logical_size(self) -> tuple[int, int]: - """Get or set a device independent (width, height) resolution. - - Might be (0, 0) if a resolution was never assigned. + def logical_size(self) -> tuple[int, int] | None: + """Get current independent (width, height) resolution, or None if logical size is unset. .. seealso:: - https://wiki.libsdl.org/SDL_RenderSetLogicalSize + https://wiki.libsdl.org/SDL3/SDL_GetRenderLogicalPresentation .. versionadded:: 13.5 + + .. versionchanged:: 19.0 + Setter is deprecated, use :any:`set_logical_presentation` instead. + + .. versionchanged:: 20.0 + Return ``None`` instead of ``(0, 0)`` when logical size is disabled. """ out = ffi.new("int[2]") - lib.SDL_RenderGetLogicalSize(self.p, out, out + 1) - return out[0], out[1] + lib.SDL_GetRenderLogicalPresentation(self.p, out, out + 1, ffi.NULL) + out_tuple = tuple(out) + return None if out_tuple == (0, 0) else out_tuple @logical_size.setter + @deprecated("Use set_logical_presentation method to correctly setup logical size.") def logical_size(self, size: tuple[int, int]) -> None: - _check(lib.SDL_RenderSetLogicalSize(self.p, *size)) + width, height = size + _check(lib.SDL_SetRenderLogicalPresentation(self.p, width, height, lib.SDL_LOGICAL_PRESENTATION_STRETCH)) @property def scale(self) -> tuple[float, float]: """Get or set an (x_scale, y_scale) multiplier for drawing. .. seealso:: - https://wiki.libsdl.org/SDL_RenderSetScale + https://wiki.libsdl.org/SDL3/SDL_SetRenderScale .. versionadded:: 13.5 """ out = ffi.new("float[2]") - lib.SDL_RenderGetScale(self.p, out, out + 1) + lib.SDL_GetRenderScale(self.p, out, out + 1) return out[0], out[1] @scale.setter def scale(self, scale: tuple[float, float]) -> None: - _check(lib.SDL_RenderSetScale(self.p, *scale)) + _check(lib.SDL_SetRenderScale(self.p, *scale)) @property def viewport(self) -> tuple[int, int, int, int] | None: """Get or set the drawing area for the current rendering target. .. seealso:: - https://wiki.libsdl.org/SDL_RenderSetViewport + https://wiki.libsdl.org/SDL3/SDL_SetRenderViewport .. versionadded:: 13.5 """ rect = ffi.new("SDL_Rect*") - lib.SDL_RenderGetViewport(self.p, rect) + lib.SDL_GetRenderViewport(self.p, rect) return rect.x, rect.y, rect.w, rect.h @viewport.setter def viewport(self, rect: tuple[int, int, int, int] | None) -> None: - _check(lib.SDL_RenderSetViewport(self.p, (rect,))) + _check(lib.SDL_SetRenderViewport(self.p, (rect,))) - @_required_version((2, 0, 18)) - def set_vsync(self, enable: bool) -> None: + def set_vsync(self, enable: bool) -> None: # noqa: FBT001 """Enable or disable VSync for this renderer. .. versionadded:: 13.5 """ - _check(lib.SDL_RenderSetVSync(self.p, enable)) + _check(lib.SDL_SetRenderVSync(self.p, enable)) def read_pixels( self, *, rect: tuple[int, int, int, int] | None = None, - format: int | Literal["RGB", "RGBA"] = "RGBA", + format: Literal["RGB", "RGBA"] = "RGBA", # noqa: A002 out: NDArray[np.uint8] | None = None, ) -> NDArray[np.uint8]: """Fetch the pixel contents of the current rendering target to an array. @@ -509,49 +568,37 @@ def read_pixels( This operation is slow due to coping from VRAM to RAM. When reading the main rendering target this should be called after rendering and before :any:`present`. - See https://wiki.libsdl.org/SDL2/SDL_RenderReadPixels + See https://wiki.libsdl.org/SDL3/SDL_RenderReadPixels Returns: - The output uint8 array of shape: ``(height, width, channels)`` with the fetched pixels. + The output uint8 array of shape ``(height, width, channels)`` with the fetched pixels. .. versionadded:: 15.0 + + .. versionchanged:: 19.0 + `format` no longer accepts `int` values. """ - FORMATS: Final = {"RGB": lib.SDL_PIXELFORMAT_RGB24, "RGBA": lib.SDL_PIXELFORMAT_RGBA32} - sdl_format = FORMATS.get(format) if isinstance(format, str) else format - if rect is None: - texture_p = lib.SDL_GetRenderTarget(self.p) - if texture_p: - texture = Texture(texture_p) - rect = (0, 0, texture.width, texture.height) - else: - rect = (0, 0, *self.output_size) - width, height = rect[2:4] + surface = _check_p( + ffi.gc(lib.SDL_RenderReadPixels(self.p, (rect,) if rect is not None else ffi.NULL), lib.SDL_DestroySurface) + ) + width, height = rect[2:4] if rect is not None else (int(surface.w), int(surface.h)) + depth = {"RGB": 3, "RGBA": 4}.get(format) + if depth is None: + msg = f"Pixel format {format!r} not supported by tcod." + raise TypeError(msg) + expected_shape = height, width, depth if out is None: - if sdl_format == lib.SDL_PIXELFORMAT_RGBA32: - out = np.empty((height, width, 4), dtype=np.uint8) - elif sdl_format == lib.SDL_PIXELFORMAT_RGB24: - out = np.empty((height, width, 3), dtype=np.uint8) - else: - msg = f"Pixel format {format!r} not supported by tcod." - raise TypeError(msg) + out = np.empty(expected_shape, dtype=np.uint8) if out.dtype != np.uint8: msg = "`out` must be a uint8 array." raise TypeError(msg) - expected_shape = (height, width, {lib.SDL_PIXELFORMAT_RGB24: 3, lib.SDL_PIXELFORMAT_RGBA32: 4}[sdl_format]) if out.shape != expected_shape: msg = f"Expected `out` to be an array of shape {expected_shape}, got {out.shape} instead." raise TypeError(msg) if not out[0].flags.c_contiguous: msg = "`out` array must be C contiguous." - _check( - lib.SDL_RenderReadPixels( - self.p, - (rect,), - sdl_format, - ffi.cast("void*", out.ctypes.data), - out.strides[0], - ) - ) + out_surface = tcod.sdl.video._TempSurface(out) + _check(lib.SDL_BlitSurface(surface, ffi.NULL, out_surface.p, ffi.NULL)) return out def clear(self) -> None: @@ -566,120 +613,126 @@ def fill_rect(self, rect: tuple[float, float, float, float]) -> None: .. versionadded:: 13.5 """ - _check(lib.SDL_RenderFillRectF(self.p, (rect,))) + _check(lib.SDL_RenderFillRect(self.p, (rect,))) def draw_rect(self, rect: tuple[float, float, float, float]) -> None: """Draw a rectangle outline. .. versionadded:: 13.5 """ - _check(lib.SDL_RenderDrawRectF(self.p, (rect,))) + _check(lib.SDL_RenderFillRects(self.p, (rect,), 1)) def draw_point(self, xy: tuple[float, float]) -> None: """Draw a point. .. versionadded:: 13.5 """ - _check(lib.SDL_RenderDrawPointF(self.p, (xy,))) + x, y = xy + _check(lib.SDL_RenderPoint(self.p, x, y)) def draw_line(self, start: tuple[float, float], end: tuple[float, float]) -> None: """Draw a single line. .. versionadded:: 13.5 """ - _check(lib.SDL_RenderDrawLineF(self.p, *start, *end)) + x1, y1 = start + x2, y2 = end + _check(lib.SDL_RenderLine(self.p, x1, y1, x2, y2)) + + @staticmethod + def _convert_array(array: NDArray[np.number] | Sequence[Sequence[float]], item_length: int) -> NDArray[np.float32]: + """Convert ndarray for a SDL function expecting a C contiguous array of either intc or float32. + + Array shape is enforced to be (n, item_length) + """ + out = np.ascontiguousarray(array, np.float32) + if len(out.shape) != 2: # noqa: PLR2004 + msg = f"Array must have 2 axes, but shape is {out.shape!r}" + raise TypeError(msg) + if out.shape[1] != item_length: + msg = f"Array shape[1] must be {item_length}, but shape is {out.shape!r}" + raise TypeError(msg) + return out - def fill_rects(self, rects: NDArray[np.intc | np.float32]) -> None: + def fill_rects(self, rects: NDArray[np.number] | Sequence[tuple[float, float, float, float]]) -> None: """Fill multiple rectangles from an array. + Args: + rects: A sequence or array of (x, y, width, height) rectangles. + .. versionadded:: 13.5 """ - assert len(rects.shape) == 2 - assert rects.shape[1] == 4 - rects = np.ascontiguousarray(rects) - if rects.dtype == np.intc: - _check(lib.SDL_RenderFillRects(self.p, tcod.ffi.from_buffer("SDL_Rect*", rects), rects.shape[0])) - elif rects.dtype == np.float32: - _check(lib.SDL_RenderFillRectsF(self.p, tcod.ffi.from_buffer("SDL_FRect*", rects), rects.shape[0])) - else: - msg = f"Array must be an np.intc or np.float32 type, got {rects.dtype}." - raise TypeError(msg) + rects = self._convert_array(rects, item_length=4) + _check(lib.SDL_RenderFillRects(self.p, ffi.from_buffer("SDL_FRect*", rects), rects.shape[0])) - def draw_rects(self, rects: NDArray[np.intc | np.float32]) -> None: + def draw_rects(self, rects: NDArray[np.number] | Sequence[tuple[float, float, float, float]]) -> None: """Draw multiple outlined rectangles from an array. + Args: + rects: A sequence or array of (x, y, width, height) rectangles. + .. versionadded:: 13.5 """ - assert len(rects.shape) == 2 - assert rects.shape[1] == 4 - rects = np.ascontiguousarray(rects) - if rects.dtype == np.intc: - _check(lib.SDL_RenderDrawRects(self.p, tcod.ffi.from_buffer("SDL_Rect*", rects), rects.shape[0])) - elif rects.dtype == np.float32: - _check(lib.SDL_RenderDrawRectsF(self.p, tcod.ffi.from_buffer("SDL_FRect*", rects), rects.shape[0])) - else: - msg = f"Array must be an np.intc or np.float32 type, got {rects.dtype}." - raise TypeError(msg) + rects = self._convert_array(rects, item_length=4) + assert len(rects.shape) == 2 # noqa: PLR2004 + assert rects.shape[1] == 4 # noqa: PLR2004 + _check(lib.SDL_RenderRects(self.p, ffi.from_buffer("SDL_FRect*", rects), rects.shape[0])) - def draw_points(self, points: NDArray[np.intc | np.float32]) -> None: + def draw_points(self, points: NDArray[np.number] | Sequence[tuple[float, float]]) -> None: """Draw an array of points. + Args: + points: A sequence or array of (x, y) points. + .. versionadded:: 13.5 """ - assert len(points.shape) == 2 - assert points.shape[1] == 2 - points = np.ascontiguousarray(points) - if points.dtype == np.intc: - _check(lib.SDL_RenderDrawRects(self.p, tcod.ffi.from_buffer("SDL_Point*", points), points.shape[0])) - elif points.dtype == np.float32: - _check(lib.SDL_RenderDrawRectsF(self.p, tcod.ffi.from_buffer("SDL_FPoint*", points), points.shape[0])) - else: - msg = f"Array must be an np.intc or np.float32 type, got {points.dtype}." - raise TypeError(msg) + points = self._convert_array(points, item_length=2) + _check(lib.SDL_RenderPoints(self.p, ffi.from_buffer("SDL_FPoint*", points), points.shape[0])) - def draw_lines(self, points: NDArray[np.intc | np.float32]) -> None: + def draw_lines(self, points: NDArray[np.number] | Sequence[tuple[float, float]]) -> None: """Draw a connected series of lines from an array. + Args: + points: A sequence or array of (x, y) points. + .. versionadded:: 13.5 """ - assert len(points.shape) == 2 - assert points.shape[1] == 2 - points = np.ascontiguousarray(points) - if points.dtype == np.intc: - _check(lib.SDL_RenderDrawRects(self.p, tcod.ffi.from_buffer("SDL_Point*", points), points.shape[0] - 1)) - elif points.dtype == np.float32: - _check(lib.SDL_RenderDrawRectsF(self.p, tcod.ffi.from_buffer("SDL_FPoint*", points), points.shape[0] - 1)) - else: - msg = f"Array must be an np.intc or np.float32 type, got {points.dtype}." - raise TypeError(msg) + points = self._convert_array(points, item_length=2) + _check(lib.SDL_RenderLines(self.p, ffi.from_buffer("SDL_FPoint*", points), points.shape[0])) - @_required_version((2, 0, 18)) def geometry( self, texture: Texture | None, - xy: NDArray[np.float32], - color: NDArray[np.uint8], - uv: NDArray[np.float32], + xy: NDArray[np.float32] | Sequence[tuple[float, float]], + color: NDArray[np.float32] | Sequence[tuple[float, float, float, float]], + uv: NDArray[np.float32] | Sequence[tuple[float, float]], indices: NDArray[np.uint8 | np.uint16 | np.uint32] | None = None, ) -> None: """Render triangles from texture and vertex data. + Args: + texture: The SDL texture to render from. + xy: A sequence of (x, y) points to buffer. + color: A sequence of (r, g, b, a) colors to buffer. + uv: A sequence of (x, y) coordinates to buffer. + indices: A sequence of indexes referring to the buffered data, every 3 indexes is a triangle to render. + .. versionadded:: 13.5 + + .. versionchanged:: 19.0 + `color` now takes float values instead of 8-bit integers. """ - assert xy.dtype == np.float32 - assert len(xy.shape) == 2 - assert xy.shape[1] == 2 - assert xy[0].flags.c_contiguous - - assert color.dtype == np.uint8 - assert len(color.shape) == 2 - assert color.shape[1] == 4 - assert color[0].flags.c_contiguous - - assert uv.dtype == np.float32 - assert len(uv.shape) == 2 - assert uv.shape[1] == 2 - assert uv[0].flags.c_contiguous + xy = np.ascontiguousarray(xy, np.float32) + assert len(xy.shape) == 2 # noqa: PLR2004 + assert xy.shape[1] == 2 # noqa: PLR2004 + + color = np.ascontiguousarray(color, np.float32) + assert len(color.shape) == 2 # noqa: PLR2004 + assert color.shape[1] == 4 # noqa: PLR2004 + + uv = np.ascontiguousarray(uv, np.float32) + assert len(uv.shape) == 2 # noqa: PLR2004 + assert uv.shape[1] == 2 # noqa: PLR2004 if indices is not None: assert indices.dtype.type in (np.uint8, np.uint16, np.uint32, np.int8, np.int16, np.int32) indices = np.ascontiguousarray(indices) @@ -691,7 +744,7 @@ def geometry( texture.p if texture else ffi.NULL, ffi.cast("float*", xy.ctypes.data), xy.strides[0], - ffi.cast("uint8_t*", color.ctypes.data), + ffi.cast("SDL_FColor*", color.ctypes.data), color.strides[0], ffi.cast("float*", uv.ctypes.data), uv.strides[0], @@ -702,40 +755,65 @@ def geometry( ) ) + def coordinates_from_window(self, xy: tuple[float, float], /) -> tuple[float, float]: + """Return the renderer coordinates from the given windows coordinates. + + .. seealso:: + https://wiki.libsdl.org/SDL3/SDL_RenderCoordinatesFromWindow + + .. versionadded:: 20.0 + """ + x, y = xy + out_xy = ffi.new("float[2]") + _check(lib.SDL_RenderCoordinatesFromWindow(self.p, x, y, out_xy, out_xy + 1)) + return tuple(out_xy) + + def coordinates_to_window(self, xy: tuple[float, float], /) -> tuple[float, float]: + """Return the window coordinates from the given render coordinates. + + .. seealso:: + https://wiki.libsdl.org/SDL3/SDL_RenderCoordinatesToWindow + + .. versionadded:: 20.0 + """ + x, y = xy + out_xy = ffi.new("float[2]") + _check(lib.SDL_RenderCoordinatesToWindow(self.p, x, y, out_xy, out_xy + 1)) + return tuple(out_xy) + def new_renderer( window: tcod.sdl.video.Window, *, - driver: int | None = None, - software: bool = False, - vsync: bool = True, - target_textures: bool = False, + driver: str | None = None, + vsync: int = True, ) -> Renderer: """Initialize and return a new SDL Renderer. Args: window: The window that this renderer will be attached to. driver: Force SDL to use a specific video driver. - software: If True then a software renderer will be forced. By default a hardware renderer is used. vsync: If True then Vsync will be enabled. - target_textures: If True then target textures can be used by the renderer. Example:: # Start by creating a window. sdl_window = tcod.sdl.video.new_window(640, 480) # Create a renderer with target texture support. - sdl_renderer = tcod.sdl.render.new_renderer(sdl_window, target_textures=True) + sdl_renderer = tcod.sdl.render.new_renderer(sdl_window) .. seealso:: :func:`tcod.sdl.video.new_window` + + .. versionchanged:: 19.0 + Removed `software` and `target_textures` parameters. + `vsync` now takes an integer. + `driver` now take a string. """ - driver = driver if driver is not None else -1 - flags = 0 - if vsync: - flags |= int(lib.SDL_RENDERER_PRESENTVSYNC) - if target_textures: - flags |= int(lib.SDL_RENDERER_TARGETTEXTURE) - flags |= int(lib.SDL_RENDERER_SOFTWARE) if software else int(lib.SDL_RENDERER_ACCELERATED) - renderer_p = _check_p(ffi.gc(lib.SDL_CreateRenderer(window.p, driver, flags), lib.SDL_DestroyRenderer)) + props = Properties() + props[(tcod.sdl.constants.SDL_PROP_RENDERER_CREATE_PRESENT_VSYNC_NUMBER, int)] = vsync + props[(tcod.sdl.constants.SDL_PROP_RENDERER_CREATE_WINDOW_POINTER, tcod.sdl.video.Window)] = window + if driver is not None: + props[(tcod.sdl.constants.SDL_PROP_RENDERER_CREATE_NAME_STRING, str)] = driver + renderer_p = _check_p(ffi.gc(lib.SDL_CreateRendererWithProperties(props.p), lib.SDL_DestroyRenderer)) return Renderer(renderer_p) diff --git a/tcod/sdl/sys.py b/tcod/sdl/sys.py index b2a71a66..c4056c69 100644 --- a/tcod/sdl/sys.py +++ b/tcod/sdl/sys.py @@ -2,7 +2,8 @@ import enum import warnings -from typing import Any + +from typing_extensions import Self from tcod.cffi import ffi, lib from tcod.sdl._internal import _check, _get_error @@ -17,14 +18,16 @@ class Subsystem(enum.IntFlag): GAMECONTROLLER = 0x00002000 EVENTS = 0x00004000 SENSOR = 0x00008000 - EVERYTHING = int(lib.SDL_INIT_EVERYTHING) -def init(flags: int = Subsystem.EVERYTHING) -> None: +def init(flags: int) -> None: _check(lib.SDL_InitSubSystem(flags)) -def quit(flags: int = Subsystem.EVERYTHING) -> None: +def quit(flags: int | None = None) -> None: # noqa: A001 + if flags is None: + lib.SDL_Quit() + return lib.SDL_QuitSubSystem(flags) @@ -41,10 +44,10 @@ def close(self) -> None: def __del__(self) -> None: self.close() - def __enter__(self) -> _ScopeInit: + def __enter__(self) -> Self: return self - def __exit__(self, *args: Any) -> None: + def __exit__(self, *_: object) -> None: self.close() diff --git a/tcod/sdl/video.py b/tcod/sdl/video.py index 64b8cf20..e9565f88 100644 --- a/tcod/sdl/video.py +++ b/tcod/sdl/video.py @@ -1,4 +1,4 @@ -"""SDL2 Window and Display handling. +"""SDL Window and Display handling. There are two main ways to access the SDL window. Either you can use this module to open a window yourself bypassing libtcod's context, @@ -6,24 +6,31 @@ .. versionadded:: 13.4 """ + from __future__ import annotations import enum import sys -from typing import Any +from typing import TYPE_CHECKING, Any import numpy as np -from numpy.typing import ArrayLike, NDArray +from typing_extensions import Self, deprecated +import tcod.sdl.constants from tcod.cffi import ffi, lib -from tcod.sdl._internal import _check, _check_p, _required_version, _version_at_least +from tcod.sdl._internal import Properties, _check, _check_p, _required_version + +if TYPE_CHECKING: + from numpy.typing import ArrayLike, NDArray __all__ = ( - "WindowFlags", + "Capitalization", "FlashOperation", + "TextInputType", "Window", - "new_window", + "WindowFlags", "get_grabbed_window", + "new_window", "screen_saver_allowed", ) @@ -37,12 +44,8 @@ class WindowFlags(enum.IntFlag): FULLSCREEN = int(lib.SDL_WINDOW_FULLSCREEN) """""" - FULLSCREEN_DESKTOP = int(lib.SDL_WINDOW_FULLSCREEN_DESKTOP) - """""" OPENGL = int(lib.SDL_WINDOW_OPENGL) """""" - SHOWN = int(lib.SDL_WINDOW_SHOWN) - """""" HIDDEN = int(lib.SDL_WINDOW_HIDDEN) """""" BORDERLESS = int(lib.SDL_WINDOW_BORDERLESS) @@ -53,22 +56,18 @@ class WindowFlags(enum.IntFlag): """""" MAXIMIZED = int(lib.SDL_WINDOW_MAXIMIZED) """""" - MOUSE_GRABBED = int(lib.SDL_WINDOW_INPUT_GRABBED) + MOUSE_GRABBED = int(lib.SDL_WINDOW_MOUSE_GRABBED) """""" INPUT_FOCUS = int(lib.SDL_WINDOW_INPUT_FOCUS) """""" MOUSE_FOCUS = int(lib.SDL_WINDOW_MOUSE_FOCUS) """""" - FOREIGN = int(lib.SDL_WINDOW_FOREIGN) - """""" - ALLOW_HIGHDPI = int(lib.SDL_WINDOW_ALLOW_HIGHDPI) + ALLOW_HIGHDPI = int(lib.SDL_WINDOW_HIGH_PIXEL_DENSITY) """""" MOUSE_CAPTURE = int(lib.SDL_WINDOW_MOUSE_CAPTURE) """""" ALWAYS_ON_TOP = int(lib.SDL_WINDOW_ALWAYS_ON_TOP) """""" - SKIP_TASKBAR = int(lib.SDL_WINDOW_SKIP_TASKBAR) - """""" UTILITY = int(lib.SDL_WINDOW_UTILITY) """""" TOOLTIP = int(lib.SDL_WINDOW_TOOLTIP) @@ -92,37 +91,97 @@ class FlashOperation(enum.IntEnum): """Flash until focus is gained.""" +class TextInputType(enum.IntEnum): + """SDL input types for text input. + + .. seealso:: + :any:`Window.start_text_input` + https://wiki.libsdl.org/SDL3/SDL_TextInputType + + .. versionadded:: 19.1 + """ + + TEXT = lib.SDL_TEXTINPUT_TYPE_TEXT + """The input is text.""" + TEXT_NAME = lib.SDL_TEXTINPUT_TYPE_TEXT_NAME + """The input is a person's name.""" + TEXT_EMAIL = lib.SDL_TEXTINPUT_TYPE_TEXT_EMAIL + """The input is an e-mail address.""" + TEXT_USERNAME = lib.SDL_TEXTINPUT_TYPE_TEXT_USERNAME + """The input is a username.""" + TEXT_PASSWORD_HIDDEN = lib.SDL_TEXTINPUT_TYPE_TEXT_PASSWORD_HIDDEN + """The input is a secure password that is hidden.""" + TEXT_PASSWORD_VISIBLE = lib.SDL_TEXTINPUT_TYPE_TEXT_PASSWORD_VISIBLE + """The input is a secure password that is visible.""" + NUMBER = lib.SDL_TEXTINPUT_TYPE_NUMBER + """The input is a number.""" + NUMBER_PASSWORD_HIDDEN = lib.SDL_TEXTINPUT_TYPE_NUMBER_PASSWORD_HIDDEN + """The input is a secure PIN that is hidden.""" + NUMBER_PASSWORD_VISIBLE = lib.SDL_TEXTINPUT_TYPE_NUMBER_PASSWORD_VISIBLE + """The input is a secure PIN that is visible.""" + + +class Capitalization(enum.IntEnum): + """Text capitalization for text input. + + .. seealso:: + :any:`Window.start_text_input` + https://wiki.libsdl.org/SDL3/SDL_Capitalization + + .. versionadded:: 19.1 + """ + + NONE = lib.SDL_CAPITALIZE_NONE + """No auto-capitalization will be done.""" + SENTENCES = lib.SDL_CAPITALIZE_SENTENCES + """The first letter of sentences will be capitalized.""" + WORDS = lib.SDL_CAPITALIZE_WORDS + """The first letter of words will be capitalized.""" + LETTERS = lib.SDL_CAPITALIZE_LETTERS + """All letters will be capitalized.""" + + class _TempSurface: """Holds a temporary surface derived from a NumPy array.""" def __init__(self, pixels: ArrayLike) -> None: self._array: NDArray[np.uint8] = np.ascontiguousarray(pixels, dtype=np.uint8) - if len(self._array.shape) != 3: + if len(self._array.shape) != 3: # noqa: PLR2004 msg = f"NumPy shape must be 3D [y, x, ch] (got {self._array.shape})" raise TypeError(msg) - if not (3 <= self._array.shape[2] <= 4): + if not (3 <= self._array.shape[2] <= 4): # noqa: PLR2004 msg = f"NumPy array must have RGB or RGBA channels. (got {self._array.shape})" raise TypeError(msg) self.p = ffi.gc( - lib.SDL_CreateRGBSurfaceFrom( - ffi.from_buffer("void*", self._array), - self._array.shape[1], # Width. - self._array.shape[0], # Height. - self._array.shape[2] * 8, # Bit depth. - self._array.strides[1], # Pitch. - 0x000000FF, - 0x0000FF00, - 0x00FF0000, - 0xFF000000 if self._array.shape[2] == 4 else 0, + _check_p( + lib.SDL_CreateSurfaceFrom( + self._array.shape[1], + self._array.shape[0], + lib.SDL_PIXELFORMAT_RGBA32 if self._array.shape[2] == 4 else lib.SDL_PIXELFORMAT_RGB24, # noqa: PLR2004 + ffi.from_buffer("void*", self._array), + self._array.strides[0], + ) ), - lib.SDL_FreeSurface, + lib.SDL_DestroySurface, ) class Window: - """An SDL2 Window object.""" + """An SDL Window object. - def __init__(self, sdl_window_p: Any) -> None: + Created from :any:`tcod.sdl.video.new_window` when working with SDL directly. + + When using the libtcod :any:`Context` you can access its `Window` via :any:`Context.sdl_window`. + """ + + def __init__(self, sdl_window_p: Any | int) -> None: # noqa: ANN401 + """Wrap a SDL_Window pointer or SDL WindowID. + + .. versionchanged:: 21.0 + Now accepts `int` types as an SDL WindowID. + """ + if isinstance(sdl_window_p, int): + sdl_window_p = _check_p(lib.SDL_GetWindowFromID(sdl_window_p)) if ffi.typeof(sdl_window_p) is not ffi.typeof("struct SDL_Window*"): msg = "sdl_window_p must be {!r} type (was {!r}).".format( ffi.typeof("struct SDL_Window*"), ffi.typeof(sdl_window_p) @@ -133,9 +192,23 @@ def __init__(self, sdl_window_p: Any) -> None: raise TypeError(msg) self.p = sdl_window_p - def __eq__(self, other: Any) -> bool: + def __eq__(self, other: object) -> bool: + """Return True if `self` and `other` wrap the same window.""" + if not isinstance(other, Window): + return NotImplemented return bool(self.p == other.p) + def __hash__(self) -> int: + """Return the hash of this instances SDL window pointer.""" + return hash(self.p) + + def _as_property_pointer(self) -> Any: # noqa: ANN401 + return self.p + + @classmethod + def _from_property_pointer(cls, raw_cffi_pointer: Any, /) -> Self: # noqa: ANN401 + return cls(raw_cffi_pointer) + def set_icon(self, pixels: ArrayLike) -> None: """Set the window icon from an image. @@ -217,24 +290,19 @@ def flags(self) -> WindowFlags: return WindowFlags(lib.SDL_GetWindowFlags(self.p)) @property - def fullscreen(self) -> int: + def fullscreen(self) -> bool: """Get or set the fullscreen status of this window. - Can be set to the :any:`WindowFlags.FULLSCREEN` or :any:`WindowFlags.FULLSCREEN_DESKTOP` flags. - Example:: # Toggle fullscreen. window: tcod.sdl.video.Window - if window.fullscreen: - window.fullscreen = False # Set windowed mode. - else: - window.fullscreen = tcod.sdl.video.WindowFlags.FULLSCREEN_DESKTOP + window.fullscreen = not window.fullscreen """ - return self.flags & (WindowFlags.FULLSCREEN | WindowFlags.FULLSCREEN_DESKTOP) + return bool(self.flags & WindowFlags.FULLSCREEN) @fullscreen.setter - def fullscreen(self, value: int) -> None: + def fullscreen(self, value: bool) -> None: _check(lib.SDL_SetWindowFullscreen(self.p, value)) @property @@ -266,26 +334,51 @@ def opacity(self) -> float: Will error if you try to set this and opacity isn't supported. """ - out = ffi.new("float*") - _check(lib.SDL_GetWindowOpacity(self.p, out)) - return float(out[0]) + return float(lib.SDL_GetWindowOpacity(self.p)) @opacity.setter def opacity(self, value: float) -> None: _check(lib.SDL_SetWindowOpacity(self.p, value)) @property + @deprecated("This attribute as been split into mouse_grab and keyboard_grab") def grab(self) -> bool: """Get or set this windows input grab mode. - .. seealso:: - https://wiki.libsdl.org/SDL_SetWindowGrab + .. deprecated:: 19.0 + This attribute as been split into :any:`mouse_grab` and :any:`keyboard_grab`. """ - return bool(lib.SDL_GetWindowGrab(self.p)) + return self.mouse_grab @grab.setter def grab(self, value: bool) -> None: - lib.SDL_SetWindowGrab(self.p, value) + self.mouse_grab = value + + @property + def mouse_grab(self) -> bool: + """Get or set this windows mouse input grab mode. + + .. versionadded:: 19.0 + """ + return bool(lib.SDL_GetWindowMouseGrab(self.p)) + + @mouse_grab.setter + def mouse_grab(self, value: bool, /) -> None: + lib.SDL_SetWindowMouseGrab(self.p, value) + + @property + def keyboard_grab(self) -> bool: + """Get or set this windows keyboard input grab mode. + + https://wiki.libsdl.org/SDL3/SDL_SetWindowKeyboardGrab + + .. versionadded:: 19.0 + """ + return bool(lib.SDL_GetWindowKeyboardGrab(self.p)) + + @keyboard_grab.setter + def keyboard_grab(self, value: bool, /) -> None: + lib.SDL_SetWindowKeyboardGrab(self.p, value) @property def mouse_rect(self) -> tuple[int, int, int, int] | None: @@ -295,13 +388,11 @@ def mouse_rect(self) -> tuple[int, int, int, int] | None: .. versionadded:: 13.5 """ - _version_at_least((2, 0, 18)) rect = lib.SDL_GetWindowMouseRect(self.p) return (rect.x, rect.y, rect.w, rect.h) if rect else None @mouse_rect.setter def mouse_rect(self, rect: tuple[int, int, int, int] | None) -> None: - _version_at_least((2, 0, 18)) _check(lib.SDL_SetWindowMouseRect(self.p, (rect,) if rect else ffi.NULL)) @_required_version((2, 0, 16)) @@ -333,8 +424,97 @@ def hide(self) -> None: """Hide this window.""" lib.SDL_HideWindow(self.p) + @property + def relative_mouse_mode(self) -> bool: + """Enable or disable relative mouse mode which will lock and hide the mouse and only report mouse motion. + + .. seealso:: + :any:`tcod.sdl.mouse.capture` + https://wiki.libsdl.org/SDL_SetWindowRelativeMouseMode + """ + return bool(lib.SDL_GetWindowRelativeMouseMode(self.p)) + + @relative_mouse_mode.setter + def relative_mouse_mode(self, enable: bool, /) -> None: + _check(lib.SDL_SetWindowRelativeMouseMode(self.p, enable)) + + def start_text_input( + self, + *, + type: TextInputType = TextInputType.TEXT, # noqa: A002 + capitalization: Capitalization | None = None, + autocorrect: bool = True, + multiline: bool | None = None, + android_type: int | None = None, + ) -> None: + """Start receiving text input events supporting Unicode. This may open an on-screen keyboard. + + This method is meant to be paired with :any:`set_text_input_area`. + + Args: + type: Type of text being inputted, see :any:`TextInputType` + capitalization: Capitalization hint, default is based on `type` given, see :any:`Capitalization`. + autocorrect: Enable auto completion and auto correction. + multiline: Allow multiple lines of text. + android_type: Input type for Android, see SDL docs. + + Example:: + + context: tcod.context.Context # Assuming tcod context is used + + if context.sdl_window: + context.sdl_window.start_text_input() + + ... # Handle Unicode input using TextInput events + + context.sdl_window.stop_text_input() # Close on-screen keyboard when done + + .. seealso:: + :any:`stop_text_input` + :any:`set_text_input_area` + :any:`TextInput` + https://wiki.libsdl.org/SDL3/SDL_StartTextInputWithProperties + + .. versionadded:: 19.1 + """ + props = Properties() + props[("SDL_PROP_TEXTINPUT_TYPE_NUMBER", int)] = int(type) + if capitalization is not None: + props[("SDL_PROP_TEXTINPUT_CAPITALIZATION_NUMBER", int)] = int(capitalization) + props[("SDL_PROP_TEXTINPUT_AUTOCORRECT_BOOLEAN", bool)] = autocorrect + if multiline is not None: + props[("SDL_PROP_TEXTINPUT_MULTILINE_BOOLEAN", bool)] = multiline + if android_type is not None: + props[("SDL_PROP_TEXTINPUT_ANDROID_INPUTTYPE_NUMBER", int)] = int(android_type) + _check(lib.SDL_StartTextInputWithProperties(self.p, props.p)) + + def set_text_input_area(self, rect: tuple[int, int, int, int], cursor: int) -> None: + """Assign the area used for entering Unicode text input. + + Args: + rect: `(x, y, width, height)` rectangle used for text input + cursor: Cursor X position, relative to `rect[0]` + + .. seealso:: + :any:`start_text_input` + https://wiki.libsdl.org/SDL3/SDL_SetTextInputArea + + .. versionadded:: 19.1 + """ + _check(lib.SDL_SetTextInputArea(self.p, (rect,), cursor)) + + def stop_text_input(self) -> None: + """Stop receiving text events for this window and close relevant on-screen keyboards. + + .. seealso:: + :any:`start_text_input` + + .. versionadded:: 19.1 + """ + _check(lib.SDL_StopTextInput(self.p)) + -def new_window( +def new_window( # noqa: PLR0913 width: int, height: int, *, @@ -363,11 +543,18 @@ def new_window( .. seealso:: :func:`tcod.sdl.render.new_renderer` """ - x = x if x is not None else int(lib.SDL_WINDOWPOS_UNDEFINED) - y = y if y is not None else int(lib.SDL_WINDOWPOS_UNDEFINED) if title is None: title = sys.argv[0] - window_p = ffi.gc(lib.SDL_CreateWindow(title.encode("utf-8"), x, y, width, height, flags), lib.SDL_DestroyWindow) + window_props = Properties() + window_props[(tcod.sdl.constants.SDL_PROP_WINDOW_CREATE_FLAGS_NUMBER, int)] = flags + window_props[(tcod.sdl.constants.SDL_PROP_WINDOW_CREATE_TITLE_STRING, str)] = title + if x is not None: + window_props[(tcod.sdl.constants.SDL_PROP_WINDOW_CREATE_X_NUMBER, int)] = x + if y is not None: + window_props[(tcod.sdl.constants.SDL_PROP_WINDOW_CREATE_Y_NUMBER, int)] = y + window_props[(tcod.sdl.constants.SDL_PROP_WINDOW_CREATE_WIDTH_NUMBER, int)] = width + window_props[(tcod.sdl.constants.SDL_PROP_WINDOW_CREATE_HEIGHT_NUMBER, int)] = height + window_p = ffi.gc(lib.SDL_CreateWindowWithProperties(window_props.p), lib.SDL_DestroyWindow) return Window(_check_p(window_p)) @@ -377,7 +564,7 @@ def get_grabbed_window() -> Window | None: return Window(sdl_window_p) if sdl_window_p else None -def screen_saver_allowed(allow: bool | None = None) -> bool: +def screen_saver_allowed(allow: bool | None = None) -> bool: # noqa: FBT001 """Allow or prevent a screen saver from being displayed and return the current allowed status. If `allow` is `None` then only the current state is returned. @@ -401,4 +588,4 @@ def screen_saver_allowed(allow: bool | None = None) -> bool: lib.SDL_EnableScreenSaver() else: lib.SDL_DisableScreenSaver() - return bool(lib.SDL_IsScreenSaverEnabled()) + return bool(lib.SDL_ScreenSaverEnabled()) diff --git a/tcod/tcod.c b/tcod/tcod.c index 75994b85..91f7cc6f 100644 --- a/tcod/tcod.c +++ b/tcod/tcod.c @@ -18,7 +18,7 @@ */ int bresenham(int x1, int y1, int x2, int y2, int n, int* __restrict out) { // Bresenham length is Chebyshev distance. - int length = MAX(abs(x1 - x2), abs(y1 - y2)) + 1; + int length = TCOD_MAX(abs(x1 - x2), abs(y1 - y2)) + 1; if (!out) { return length; } if (n < length) { return TCOD_set_errorv("Bresenham output length mismatched."); } TCOD_bresenham_data_t bresenham; diff --git a/tcod/tcod.py b/tcod/tcod.py index 6fab4ff4..39240194 100644 --- a/tcod/tcod.py +++ b/tcod/tcod.py @@ -6,6 +6,7 @@ Read the documentation online: https://python-tcod.readthedocs.io/en/latest/ """ + from __future__ import annotations import warnings @@ -21,7 +22,7 @@ image, libtcodpy, los, - map, + map, # noqa: A004 noise, path, random, @@ -78,7 +79,6 @@ def __getattr__(name: str, stacklevel: int = 1) -> Any: # noqa: ANN401 "console", "context", "event", - "tileset", "image", "los", "map", @@ -86,4 +86,5 @@ def __getattr__(name: str, stacklevel: int = 1) -> Any: # noqa: ANN401 "path", "random", "tileset", + "tileset", ] diff --git a/tcod/tdl.c b/tcod/tdl.c deleted file mode 100644 index c293428f..00000000 --- a/tcod/tdl.c +++ /dev/null @@ -1,201 +0,0 @@ -/* extra functions provided for the python-tdl library */ -#include "tdl.h" - -#include "../libtcod/src/libtcod/wrappers.h" - -void SDL_main(void){}; - -TCOD_value_t TDL_list_get_union(TCOD_list_t l, int idx) { - TCOD_value_t item; - item.custom = TCOD_list_get(l, idx); - return item; -} - -bool TDL_list_get_bool(TCOD_list_t l, int idx) { return TDL_list_get_union(l, idx).b; } - -char TDL_list_get_char(TCOD_list_t l, int idx) { return TDL_list_get_union(l, idx).c; } - -int TDL_list_get_int(TCOD_list_t l, int idx) { return TDL_list_get_union(l, idx).i; } - -float TDL_list_get_float(TCOD_list_t l, int idx) { return TDL_list_get_union(l, idx).f; } - -char* TDL_list_get_string(TCOD_list_t l, int idx) { return TDL_list_get_union(l, idx).s; } - -TCOD_color_t TDL_list_get_color(TCOD_list_t l, int idx) { return TDL_list_get_union(l, idx).col; } - -TCOD_dice_t TDL_list_get_dice(TCOD_list_t l, int idx) { return TDL_list_get_union(l, idx).dice; } - -/* get a TCOD color type from a 0xRRGGBB formatted integer */ -TCOD_color_t TDL_color_from_int(int color) { - TCOD_color_t tcod_color = {(color >> 16) & 0xff, (color >> 8) & 0xff, color & 0xff}; - return tcod_color; -} - -int TDL_color_to_int(TCOD_color_t* color) { return (color->r << 16) | (color->g << 8) | color->b; } - -int* TDL_color_int_to_array(int color) { - static int array[3]; - array[0] = (color >> 16) & 0xff; - array[1] = (color >> 8) & 0xff; - array[2] = color & 0xff; - return array; -} - -int TDL_color_RGB(int r, int g, int b) { return ((r & 0xff) << 16) | ((g & 0xff) << 8) | (b & 0xff); } - -int TDL_color_HSV(float h, float s, float v) { - TCOD_color_t tcod_color = TCOD_color_HSV(h, s, v); - return TDL_color_to_int(&tcod_color); -} - -bool TDL_color_equals(int c1, int c2) { return (c1 == c2); } - -int TDL_color_add(int c1, int c2) { - TCOD_color_t tc1 = TDL_color_from_int(c1); - TCOD_color_t tc2 = TDL_color_from_int(c2); - tc1 = TCOD_color_add(tc1, tc2); - return TDL_color_to_int(&tc1); -} - -int TDL_color_subtract(int c1, int c2) { - TCOD_color_t tc1 = TDL_color_from_int(c1); - TCOD_color_t tc2 = TDL_color_from_int(c2); - tc1 = TCOD_color_subtract(tc1, tc2); - return TDL_color_to_int(&tc1); -} - -int TDL_color_multiply(int c1, int c2) { - TCOD_color_t tc1 = TDL_color_from_int(c1); - TCOD_color_t tc2 = TDL_color_from_int(c2); - tc1 = TCOD_color_multiply(tc1, tc2); - return TDL_color_to_int(&tc1); -} - -int TDL_color_multiply_scalar(int c, float value) { - TCOD_color_t tc = TDL_color_from_int(c); - tc = TCOD_color_multiply_scalar(tc, value); - return TDL_color_to_int(&tc); -} - -int TDL_color_lerp(int c1, int c2, float coef) { - TCOD_color_t tc1 = TDL_color_from_int(c1); - TCOD_color_t tc2 = TDL_color_from_int(c2); - tc1 = TCOD_color_lerp(tc1, tc2, coef); - return TDL_color_to_int(&tc1); -} - -float TDL_color_get_hue(int color) { - TCOD_color_t tcod_color = TDL_color_from_int(color); - return TCOD_color_get_hue(tcod_color); -} -float TDL_color_get_saturation(int color) { - TCOD_color_t tcod_color = TDL_color_from_int(color); - return TCOD_color_get_saturation(tcod_color); -} -float TDL_color_get_value(int color) { - TCOD_color_t tcod_color = TDL_color_from_int(color); - return TCOD_color_get_value(tcod_color); -} -int TDL_color_set_hue(int color, float h) { - TCOD_color_t tcod_color = TDL_color_from_int(color); - TCOD_color_set_hue(&tcod_color, h); - return TDL_color_to_int(&tcod_color); -} -int TDL_color_set_saturation(int color, float h) { - TCOD_color_t tcod_color = TDL_color_from_int(color); - TCOD_color_set_saturation(&tcod_color, h); - return TDL_color_to_int(&tcod_color); -} -int TDL_color_set_value(int color, float h) { - TCOD_color_t tcod_color = TDL_color_from_int(color); - TCOD_color_set_value(&tcod_color, h); - return TDL_color_to_int(&tcod_color); -} -int TDL_color_shift_hue(int color, float hue_shift) { - TCOD_color_t tcod_color = TDL_color_from_int(color); - TCOD_color_shift_hue(&tcod_color, hue_shift); - return TDL_color_to_int(&tcod_color); -} -int TDL_color_scale_HSV(int color, float scoef, float vcoef) { - TCOD_color_t tcod_color = TDL_color_from_int(color); - TCOD_color_scale_HSV(&tcod_color, scoef, vcoef); - return TDL_color_to_int(&tcod_color); -} - -#define TRANSPARENT_BIT 1 -#define WALKABLE_BIT 2 -#define FOV_BIT 4 - -/* set map transparent and walkable flags from a buffer */ -void TDL_map_data_from_buffer(TCOD_map_t map, uint8_t* buffer) { - int width = TCOD_map_get_width(map); - int height = TCOD_map_get_height(map); - int x; - int y; - for (y = 0; y < height; y++) { - for (x = 0; x < width; x++) { - int i = y * width + x; - TCOD_map_set_properties(map, x, y, (buffer[i] & TRANSPARENT_BIT) != 0, (buffer[i] & WALKABLE_BIT) != 0); - } - } -} - -/* get fov from tcod map */ -void TDL_map_fov_to_buffer(TCOD_map_t map, uint8_t* buffer, bool cumulative) { - int width = TCOD_map_get_width(map); - int height = TCOD_map_get_height(map); - int x; - int y; - for (y = 0; y < height; y++) { - for (x = 0; x < width; x++) { - int i = y * width + x; - if (!cumulative) { buffer[i] &= ~FOV_BIT; } - if (TCOD_map_is_in_fov(map, x, y)) { buffer[i] |= FOV_BIT; } - } - } -} - -/* set functions are called conditionally for ch/fg/bg (-1 is ignored) - colors are converted to TCOD_color_t types in C and is much faster than in - Python. - Also Python indexing is used, negative x/y will index to (width-x, etc.) */ -int TDL_console_put_char_ex(TCOD_console_t console, int x, int y, int ch, int fg, int bg, TCOD_bkgnd_flag_t blend) { - int width = TCOD_console_get_width(console); - int height = TCOD_console_get_height(console); - TCOD_color_t color; - - if (x < -width || x >= width || y < -height || y >= height) { return -1; /* outside of console */ } - - /* normalize x, y */ - if (x < 0) { x += width; }; - if (y < 0) { y += height; }; - - if (ch != -1) { TCOD_console_set_char(console, x, y, ch); } - if (fg != -1) { - color = TDL_color_from_int(fg); - TCOD_console_set_char_foreground(console, x, y, color); - } - if (bg != -1) { - color = TDL_color_from_int(bg); - TCOD_console_set_char_background(console, x, y, color, blend); - } - return 0; -} - -int TDL_console_get_bg(TCOD_console_t console, int x, int y) { - TCOD_color_t tcod_color = TCOD_console_get_char_background(console, x, y); - return TDL_color_to_int(&tcod_color); -} - -int TDL_console_get_fg(TCOD_console_t console, int x, int y) { - TCOD_color_t tcod_color = TCOD_console_get_char_foreground(console, x, y); - return TDL_color_to_int(&tcod_color); -} - -void TDL_console_set_bg(TCOD_console_t console, int x, int y, int color, TCOD_bkgnd_flag_t flag) { - TCOD_console_set_char_background(console, x, y, TDL_color_from_int(color), flag); -} - -void TDL_console_set_fg(TCOD_console_t console, int x, int y, int color) { - TCOD_console_set_char_foreground(console, x, y, TDL_color_from_int(color)); -} diff --git a/tcod/tdl.h b/tcod/tdl.h deleted file mode 100644 index 71a7492e..00000000 --- a/tcod/tdl.h +++ /dev/null @@ -1,54 +0,0 @@ -#ifndef PYTHON_TCOD_TDL_H_ -#define PYTHON_TCOD_TDL_H_ - -#include "../libtcod/src/libtcod/libtcod.h" - -/* TDL FUNCTIONS ---------------------------------------------------------- */ - -TCOD_value_t TDL_list_get_union(TCOD_list_t l, int idx); -bool TDL_list_get_bool(TCOD_list_t l, int idx); -char TDL_list_get_char(TCOD_list_t l, int idx); -int TDL_list_get_int(TCOD_list_t l, int idx); -float TDL_list_get_float(TCOD_list_t l, int idx); -char* TDL_list_get_string(TCOD_list_t l, int idx); -TCOD_color_t TDL_list_get_color(TCOD_list_t l, int idx); -TCOD_dice_t TDL_list_get_dice(TCOD_list_t l, int idx); -/*bool (*TDL_parser_new_property_func)(const char *propname, TCOD_value_type_t - * type, TCOD_value_t *value);*/ - -/* color functions modified to use integers instead of structs */ -TCOD_color_t TDL_color_from_int(int color); -int TDL_color_to_int(TCOD_color_t* color); -int* TDL_color_int_to_array(int color); -int TDL_color_RGB(int r, int g, int b); -int TDL_color_HSV(float h, float s, float v); -bool TDL_color_equals(int c1, int c2); -int TDL_color_add(int c1, int c2); -int TDL_color_subtract(int c1, int c2); -int TDL_color_multiply(int c1, int c2); -int TDL_color_multiply_scalar(int c, float value); -int TDL_color_lerp(int c1, int c2, float coef); -float TDL_color_get_hue(int color); -float TDL_color_get_saturation(int color); -float TDL_color_get_value(int color); -int TDL_color_set_hue(int color, float h); -int TDL_color_set_saturation(int color, float h); -int TDL_color_set_value(int color, float h); -int TDL_color_shift_hue(int color, float hue_shift); -int TDL_color_scale_HSV(int color, float scoef, float vcoef); - -/* map data functions using a bitmap of: - * 1 = is_transparant - * 2 = is_walkable - * 4 = in_fov - */ -void TDL_map_data_from_buffer(TCOD_map_t map, uint8_t* buffer); -void TDL_map_fov_to_buffer(TCOD_map_t map, uint8_t* buffer, bool cumulative); - -int TDL_console_put_char_ex(TCOD_console_t console, int x, int y, int ch, int fg, int bg, TCOD_bkgnd_flag_t flag); -int TDL_console_get_bg(TCOD_console_t console, int x, int y); -int TDL_console_get_fg(TCOD_console_t console, int x, int y); -void TDL_console_set_bg(TCOD_console_t console, int x, int y, int color, TCOD_bkgnd_flag_t flag); -void TDL_console_set_fg(TCOD_console_t console, int x, int y, int color); - -#endif /* PYTHON_TCOD_TDL_H_ */ diff --git a/tcod/tileset.py b/tcod/tileset.py index b53b0149..58ddf058 100644 --- a/tcod/tileset.py +++ b/tcod/tileset.py @@ -1,44 +1,57 @@ """Tileset and font related functions. -Tilesets can be loaded as a whole from tile-sheets or True-Type fonts, or they -can be put together from multiple tile images by loading them separately -using :any:`Tileset.set_tile`. +If you want to load a tileset from a common tileset image then you only need :any:`tcod.tileset.load_tilesheet`. -A major restriction with libtcod is that all tiles must be the same size and -tiles can't overlap when rendered. For sprite-based rendering it can be -useful to use `an alternative library for graphics rendering +Tilesets can be loaded as a whole from tile-sheets or True-Type fonts, +or they can be put together from multiple tile images by loading them separately using :any:`Tileset.__setitem__`. + +A major restriction with libtcod is that all tiles must be the same size and tiles can't overlap when rendered. +For sprite-based rendering it can be useful to use `an alternative library for graphics rendering `_ while continuing to use python-tcod's pathfinding and field-of-view algorithms. """ + from __future__ import annotations +import copy import itertools -from os import PathLike +from collections.abc import Iterator, Mapping, MutableMapping from pathlib import Path -from typing import Any, Iterable +from typing import TYPE_CHECKING, Any, SupportsInt, overload import numpy as np -from numpy.typing import ArrayLike, NDArray +from typing_extensions import Self, deprecated -import tcod.console -from tcod._internal import _check, _console, _raise_tcod_error, deprecate +from tcod._internal import _check, _check_p, _console, _path_encode, _raise_tcod_error from tcod.cffi import ffi, lib +if TYPE_CHECKING: + from collections.abc import Iterable + from os import PathLike + + from numpy.typing import ArrayLike, NDArray + + import tcod.console + -class Tileset: +class Tileset(MutableMapping[int, "NDArray[np.uint8]"]): """A collection of graphical tiles. - This class is provisional, the API may change in the future. + .. versionchanged:: 20.1 + Is now a :class:`collections.abc.MutableMapping` type. """ def __init__(self, tile_width: int, tile_height: int) -> None: - self._tileset_p = ffi.gc( - lib.TCOD_tileset_new(tile_width, tile_height), - lib.TCOD_tileset_delete, + """Initialize the tileset.""" + self._tileset_p = _check_p( + ffi.gc( + lib.TCOD_tileset_new(tile_width, tile_height), + lib.TCOD_tileset_delete, + ) ) @classmethod - def _claim(cls, cdata: Any) -> Tileset: + def _claim(cls, cdata: Any) -> Tileset: # noqa: ANN401 """Return a new Tileset that owns the provided TCOD_Tileset* object.""" self = object.__new__(cls) if cdata == ffi.NULL: @@ -48,7 +61,7 @@ def _claim(cls, cdata: Any) -> Tileset: return self @classmethod - def _from_ref(cls, tileset_p: Any) -> Tileset: + def _from_ref(cls, tileset_p: Any) -> Tileset: # noqa: ANN401 self = object.__new__(cls) self._tileset_p = tileset_p return self @@ -68,28 +81,105 @@ def tile_shape(self) -> tuple[int, int]: """Shape (height, width) of the tile in pixels.""" return self.tile_height, self.tile_width - def __contains__(self, codepoint: int) -> bool: + def __copy__(self) -> Self: + """Return a clone of this tileset. + + This is not an exact copy. :any:`remap` will not work on the clone. + + .. versionadded:: 20.1 + """ + clone = self.__class__(self.tile_width, self.tile_height) + for codepoint, tile in self.items(): + clone[codepoint] = tile + return clone + + @staticmethod + def _iter_items( + tiles: Iterable[tuple[int, ArrayLike | NDArray[np.uint8]]] | Mapping[int, ArrayLike | NDArray[np.uint8]], / + ) -> Iterable[tuple[int, ArrayLike | NDArray[np.uint8]]]: + """Convert a potential mapping to an iterator.""" + if isinstance(tiles, Mapping): + return tiles.items() # pyright: ignore[reportReturnType] + return tiles + + def __iadd__( + self, + other: Iterable[tuple[int, ArrayLike | NDArray[np.uint8]]] | Mapping[int, ArrayLike | NDArray[np.uint8]], + /, + ) -> Self: + """Add tiles to this tileset inplace, prefers replacing tiles. + + .. versionadded:: 20.1 + """ + for codepoint, tile in self._iter_items(other): + self[codepoint] = tile + return self + + def __add__( + self, + other: Iterable[tuple[int, ArrayLike | NDArray[np.uint8]]] | Mapping[int, ArrayLike | NDArray[np.uint8]], + /, + ) -> Self: + """Combine tiles with this tileset, prefers replacing tiles. + + .. versionadded:: 20.1 + """ + clone = copy.copy(self) + clone += other + return clone + + def __ior__( + self, + other: Iterable[tuple[int, ArrayLike | NDArray[np.uint8]]] | Mapping[int, ArrayLike | NDArray[np.uint8]], + /, + ) -> Self: + """Add tiles to this tileset inplace, keeps existing tiles instead of replacing them. + + .. versionadded:: 20.1 + """ + for codepoint, tile in self._iter_items(other): + if codepoint not in self: + self[codepoint] = tile + return self + + def __or__( + self, + other: Iterable[tuple[int, ArrayLike | NDArray[np.uint8]]] | Mapping[int, ArrayLike | NDArray[np.uint8]], + /, + ) -> Self: + """Combine tiles with this tileset, prefers keeping existing tiles instead of replacing them. + + .. versionadded:: 20.1 + """ + clone = copy.copy(self) + clone |= other + return clone + + def __contains__(self, codepoint: object, /) -> bool: """Test if a tileset has a codepoint with ``n in tileset``.""" - return bool(lib.TCOD_tileset_get_tile_(self._tileset_p, codepoint, ffi.NULL) == 0) + if not isinstance(codepoint, SupportsInt): + return False + codepoint = int(codepoint) + if not 0 <= codepoint < self._tileset_p.character_map_length: + return False + return bool(self._get_character_map()[int(codepoint)] > 0) - def get_tile(self, codepoint: int) -> NDArray[np.uint8]: - """Return a copy of a tile for the given codepoint. + def __getitem__(self, codepoint: int, /) -> NDArray[np.uint8]: + """Return the RGBA tile data for the given codepoint. - If the tile does not exist yet then a blank array will be returned. + The tile will have a shape of (height, width, rgba) and a dtype of uint8. + Note that most grey-scale tilesets will only use the alpha channel with a solid white color channel. - The tile will have a shape of (height, width, rgba) and a dtype of - uint8. Note that most grey-scale tiles will only use the alpha - channel and will usually have a solid white color channel. + .. versionadded:: 20.1 """ - tile: NDArray[np.uint8] = np.zeros((*self.tile_shape, 4), dtype=np.uint8) - lib.TCOD_tileset_get_tile_( - self._tileset_p, - codepoint, - ffi.from_buffer("struct TCOD_ColorRGBA*", tile), + if codepoint not in self: + raise KeyError(codepoint) + tile_p = lib.TCOD_tileset_get_tile(self._tileset_p, codepoint) + return np.frombuffer(ffi.buffer(tile_p[0 : self.tile_shape[0] * self.tile_shape[1]]), dtype=np.uint8).reshape( + *self.tile_shape, 4, copy=True ) - return tile - def set_tile(self, codepoint: int, tile: ArrayLike | NDArray[np.uint8]) -> None: + def __setitem__(self, codepoint: int, tile: ArrayLike | NDArray[np.uint8], /) -> None: """Upload a tile into this array. Args: @@ -115,28 +205,29 @@ def set_tile(self, codepoint: int, tile: ArrayLike | NDArray[np.uint8]) -> None: # Normal usage when a tile already has its own alpha channel. # The loaded tile must be the correct shape for the tileset you assign it to. # The tile is assigned to a private use area and will not conflict with any exiting codepoint. - tileset.set_tile(0x100000, imageio.load("rgba_tile.png")) + tileset[0x100000] = imageio.imread("rgba_tile.png") # Load a greyscale tile. - tileset.set_tile(0x100001, imageio.load("greyscale_tile.png"), pilmode="L") + tileset[0x100001] = imageio.imread("greyscale_tile.png", mode="L") # If you are stuck with an RGB array then you can use the red channel as the input: `rgb[:, :, 0]` # Loads an RGB sprite without a background. - tileset.set_tile(0x100002, imageio.load("rgb_no_background.png", pilmode="RGBA")) + tileset[0x100002] = imageio.imread("rgb_no_background.png", mode="RGBA") # If you're stuck with an RGB array then you can pad the channel axis with an alpha of 255: # rgba = np.pad(rgb, pad_width=((0, 0), (0, 0), (0, 1)), constant_values=255) # Loads an RGB sprite with a key color background. KEY_COLOR = np.asarray((255, 0, 255), dtype=np.uint8) - sprite_rgb = imageio.load("rgb_tile.png") + sprite_rgb = imageio.imread("rgb_tile.png") # Compare the RGB colors to KEY_COLOR, compress full matches to a 2D mask. sprite_mask = (sprite_rgb != KEY_COLOR).all(axis=2) # Generate the alpha array, with 255 as the foreground and 0 as the background. sprite_alpha = sprite_mask.astype(np.uint8) * 255 # Combine the RGB and alpha arrays into an RGBA array. sprite_rgba = np.append(sprite_rgb, sprite_alpha, axis=2) - tileset.set_tile(0x100003, sprite_rgba) + tileset[0x100003] = sprite_rgba + .. versionadded:: 20.1 """ tile = np.ascontiguousarray(tile, dtype=np.uint8) if tile.shape == self.tile_shape: @@ -147,7 +238,7 @@ def set_tile(self, codepoint: int, tile: ArrayLike | NDArray[np.uint8]) -> None: required = (*self.tile_shape, 4) if tile.shape != required: note = "" - if len(tile.shape) == 3 and tile.shape[2] == 3: + if len(tile.shape) == 3 and tile.shape[2] == 3: # noqa: PLR2004 note = ( "\nNote: An RGB array is too ambiguous," " an alpha channel must be added to this array to divide the background/foreground areas." @@ -161,11 +252,71 @@ def set_tile(self, codepoint: int, tile: ArrayLike | NDArray[np.uint8]) -> None: ) return None + def _get_character_map(self) -> NDArray[np.intc]: + """Return the internal character mapping as an array. + + This reference will break if the tileset is modified. + """ + return np.frombuffer( + ffi.buffer(self._tileset_p.character_map[0 : self._tileset_p.character_map_length]), dtype=np.intc + ) + + def __delitem__(self, codepoint: int, /) -> None: + """Unmap a `codepoint` from this tileset. + + Tilesets are optimized for set-and-forget, deleting a tile may not free up memory. + + .. versionadded:: 20.1 + """ + if codepoint not in self: + raise KeyError(codepoint) + self._get_character_map()[codepoint] = 0 + + def __len__(self) -> int: + """Return the total count of codepoints assigned in this tileset. + + .. versionadded:: 20.1 + """ + return int((self._get_character_map() > 0).sum()) + + def __iter__(self) -> Iterator[int]: + """Iterate over the assigned codepoints of this tileset. + + .. versionadded:: 20.1 + """ + # tolist makes a copy, otherwise the reference to character_map can dangle during iteration + for i, v in enumerate(self._get_character_map().tolist()): + if v: + yield i + + def get_tile(self, codepoint: int) -> NDArray[np.uint8]: + """Return a copy of a tile for the given codepoint. + + If the tile does not exist then a blank zero array will be returned. + + The tile will have a shape of (height, width, rgba) and a dtype of + uint8. Note that most grey-scale tiles will only use the alpha + channel and will usually have a solid white color channel. + """ + try: + return self[codepoint] + except KeyError: + return np.zeros((*self.tile_shape, 4), dtype=np.uint8) + + @deprecated("Assign to a tile using 'tileset[codepoint] = tile' instead.") + def set_tile(self, codepoint: int, tile: ArrayLike | NDArray[np.uint8]) -> None: + """Upload a tile into this array. + + .. deprecated:: 20.1 + Was replaced with :any:`Tileset.__setitem__`. + Use ``tileset[codepoint] = tile`` syntax instead of this method. + """ + self[codepoint] = tile + def render(self, console: tcod.console.Console) -> NDArray[np.uint8]: """Render an RGBA array, using console with this tileset. - `console` is the Console object to render, this can not be the root - console. + `console` is the Console object to render, this can not be the root console. The output array will be a np.uint8 array with the shape of: ``(con_height * tile_height, con_width * tile_width, 4)``. @@ -180,15 +331,14 @@ def render(self, console: tcod.console.Console) -> NDArray[np.uint8]: out: NDArray[np.uint8] = np.empty((height, width, 4), np.uint8) out[:] = 9 surface_p = ffi.gc( - lib.SDL_CreateRGBSurfaceWithFormatFrom( - ffi.from_buffer("void*", out), + lib.SDL_CreateSurfaceFrom( width, height, - 32, - out.strides[0], lib.SDL_PIXELFORMAT_RGBA32, + ffi.from_buffer("void*", out), + out.strides[0], ), - lib.SDL_FreeSurface, + lib.SDL_DestroySurface, ) with surface_p, ffi.new("SDL_Surface**", surface_p) as surface_p_p: _check( @@ -211,17 +361,18 @@ def remap(self, codepoint: int, x: int, y: int = 0) -> None: Large values of `x` will wrap to the next row, so using `x` by itself is equivalent to `Tile Index` in the :any:`charmap-reference`. - This is normally used on loaded tilesheets. Other methods of Tileset - creation won't have reliable tile indexes. + This is typically used on tilesets loaded with :any:`load_tilesheet`. + Other methods of Tileset creation will not have reliable tile indexes. .. versionadded:: 11.12 """ tile_i = x + y * self._tileset_p.virtual_columns if not (0 <= tile_i < self._tileset_p.tiles_count): - raise IndexError( - "Tile %i is non-existent and can't be assigned." - " (Tileset has %i tiles.)" % (tile_i, self._tileset_p.tiles_count) + msg = ( + f"Tile {tile_i} is non-existent and can't be assigned." + f" (Tileset has {self._tileset_p.tiles_count} tiles.)" ) + raise IndexError(msg) _check( lib.TCOD_tileset_assign_tile( self._tileset_p, @@ -231,7 +382,7 @@ def remap(self, codepoint: int, x: int, y: int = 0) -> None: ) -@deprecate("Using the default tileset is deprecated.") +@deprecated("Using the default tileset is deprecated.") def get_default() -> Tileset: """Return a reference to the default Tileset. @@ -244,7 +395,7 @@ def get_default() -> Tileset: return Tileset._claim(lib.TCOD_get_default_tileset()) -@deprecate("Using the default tileset is deprecated.") +@deprecated("Using the default tileset is deprecated.") def set_default(tileset: Tileset) -> None: """Set the default tileset. @@ -268,13 +419,13 @@ def load_truetype_font(path: str | PathLike[str], tile_width: int, tile_height: This function is provisional. The API may change. """ path = Path(path).resolve(strict=True) - cdata = lib.TCOD_load_truetype_font_(bytes(path), tile_width, tile_height) + cdata = lib.TCOD_load_truetype_font_(_path_encode(path), tile_width, tile_height) if not cdata: raise RuntimeError(ffi.string(lib.TCOD_get_error())) return Tileset._claim(cdata) -@deprecate("Accessing the default tileset is deprecated.") +@deprecated("Accessing the default tileset is deprecated.") def set_truetype_font(path: str | PathLike[str], tile_width: int, tile_height: int) -> None: """Set the default tileset from a `.ttf` or `.otf` file. @@ -296,7 +447,7 @@ def set_truetype_font(path: str | PathLike[str], tile_width: int, tile_height: i Use :any:`load_truetype_font` instead. """ path = Path(path).resolve(strict=True) - if lib.TCOD_tileset_load_truetype_(bytes(path), tile_width, tile_height): + if lib.TCOD_tileset_load_truetype_(_path_encode(path), tile_width, tile_height): raise RuntimeError(ffi.string(lib.TCOD_get_error())) @@ -314,7 +465,7 @@ def load_bdf(path: str | PathLike[str]) -> Tileset: .. versionadded:: 11.10 """ path = Path(path).resolve(strict=True) - cdata = lib.TCOD_load_bdf(bytes(path)) + cdata = lib.TCOD_load_bdf(_path_encode(path)) if not cdata: raise RuntimeError(ffi.string(lib.TCOD_get_error()).decode()) return Tileset._claim(cdata) @@ -337,23 +488,53 @@ def load_tilesheet(path: str | PathLike[str], columns: int, rows: int, charmap: If `None` is used then no tiles will be mapped, you will need to use :any:`Tileset.remap` to assign codepoints to this Tileset. + Image alpha and key colors are handled automatically. + For example any tileset from the `Dwarf Fortress tileset repository `_ + will load correctly with the following example: + + Example:: + + from pathlib import Path + + import tcod.tileset + + THIS_DIR = Path(__file__, "..") # Directory of this script file + FONT = THIS_DIR / "assets/tileset.png" # Replace with any tileset from the DF tileset repository + + # Will raise FileNotFoundError if the font is missing! + tileset = tcod.tileset.load_tilesheet(FONT, 16, 16, tcod.tileset.CHARMAP_CP437) + + The tileset return value is usually passed to the `tileset` parameter of :any:`tcod.context.new`. + .. versionadded:: 11.12 """ path = Path(path).resolve(strict=True) mapping = [] if charmap is not None: mapping = list(itertools.islice(charmap, columns * rows)) - cdata = lib.TCOD_tileset_load(bytes(path), columns, rows, len(mapping), mapping) + cdata = lib.TCOD_tileset_load(_path_encode(path), columns, rows, len(mapping), mapping) if not cdata: _raise_tcod_error() return Tileset._claim(cdata) -def procedural_block_elements(*, tileset: Tileset) -> None: - """Overwrite the block element codepoints in `tileset` with procedurally generated glyphs. +@overload +@deprecated( + "Prefer assigning tiles using dictionary semantics:\n" + "'tileset += tcod.tileset.procedural_block_elements(shape=tileset.tile_shape)'" +) +def procedural_block_elements(*, tileset: Tileset) -> Tileset: ... +@overload +def procedural_block_elements(*, shape: tuple[int, int]) -> Tileset: ... + + +def procedural_block_elements(*, tileset: Tileset | None = None, shape: tuple[int, int] | None = None) -> Tileset: + """Generate and return a :any:`Tileset` with procedurally generated block elements. Args: - tileset (Tileset): A :any:`Tileset` with tiles of any shape. + tileset: A :any:`Tileset` with tiles of any shape. The codepoints of this tileset will be overwritten. + This parameter is deprecated and only shape `should` be used. + shape: The ``(height, width)`` tile size to generate. This will overwrite all of the codepoints `listed here `_ except for the shade glyphs. @@ -363,10 +544,15 @@ def procedural_block_elements(*, tileset: Tileset) -> None: .. versionadded:: 13.1 + .. versionchanged:: 20.1 + Added `shape` parameter, now returns a `Tileset`. + `tileset` parameter is deprecated. + Example:: + >>> import tcod.tileset >>> tileset = tcod.tileset.Tileset(8, 8) - >>> tcod.tileset.procedural_block_elements(tileset=tileset) + >>> tileset += tcod.tileset.procedural_block_elements(shape=tileset.tile_shape) >>> tileset.get_tile(0x259E)[:, :, 3] # "▞" Quadrant upper right and lower left. array([[ 0, 0, 0, 0, 255, 255, 255, 255], [ 0, 0, 0, 0, 255, 255, 255, 255], @@ -395,6 +581,9 @@ def procedural_block_elements(*, tileset: Tileset) -> None: [255, 255, 255, 0, 0, 0, 0, 0], [255, 255, 255, 0, 0, 0, 0, 0]], dtype=uint8) """ + if tileset is None: + assert shape is not None + tileset = Tileset(shape[1], shape[0]) quadrants: NDArray[np.uint8] = np.zeros(tileset.tile_shape, dtype=np.uint8) half_height = tileset.tile_height // 2 half_width = tileset.tile_width // 2 @@ -422,7 +611,7 @@ def procedural_block_elements(*, tileset: Tileset) -> None: ): alpha: NDArray[np.uint8] = np.asarray((quadrants & quad_mask) != 0, dtype=np.uint8) alpha *= 255 - tileset.set_tile(codepoint, alpha) + tileset[codepoint] = alpha for codepoint, axis, fraction, negative in ( (0x2581, 0, 7, True), # "▁" Lower one eighth block. @@ -446,7 +635,8 @@ def procedural_block_elements(*, tileset: Tileset) -> None: indexes[axis] = slice(divide, None) if negative else slice(None, divide) alpha = np.zeros(tileset.tile_shape, dtype=np.uint8) alpha[tuple(indexes)] = 255 - tileset.set_tile(codepoint, alpha) + tileset[codepoint] = alpha + return tileset CHARMAP_CP437 = [ diff --git a/tests/conftest.py b/tests/conftest.py index 163d0918..580f16bc 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,63 +1,77 @@ """Test directory configuration.""" + +from __future__ import annotations + import random import warnings -from typing import Callable, Iterator, Union +from typing import TYPE_CHECKING import pytest import tcod +from tcod import libtcodpy -# ruff: noqa: D103 +if TYPE_CHECKING: + from collections.abc import Callable, Iterator def pytest_addoption(parser: pytest.Parser) -> None: parser.addoption("--no-window", action="store_true", help="Skip tests which need a rendering context.") +@pytest.fixture +def uses_window(request: pytest.FixtureRequest) -> Iterator[None]: + """Marks tests which require a rendering context.""" + if request.config.getoption("--no-window"): + pytest.skip("This test needs a rendering context.") + yield None + return + + @pytest.fixture(scope="session", params=["SDL", "SDL2"]) def session_console(request: pytest.FixtureRequest) -> Iterator[tcod.console.Console]: if request.config.getoption("--no-window"): pytest.skip("This test needs a rendering context.") - FONT_FILE = "libtcod/terminal.png" - WIDTH = 12 - HEIGHT = 10 - TITLE = "libtcod-cffi tests" - FULLSCREEN = False - RENDERER = getattr(tcod, "RENDERER_" + request.param) - - tcod.console_set_custom_font(FONT_FILE) - with tcod.console_init_root(WIDTH, HEIGHT, TITLE, FULLSCREEN, RENDERER, vsync=False) as con: + font_file = "libtcod/terminal.png" + width = 12 + height = 10 + title = "libtcod-cffi tests" + fullscreen = False + renderer = getattr(libtcodpy, "RENDERER_" + request.param) + + libtcodpy.console_set_custom_font(font_file) + with libtcodpy.console_init_root(width, height, title, fullscreen, renderer, vsync=False) as con: yield con -@pytest.fixture() +@pytest.fixture def console(session_console: tcod.console.Console) -> tcod.console.Console: console = session_console - tcod.console_flush() + libtcodpy.console_flush() with warnings.catch_warnings(): warnings.simplefilter("ignore") console.default_fg = (255, 255, 255) console.default_bg = (0, 0, 0) - console.default_bg_blend = tcod.BKGND_SET - console.default_alignment = tcod.LEFT + console.default_bg_blend = libtcodpy.BKGND_SET + console.default_alignment = libtcodpy.LEFT console.clear() return console -@pytest.fixture() +@pytest.fixture def offscreen(console: tcod.console.Console) -> tcod.console.Console: """Return an off-screen console with the same size as the root console.""" return tcod.console.Console(console.width, console.height) -@pytest.fixture() -def fg() -> tcod.Color: - return tcod.Color(random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)) +@pytest.fixture +def fg() -> libtcodpy.Color: + return libtcodpy.Color(random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)) -@pytest.fixture() -def bg() -> tcod.Color: - return tcod.Color(random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)) +@pytest.fixture +def bg() -> libtcodpy.Color: + return libtcodpy.Color(random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)) def ch_ascii_int() -> int: @@ -84,6 +98,6 @@ def ch_latin1_str() -> str: "latin1_str", ] ) -def ch(request: pytest.FixtureRequest) -> Callable[[], Union[int, str]]: +def ch(request: pytest.FixtureRequest) -> Callable[[], int | str]: """Test with multiple types of ascii/latin1 characters.""" - return globals()["ch_%s" % request.param]() # type: ignore + return globals()[f"ch_{request.param}"]() # type: ignore[no-any-return] diff --git a/tests/test_console.py b/tests/test_console.py index f8fa5d91..873a7691 100644 --- a/tests/test_console.py +++ b/tests/test_console.py @@ -1,4 +1,5 @@ """Tests for tcod.console.""" + import pickle from pathlib import Path @@ -8,14 +9,12 @@ import tcod import tcod.console -# ruff: noqa: D103 - def test_array_read_write() -> None: console = tcod.console.Console(width=12, height=10) - FG = (255, 254, 253) - BG = (1, 2, 3) - CH = ord("&") + FG = (255, 254, 253) # noqa: N806 + BG = (1, 2, 3) # noqa: N806 + CH = ord("&") # noqa: N806 with pytest.warns(): tcod.console_put_char_ex(console, 0, 0, CH, FG, BG) assert console.ch[0, 0] == CH @@ -64,21 +63,28 @@ def test_console_defaults() -> None: @pytest.mark.filterwarnings("ignore:Parameter names have been moved around,") -@pytest.mark.filterwarnings("ignore:Pass the key color to Console.blit instea") +@pytest.mark.filterwarnings("ignore:Pass the key color to Console.blit instead") @pytest.mark.filterwarnings("ignore:.*default values have been deprecated") def test_console_methods() -> None: console = tcod.console.Console(width=12, height=10) console.put_char(0, 0, ord("@")) - console.print_(0, 0, "Test") - console.print_rect(0, 0, 2, 8, "a b c d e f") + with pytest.deprecated_call(): + console.print_(0, 0, "Test") + with pytest.deprecated_call(): + console.print_rect(0, 0, 2, 8, "a b c d e f") console.get_height_rect(0, 0, 2, 8, "a b c d e f") - console.rect(0, 0, 2, 2, True) - console.hline(0, 1, 10) - console.vline(1, 0, 10) - console.print_frame(0, 0, 8, 8, "Frame") - console.blit(0, 0, 0, 0, console, 0, 0) # type: ignore - console.blit(0, 0, 0, 0, console, 0, 0, key_color=(0, 0, 0)) # type: ignore - console.set_key_color((254, 0, 254)) + with pytest.deprecated_call(): + console.rect(0, 0, 2, 2, True) # noqa: FBT003 + with pytest.deprecated_call(): + console.hline(0, 1, 10) + with pytest.deprecated_call(): + console.vline(1, 0, 10) + with pytest.deprecated_call(): + console.print_frame(0, 0, 8, 8, "Frame") + console.blit(0, 0, 0, 0, console, 0, 0) # type: ignore[arg-type] + console.blit(0, 0, 0, 0, console, 0, 0, key_color=(0, 0, 0)) # type: ignore[arg-type] + with pytest.deprecated_call(): + console.set_key_color((254, 0, 254)) def test_console_pickle() -> None: @@ -101,9 +107,9 @@ def test_console_pickle_fortran() -> None: def test_console_repr() -> None: - from numpy import array # noqa: F401 # Used for eval + from numpy import array # Used for eval # noqa: F401, PLC0415 - eval(repr(tcod.console.Console(10, 2))) + eval(repr(tcod.console.Console(10, 2))) # noqa: S307 def test_console_str() -> None: @@ -111,7 +117,7 @@ def test_console_str() -> None: console.ch[:] = ord(".") with pytest.warns(): console.print_(0, 0, "Test") - assert str(console) == ("") + assert str(console) == ("") def test_console_fortran_buffer() -> None: @@ -149,7 +155,7 @@ def test_rexpaint(tmp_path: Path) -> None: assert consoles[0].rgb.shape == loaded[0].rgb.shape assert consoles[1].rgb.shape == loaded[1].rgb.shape with pytest.raises(FileNotFoundError): - tcod.console.load_xp(tmp_path / "non_existant") + tcod.console.load_xp(tmp_path / "non_existent") def test_draw_frame() -> None: diff --git a/tests/test_deprecated.py b/tests/test_deprecated.py index 502d9ea9..4a960bba 100644 --- a/tests/test_deprecated.py +++ b/tests/test_deprecated.py @@ -1,4 +1,5 @@ """Test deprecated features.""" + from __future__ import annotations import numpy as np @@ -8,12 +9,11 @@ import tcod.constants import tcod.event import tcod.libtcodpy +import tcod.random with pytest.warns(): import libtcodpy -# ruff: noqa: D103 - def test_deprecate_color() -> None: with pytest.warns(FutureWarning, match=r"\(0, 0, 0\)"): @@ -49,7 +49,22 @@ def test_deprecate_mouse_constants() -> None: _ = tcod.event.BUTTON_LMASK +def test_deprecate_kmod_constants() -> None: + with pytest.warns(FutureWarning, match=r"Modifier.LSHIFT"): + _ = tcod.event.KMOD_LSHIFT + with pytest.warns(FutureWarning, match=r"Modifier.GUI"): + _ = tcod.event.KMOD_GUI + + def test_line_where() -> None: with pytest.warns(): where = tcod.libtcodpy.line_where(1, 0, 3, 4) np.testing.assert_array_equal(where, [[1, 1, 2, 2, 3], [0, 1, 2, 3, 4]]) + + +def test_gauss_typo() -> None: + rng = tcod.random.Random() + with pytest.warns(FutureWarning, match=r"gauss"): + rng.guass(1, 1) + with pytest.warns(FutureWarning, match=r"inverse_gauss"): + rng.inverse_guass(1, 1) diff --git a/tests/test_event.py b/tests/test_event.py new file mode 100644 index 00000000..9cc28243 --- /dev/null +++ b/tests/test_event.py @@ -0,0 +1,73 @@ +"""Tests for event parsing and handling.""" + +from typing import Any, Final + +import pytest + +import tcod.event +import tcod.sdl.sys +from tcod._internal import _check +from tcod.cffi import ffi, lib +from tcod.event import KeySym, Modifier, Scancode + +EXPECTED_EVENTS: Final = ( + tcod.event.Quit(), + tcod.event.KeyDown(scancode=Scancode.A, sym=KeySym.A, mod=Modifier(0), pressed=True), + tcod.event.KeyUp(scancode=Scancode.A, sym=KeySym.A, mod=Modifier(0), pressed=False), +) +"""Events to compare with after passing though the SDL event queue.""" + + +def as_sdl_event(event: tcod.event.Event) -> dict[str, dict[str, Any]]: + """Convert events into SDL_Event unions using cffi's union format.""" + match event: + case tcod.event.Quit(): + return {"quit": {"type": lib.SDL_EVENT_QUIT}} + case tcod.event.KeyboardEvent(): + return { + "key": { + "type": (lib.SDL_EVENT_KEY_UP, lib.SDL_EVENT_KEY_DOWN)[event.pressed], + "scancode": event.scancode, + "key": event.sym, + "mod": event.mod, + "down": event.pressed, + "repeat": event.repeat, + } + } + raise AssertionError + + +EVENT_PACK: Final = ffi.new("SDL_Event[]", [as_sdl_event(_e) for _e in EXPECTED_EVENTS]) +"""A custom C array of SDL_Event unions based on EXPECTED_EVENTS.""" + + +def push_events() -> None: + """Reset the SDL event queue to an expected list of events.""" + tcod.sdl.sys.init(tcod.sdl.sys.Subsystem.EVENTS) # Ensure SDL event queue is enabled + + lib.SDL_PumpEvents() # Clear everything from the queue + lib.SDL_FlushEvents(lib.SDL_EVENT_FIRST, lib.SDL_EVENT_LAST) + + assert _check( # Fill the queue with EVENT_PACK + lib.SDL_PeepEvents(EVENT_PACK, len(EVENT_PACK), lib.SDL_ADDEVENT, lib.SDL_EVENT_FIRST, lib.SDL_EVENT_LAST) + ) == len(EVENT_PACK) + + +def test_get_events() -> None: + push_events() + assert tuple(tcod.event.get()) == EXPECTED_EVENTS + + assert tuple(tcod.event.get()) == () + assert tuple(tcod.event.wait(timeout=0)) == () + + push_events() + assert tuple(tcod.event.wait()) == EXPECTED_EVENTS + + +def test_event_dispatch() -> None: + push_events() + with pytest.deprecated_call(): + tcod.event.EventDispatch().event_wait(timeout=0) + push_events() + with pytest.deprecated_call(): + tcod.event.EventDispatch().event_get() diff --git a/tests/test_libtcodpy.py b/tests/test_libtcodpy.py index 9eb151bb..ca1ab1a2 100644 --- a/tests/test_libtcodpy.py +++ b/tests/test_libtcodpy.py @@ -1,6 +1,8 @@ """Tests for the libtcodpy API.""" + +from collections.abc import Callable, Iterator from pathlib import Path -from typing import Any, Callable, Iterator, List, Optional, Tuple, Union +from typing import Any import numpy as np import pytest @@ -9,8 +11,6 @@ import tcod from tcod import libtcodpy -# ruff: noqa: D103 - pytestmark = [ pytest.mark.filterwarnings("ignore::DeprecationWarning"), pytest.mark.filterwarnings("ignore::PendingDeprecationWarning"), @@ -23,12 +23,12 @@ def test_console_behavior(console: tcod.console.Console) -> None: @pytest.mark.skip("takes too long") @pytest.mark.filterwarnings("ignore") -def test_credits_long(console: tcod.console.Console) -> None: +def test_credits_long(console: tcod.console.Console) -> None: # noqa: ARG001 libtcodpy.console_credits() -def test_credits(console: tcod.console.Console) -> None: - libtcodpy.console_credits_render(0, 0, True) +def test_credits(console: tcod.console.Console) -> None: # noqa: ARG001 + libtcodpy.console_credits_render(0, 0, True) # noqa: FBT003 libtcodpy.console_credits_reset() @@ -36,9 +36,9 @@ def assert_char( # noqa: PLR0913 console: tcod.console.Console, x: int, y: int, - ch: Optional[Union[str, int]] = None, - fg: Optional[Tuple[int, int, int]] = None, - bg: Optional[Tuple[int, int, int]] = None, + ch: str | int | None = None, + fg: tuple[int, int, int] | None = None, + bg: tuple[int, int, int] | None = None, ) -> None: if ch is not None: if isinstance(ch, str): @@ -51,7 +51,7 @@ def assert_char( # noqa: PLR0913 @pytest.mark.filterwarnings("ignore") -def test_console_defaults(console: tcod.console.Console, fg: Tuple[int, int, int], bg: Tuple[int, int, int]) -> None: +def test_console_defaults(console: tcod.console.Console, fg: tuple[int, int, int], bg: tuple[int, int, int]) -> None: libtcodpy.console_set_default_foreground(console, fg) libtcodpy.console_set_default_background(console, bg) libtcodpy.console_clear(console) @@ -59,13 +59,13 @@ def test_console_defaults(console: tcod.console.Console, fg: Tuple[int, int, int @pytest.mark.filterwarnings("ignore") -def test_console_set_char_background(console: tcod.console.Console, bg: Tuple[int, int, int]) -> None: +def test_console_set_char_background(console: tcod.console.Console, bg: tuple[int, int, int]) -> None: libtcodpy.console_set_char_background(console, 0, 0, bg, libtcodpy.BKGND_SET) assert_char(console, 0, 0, bg=bg) @pytest.mark.filterwarnings("ignore") -def test_console_set_char_foreground(console: tcod.console.Console, fg: Tuple[int, int, int]) -> None: +def test_console_set_char_foreground(console: tcod.console.Console, fg: tuple[int, int, int]) -> None: libtcodpy.console_set_char_foreground(console, 0, 0, fg) assert_char(console, 0, 0, fg=fg) @@ -84,14 +84,14 @@ def test_console_put_char(console: tcod.console.Console, ch: int) -> None: @pytest.mark.filterwarnings("ignore") def console_put_char_ex( - console: tcod.console.Console, ch: int, fg: Tuple[int, int, int], bg: Tuple[int, int, int] + console: tcod.console.Console, ch: int, fg: tuple[int, int, int], bg: tuple[int, int, int] ) -> None: libtcodpy.console_put_char_ex(console, 0, 0, ch, fg, bg) assert_char(console, 0, 0, ch=ch, fg=fg, bg=bg) @pytest.mark.filterwarnings("ignore") -def test_console_printing(console: tcod.console.Console, fg: Tuple[int, int, int], bg: Tuple[int, int, int]) -> None: +def test_console_printing(console: tcod.console.Console, fg: tuple[int, int, int], bg: tuple[int, int, int]) -> None: libtcodpy.console_set_background_flag(console, libtcodpy.BKGND_SET) assert libtcodpy.console_get_background_flag(console) == libtcodpy.BKGND_SET @@ -112,7 +112,7 @@ def test_console_printing(console: tcod.console.Console, fg: Tuple[int, int, int @pytest.mark.filterwarnings("ignore") def test_console_rect(console: tcod.console.Console) -> None: - libtcodpy.console_rect(console, 0, 0, 4, 4, False, libtcodpy.BKGND_SET) + libtcodpy.console_rect(console, 0, 0, 4, 4, False, libtcodpy.BKGND_SET) # noqa: FBT003 @pytest.mark.filterwarnings("ignore") @@ -127,13 +127,13 @@ def test_console_print_frame(console: tcod.console.Console) -> None: @pytest.mark.filterwarnings("ignore") -def test_console_fade(console: tcod.console.Console) -> None: +def test_console_fade(console: tcod.console.Console) -> None: # noqa: ARG001 libtcodpy.console_set_fade(0, (0, 0, 0)) libtcodpy.console_get_fade() libtcodpy.console_get_fading_color() -def assertConsolesEqual(a: tcod.console.Console, b: tcod.console.Console) -> bool: +def assert_consoles_equal(a: tcod.console.Console, b: tcod.console.Console, /) -> bool: return bool((a.fg[:] == b.fg[:]).all() and (a.bg[:] == b.bg[:]).all() and (a.ch[:] == b.ch[:]).all()) @@ -141,7 +141,7 @@ def assertConsolesEqual(a: tcod.console.Console, b: tcod.console.Console) -> boo def test_console_blit(console: tcod.console.Console, offscreen: tcod.console.Console) -> None: libtcodpy.console_print(offscreen, 0, 0, "test") libtcodpy.console_blit(offscreen, 0, 0, 0, 0, console, 0, 0, 1, 1) - assertConsolesEqual(console, offscreen) + assert_consoles_equal(console, offscreen) libtcodpy.console_set_key_color(offscreen, (0, 0, 0)) @@ -152,7 +152,7 @@ def test_console_asc_read_write(console: tcod.console.Console, offscreen: tcod.c asc_file = tmp_path / "test.asc" assert libtcodpy.console_save_asc(console, asc_file) assert libtcodpy.console_load_asc(offscreen, asc_file) - assertConsolesEqual(console, offscreen) + assert_consoles_equal(console, offscreen) @pytest.mark.filterwarnings("ignore") @@ -162,11 +162,11 @@ def test_console_apf_read_write(console: tcod.console.Console, offscreen: tcod.c apf_file = tmp_path / "test.apf" assert libtcodpy.console_save_apf(console, apf_file) assert libtcodpy.console_load_apf(offscreen, apf_file) - assertConsolesEqual(console, offscreen) + assert_consoles_equal(console, offscreen) @pytest.mark.filterwarnings("ignore") -def test_console_rexpaint_load_test_file(console: tcod.console.Console) -> None: +def test_console_rexpaint_load_test_file(console: tcod.console.Console) -> None: # noqa: ARG001 xp_console = libtcodpy.console_from_xp("libtcod/data/rexpaint/test.xp") assert xp_console assert libtcodpy.console_get_char(xp_console, 0, 0) == ord("T") @@ -181,8 +181,8 @@ def test_console_rexpaint_save_load( console: tcod.console.Console, tmp_path: Path, ch: int, - fg: Tuple[int, int, int], - bg: Tuple[int, int, int], + fg: tuple[int, int, int], + bg: tuple[int, int, int], ) -> None: libtcodpy.console_print(console, 0, 0, "test") libtcodpy.console_put_char_ex(console, 1, 1, ch, fg, bg) @@ -190,13 +190,13 @@ def test_console_rexpaint_save_load( assert libtcodpy.console_save_xp(console, xp_file, 1) xp_console = libtcodpy.console_from_xp(xp_file) assert xp_console - assertConsolesEqual(console, xp_console) - assert libtcodpy.console_load_xp(None, xp_file) # type: ignore - assertConsolesEqual(console, xp_console) + assert_consoles_equal(console, xp_console) + assert libtcodpy.console_load_xp(None, xp_file) # type: ignore[arg-type] + assert_consoles_equal(console, xp_console) @pytest.mark.filterwarnings("ignore") -def test_console_rexpaint_list_save_load(console: tcod.console.Console, tmp_path: Path) -> None: +def test_console_rexpaint_list_save_load(console: tcod.console.Console, tmp_path: Path) -> None: # noqa: ARG001 con1 = libtcodpy.console_new(8, 2) con2 = libtcodpy.console_new(8, 2) libtcodpy.console_print(con1, 0, 0, "hello") @@ -205,19 +205,19 @@ def test_console_rexpaint_list_save_load(console: tcod.console.Console, tmp_path assert libtcodpy.console_list_save_xp([con1, con2], xp_file, 1) loaded_consoles = libtcodpy.console_list_load_xp(xp_file) assert loaded_consoles - for a, b in zip([con1, con2], loaded_consoles): - assertConsolesEqual(a, b) + for a, b in zip([con1, con2], loaded_consoles, strict=True): + assert_consoles_equal(a, b) libtcodpy.console_delete(a) libtcodpy.console_delete(b) @pytest.mark.filterwarnings("ignore") -def test_console_fullscreen(console: tcod.console.Console) -> None: - libtcodpy.console_set_fullscreen(False) +def test_console_fullscreen(console: tcod.console.Console) -> None: # noqa: ARG001 + libtcodpy.console_set_fullscreen(False) # noqa: FBT003 @pytest.mark.filterwarnings("ignore") -def test_console_key_input(console: tcod.console.Console) -> None: +def test_console_key_input(console: tcod.console.Console) -> None: # noqa: ARG001 libtcodpy.console_check_for_keypress() libtcodpy.console_is_key_pressed(libtcodpy.KEY_ENTER) @@ -259,9 +259,9 @@ def test_console_fill_numpy(console: tcod.console.Console) -> None: for y in range(height): fill[y, :] = y % 256 - libtcodpy.console_fill_background(console, fill, fill, fill) # type: ignore - libtcodpy.console_fill_foreground(console, fill, fill, fill) # type: ignore - libtcodpy.console_fill_char(console, fill) # type: ignore + libtcodpy.console_fill_background(console, fill, fill, fill) # type: ignore[arg-type] + libtcodpy.console_fill_foreground(console, fill, fill, fill) # type: ignore[arg-type] + libtcodpy.console_fill_char(console, fill) # type: ignore[arg-type] # verify fill bg: NDArray[np.intc] = np.zeros((height, width), dtype=np.intc) @@ -272,10 +272,10 @@ def test_console_fill_numpy(console: tcod.console.Console) -> None: bg[y, x] = libtcodpy.console_get_char_background(console, x, y)[0] fg[y, x] = libtcodpy.console_get_char_foreground(console, x, y)[0] ch[y, x] = libtcodpy.console_get_char(console, x, y) - fill = fill.tolist() - assert fill == bg.tolist() - assert fill == fg.tolist() - assert fill == ch.tolist() + fill_ = fill.tolist() + assert fill_ == bg.tolist() + assert fill_ == fg.tolist() + assert fill_ == ch.tolist() @pytest.mark.filterwarnings("ignore") @@ -299,15 +299,15 @@ def test_console_buffer_error(console: tcod.console.Console) -> None: @pytest.mark.filterwarnings("ignore") -def test_console_font_mapping(console: tcod.console.Console) -> None: +def test_console_font_mapping(console: tcod.console.Console) -> None: # noqa: ARG001 libtcodpy.console_map_ascii_code_to_font(ord("@"), 1, 1) libtcodpy.console_map_ascii_codes_to_font(ord("@"), 1, 0, 0) libtcodpy.console_map_string_to_font("@", 0, 0) @pytest.mark.filterwarnings("ignore") -def test_mouse(console: tcod.console.Console) -> None: - libtcodpy.mouse_show_cursor(True) +def test_mouse(console: tcod.console.Console) -> None: # noqa: ARG001 + libtcodpy.mouse_show_cursor(True) # noqa: FBT003 libtcodpy.mouse_is_cursor_visible() mouse = libtcodpy.mouse_get_status() repr(mouse) @@ -315,7 +315,7 @@ def test_mouse(console: tcod.console.Console) -> None: @pytest.mark.filterwarnings("ignore") -def test_sys_time(console: tcod.console.Console) -> None: +def test_sys_time(console: tcod.console.Console) -> None: # noqa: ARG001 libtcodpy.sys_set_fps(0) libtcodpy.sys_get_fps() libtcodpy.sys_get_last_frame_length() @@ -325,18 +325,18 @@ def test_sys_time(console: tcod.console.Console) -> None: @pytest.mark.filterwarnings("ignore") -def test_sys_screenshot(console: tcod.console.Console, tmp_path: Path) -> None: +def test_sys_screenshot(console: tcod.console.Console, tmp_path: Path) -> None: # noqa: ARG001 libtcodpy.sys_save_screenshot(tmp_path / "test.png") @pytest.mark.filterwarnings("ignore") -def test_sys_custom_render(console: tcod.console.Console) -> None: +def test_sys_custom_render(console: tcod.console.Console) -> None: # noqa: ARG001 if libtcodpy.sys_get_renderer() != libtcodpy.RENDERER_SDL: pytest.xfail(reason="Only supports SDL") escape = [] - def sdl_callback(sdl_surface: object) -> None: + def sdl_callback(_sdl_surface: object) -> None: escape.append(True) libtcodpy.sys_register_SDL_renderer(sdl_callback) @@ -376,7 +376,7 @@ def test_image(console: tcod.console.Console, tmp_path: Path) -> None: @pytest.mark.parametrize("sample", ["@", "\u2603"]) # Unicode snowman @pytest.mark.xfail(reason="Unreliable") @pytest.mark.filterwarnings("ignore") -def test_clipboard(console: tcod.console.Console, sample: str) -> None: +def test_clipboard(console: tcod.console.Console, sample: str) -> None: # noqa: ARG001 saved = libtcodpy.sys_clipboard_get() try: libtcodpy.sys_clipboard_set(sample) @@ -404,7 +404,7 @@ def test_line_step() -> None: def test_line() -> None: """Tests normal use, lazy evaluation, and error propagation.""" # test normal results - test_result: List[Tuple[int, int]] = [] + test_result: list[tuple[int, int]] = [] def line_test(x: int, y: int) -> bool: test_result.append((x, y)) @@ -432,7 +432,6 @@ def test_line_iter() -> None: @pytest.mark.filterwarnings("ignore") def test_bsp() -> None: - """Commented out statements work in libtcod-cffi.""" bsp = libtcodpy.bsp_new_with_size(0, 0, 64, 64) repr(bsp) # test __repr__ on leaf libtcodpy.bsp_resize(bsp, 0, 0, 32, 32) @@ -448,33 +447,33 @@ def test_bsp() -> None: bsp.level = bsp.level # cover functions on leaf - # self.assertFalse(libtcodpy.bsp_left(bsp)) - # self.assertFalse(libtcodpy.bsp_right(bsp)) - # self.assertFalse(libtcodpy.bsp_father(bsp)) + assert not libtcodpy.bsp_left(bsp) + assert not libtcodpy.bsp_right(bsp) + assert not libtcodpy.bsp_father(bsp) assert libtcodpy.bsp_is_leaf(bsp) assert libtcodpy.bsp_contains(bsp, 1, 1) - # self.assertFalse(libtcodpy.bsp_contains(bsp, -1, -1)) - # self.assertEqual(libtcodpy.bsp_find_node(bsp, 1, 1), bsp) - # self.assertFalse(libtcodpy.bsp_find_node(bsp, -1, -1)) + assert not libtcodpy.bsp_contains(bsp, -1, -1) + assert libtcodpy.bsp_find_node(bsp, 1, 1) == bsp + assert not libtcodpy.bsp_find_node(bsp, -1, -1) - libtcodpy.bsp_split_once(bsp, False, 4) + libtcodpy.bsp_split_once(bsp, False, 4) # noqa: FBT003 repr(bsp) # test __repr__ with parent - libtcodpy.bsp_split_once(bsp, True, 4) + libtcodpy.bsp_split_once(bsp, True, 4) # noqa: FBT003 repr(bsp) # cover functions on parent assert libtcodpy.bsp_left(bsp) assert libtcodpy.bsp_right(bsp) - # self.assertFalse(libtcodpy.bsp_father(bsp)) + assert not libtcodpy.bsp_father(bsp) assert not libtcodpy.bsp_is_leaf(bsp) - # self.assertEqual(libtcodpy.bsp_father(libtcodpy.bsp_left(bsp)), bsp) - # self.assertEqual(libtcodpy.bsp_father(libtcodpy.bsp_right(bsp)), bsp) + assert libtcodpy.bsp_father(libtcodpy.bsp_left(bsp)) == bsp # type: ignore[arg-type] + assert libtcodpy.bsp_father(libtcodpy.bsp_right(bsp)) == bsp # type: ignore[arg-type] libtcodpy.bsp_split_recursive(bsp, None, 4, 2, 2, 1.0, 1.0) # cover bsp_traverse - def traverse(node: tcod.bsp.BSP, user_data: object) -> None: + def traverse(_node: tcod.bsp.BSP, _user_data: object) -> None: return None libtcodpy.bsp_traverse_pre_order(bsp, traverse) @@ -493,13 +492,13 @@ def traverse(node: tcod.bsp.BSP, user_data: object) -> None: @pytest.mark.filterwarnings("ignore") def test_map() -> None: - WIDTH, HEIGHT = 13, 17 - map = libtcodpy.map_new(WIDTH, HEIGHT) + WIDTH, HEIGHT = 13, 17 # noqa: N806 + map = libtcodpy.map_new(WIDTH, HEIGHT) # noqa: A001 assert libtcodpy.map_get_width(map) == WIDTH assert libtcodpy.map_get_height(map) == HEIGHT libtcodpy.map_copy(map, map) libtcodpy.map_clear(map) - libtcodpy.map_set_properties(map, 0, 0, True, True) + libtcodpy.map_set_properties(map, 0, 0, True, True) # noqa: FBT003 assert libtcodpy.map_is_transparent(map, 0, 0) assert libtcodpy.map_is_walkable(map, 0, 0) libtcodpy.map_is_in_fov(map, 0, 0) @@ -518,21 +517,21 @@ def test_color() -> None: color_a["b"] = color_a["b"] assert list(color_a) == [0, 3, 2] - assert color_a == color_a + assert color_a == color_a # noqa: PLR0124 color_b = libtcodpy.Color(255, 255, 255) assert color_a != color_b - color = libtcodpy.color_lerp(color_a, color_b, 0.5) # type: ignore + color = libtcodpy.color_lerp(color_a, color_b, 0.5) # type: ignore[arg-type] libtcodpy.color_set_hsv(color, 0, 0, 0) - libtcodpy.color_get_hsv(color) # type: ignore + libtcodpy.color_get_hsv(color) # type: ignore[arg-type] libtcodpy.color_scale_HSV(color, 0, 0) def test_color_repr() -> None: - Color = libtcodpy.Color + Color = libtcodpy.Color # noqa: N806 col = Color(0, 1, 2) - assert eval(repr(col)) == col + assert eval(repr(col)) == col # noqa: S307 @pytest.mark.filterwarnings("ignore") @@ -659,7 +658,7 @@ def test_heightmap() -> None: POINTS_AC = POINT_A + POINT_C # invalid path -@pytest.fixture() +@pytest.fixture def map_() -> Iterator[tcod.map.Map]: map_ = tcod.map.Map(MAP_WIDTH, MAP_HEIGHT) map_.walkable[...] = map_.transparent[...] = MAP[...] == " " @@ -667,12 +666,10 @@ def map_() -> Iterator[tcod.map.Map]: libtcodpy.map_delete(map_) -@pytest.fixture() +@pytest.fixture def path_callback(map_: tcod.map.Map) -> Callable[[int, int, int, int, None], bool]: - def callback(ox: int, oy: int, dx: int, dy: int, user_data: None) -> bool: - if map_.walkable[dy, dx]: - return True - return False + def callback(_ox: int, _oy: int, dx: int, dy: int, _user_data: None) -> bool: + return bool(map_.walkable[dy, dx]) return callback @@ -699,14 +696,14 @@ def test_astar(map_: tcod.map.Map) -> None: assert libtcodpy.path_size(astar) > 0 assert not libtcodpy.path_is_empty(astar) - x: Optional[int] - y: Optional[int] + x: int | None + y: int | None for i in range(libtcodpy.path_size(astar)): x, y = libtcodpy.path_get(astar, i) while (x, y) != (None, None): - x, y = libtcodpy.path_walk(astar, False) + x, y = libtcodpy.path_walk(astar, False) # noqa: FBT003 libtcodpy.path_delete(astar) @@ -737,8 +734,8 @@ def test_dijkstra(map_: tcod.map.Map) -> None: libtcodpy.dijkstra_reverse(path) - x: Optional[int] - y: Optional[int] + x: int | None + y: int | None for i in range(libtcodpy.dijkstra_size(path)): x, y = libtcodpy.dijkstra_get(path, i) diff --git a/tests/test_noise.py b/tests/test_noise.py index 0f64d5fe..28825328 100644 --- a/tests/test_noise.py +++ b/tests/test_noise.py @@ -1,4 +1,5 @@ """Tests for the tcod.noise module.""" + import copy import pickle @@ -6,8 +7,7 @@ import pytest import tcod.noise - -# ruff: noqa: D103 +import tcod.random @pytest.mark.parametrize("implementation", tcod.noise.Implementation) @@ -99,3 +99,7 @@ def test_noise_copy() -> None: assert repr(noise3) == repr(pickle.loads(pickle.dumps(noise3))) noise4 = tcod.noise.Noise(2, seed=42) assert repr(noise4) == repr(pickle.loads(pickle.dumps(noise4))) + + +def test_noise_grid() -> None: + tcod.noise.grid((2, 2), scale=2) # Check int scale diff --git a/tests/test_parser.py b/tests/test_parser.py index ce200166..67cd10e3 100644 --- a/tests/test_parser.py +++ b/tests/test_parser.py @@ -1,4 +1,5 @@ """Test old libtcodpy parser.""" + from pathlib import Path from typing import Any @@ -6,27 +7,25 @@ import tcod as libtcod -# ruff: noqa: D103 - @pytest.mark.filterwarnings("ignore") def test_parser() -> None: print("***** File Parser test *****") parser = libtcod.parser_new() struct = libtcod.parser_new_struct(parser, "myStruct") - libtcod.struct_add_property(struct, "bool_field", libtcod.TYPE_BOOL, True) - libtcod.struct_add_property(struct, "char_field", libtcod.TYPE_CHAR, True) - libtcod.struct_add_property(struct, "int_field", libtcod.TYPE_INT, True) - libtcod.struct_add_property(struct, "float_field", libtcod.TYPE_FLOAT, True) - libtcod.struct_add_property(struct, "color_field", libtcod.TYPE_COLOR, True) - libtcod.struct_add_property(struct, "dice_field", libtcod.TYPE_DICE, True) - libtcod.struct_add_property(struct, "string_field", libtcod.TYPE_STRING, True) - libtcod.struct_add_list_property(struct, "bool_list", libtcod.TYPE_BOOL, True) - libtcod.struct_add_list_property(struct, "char_list", libtcod.TYPE_CHAR, True) - libtcod.struct_add_list_property(struct, "integer_list", libtcod.TYPE_INT, True) - libtcod.struct_add_list_property(struct, "float_list", libtcod.TYPE_FLOAT, True) - libtcod.struct_add_list_property(struct, "string_list", libtcod.TYPE_STRING, True) - libtcod.struct_add_list_property(struct, "color_list", libtcod.TYPE_COLOR, True) + libtcod.struct_add_property(struct, "bool_field", libtcod.TYPE_BOOL, True) # noqa: FBT003 + libtcod.struct_add_property(struct, "char_field", libtcod.TYPE_CHAR, True) # noqa: FBT003 + libtcod.struct_add_property(struct, "int_field", libtcod.TYPE_INT, True) # noqa: FBT003 + libtcod.struct_add_property(struct, "float_field", libtcod.TYPE_FLOAT, True) # noqa: FBT003 + libtcod.struct_add_property(struct, "color_field", libtcod.TYPE_COLOR, True) # noqa: FBT003 + libtcod.struct_add_property(struct, "dice_field", libtcod.TYPE_DICE, True) # noqa: FBT003 + libtcod.struct_add_property(struct, "string_field", libtcod.TYPE_STRING, True) # noqa: FBT003 + libtcod.struct_add_list_property(struct, "bool_list", libtcod.TYPE_BOOL, True) # noqa: FBT003 + libtcod.struct_add_list_property(struct, "char_list", libtcod.TYPE_CHAR, True) # noqa: FBT003 + libtcod.struct_add_list_property(struct, "integer_list", libtcod.TYPE_INT, True) # noqa: FBT003 + libtcod.struct_add_list_property(struct, "float_list", libtcod.TYPE_FLOAT, True) # noqa: FBT003 + libtcod.struct_add_list_property(struct, "string_list", libtcod.TYPE_STRING, True) # noqa: FBT003 + libtcod.struct_add_list_property(struct, "color_list", libtcod.TYPE_COLOR, True) # noqa: FBT003 # default listener print("***** Default listener *****") @@ -61,7 +60,7 @@ def new_property(self, name: str, typ: int, value: Any) -> bool: # noqa: ANN401 type_names = ["NONE", "BOOL", "CHAR", "INT", "FLOAT", "STRING", "COLOR", "DICE"] type_name = type_names[typ & 0xFF] if typ & libtcod.TYPE_LIST: - type_name = "LIST<%s>" % type_name + type_name = f"LIST<{type_name}>" print("new property named ", name, " type ", type_name, " value ", value) return True diff --git a/tests/test_random.py b/tests/test_random.py index e6b64e1e..764ae988 100644 --- a/tests/test_random.py +++ b/tests/test_random.py @@ -1,11 +1,12 @@ """Test random number generators.""" + import copy import pickle from pathlib import Path -import tcod.random +import pytest -# ruff: noqa: D103 +import tcod.random SCRIPT_DIR = Path(__file__).parent @@ -14,8 +15,10 @@ def test_tcod_random() -> None: rand = tcod.random.Random(tcod.random.COMPLEMENTARY_MULTIPLY_WITH_CARRY) assert 0 <= rand.randint(0, 100) <= 100 # noqa: PLR2004 assert 0 <= rand.uniform(0, 100) <= 100 # noqa: PLR2004 - rand.guass(0, 1) - rand.inverse_guass(0, 1) + with pytest.warns(FutureWarning, match=r"typo"): + rand.guass(0, 1) + with pytest.warns(FutureWarning, match=r"typo"): + rand.inverse_guass(0, 1) def test_tcod_random_copy() -> None: diff --git a/tests/test_sdl.py b/tests/test_sdl.py index fa2ac29a..42433510 100644 --- a/tests/test_sdl.py +++ b/tests/test_sdl.py @@ -1,4 +1,5 @@ """Test SDL specific features.""" + import sys import numpy as np @@ -8,10 +9,8 @@ import tcod.sdl.sys import tcod.sdl.video -# ruff: noqa: D103 - -def test_sdl_window() -> None: +def test_sdl_window(uses_window: None) -> None: # noqa: ARG001 assert tcod.sdl.video.get_grabbed_window() is None window = tcod.sdl.video.new_window(1, 1) window.raise_window() @@ -23,7 +22,7 @@ def test_sdl_window() -> None: assert window.title == sys.argv[0] window.title = "Title" assert window.title == "Title" - assert window.opacity == 1.0 # noqa: PLR2004 + assert window.opacity == 1.0 window.position = window.position window.fullscreen = window.fullscreen window.resizable = window.resizable @@ -39,6 +38,10 @@ def test_sdl_window() -> None: window.opacity = window.opacity window.grab = window.grab + window.start_text_input(capitalization=tcod.sdl.video.Capitalization.NONE, multiline=False) + window.set_text_input_area((0, 0, 8, 8), 0) + window.stop_text_input() + def test_sdl_window_bad_types() -> None: with pytest.raises(TypeError): @@ -47,17 +50,19 @@ def test_sdl_window_bad_types() -> None: tcod.sdl.video.Window(tcod.ffi.new("SDL_Rect*")) -def test_sdl_screen_saver() -> None: - tcod.sdl.sys.init() - assert tcod.sdl.video.screen_saver_allowed(False) is False - assert tcod.sdl.video.screen_saver_allowed(True) is True +def test_sdl_screen_saver(uses_window: None) -> None: # noqa: ARG001 + tcod.sdl.sys.init(tcod.sdl.sys.Subsystem.VIDEO) + assert tcod.sdl.video.screen_saver_allowed(False) is False # noqa: FBT003 + assert tcod.sdl.video.screen_saver_allowed(True) is True # noqa: FBT003 assert tcod.sdl.video.screen_saver_allowed() is True -def test_sdl_render() -> None: - window = tcod.sdl.video.new_window(1, 1) - render = tcod.sdl.render.new_renderer(window, software=True, vsync=False, target_textures=True) +def test_sdl_render(uses_window: None) -> None: # noqa: ARG001 + window = tcod.sdl.video.new_window(4, 4) + render = tcod.sdl.render.new_renderer(window, driver="software", vsync=False) + render.clear() render.present() + render.clear() rgb = render.upload_texture(np.zeros((8, 8, 3), np.uint8)) assert (rgb.width, rgb.height) == (8, 8) assert rgb.access == tcod.sdl.render.TextureAccess.STATIC @@ -65,15 +70,45 @@ def test_sdl_render() -> None: rgb.alpha_mod = rgb.alpha_mod rgb.blend_mode = rgb.blend_mode rgb.color_mod = rgb.color_mod + rgb.scale_mode = rgb.scale_mode rgba = render.upload_texture(np.zeros((8, 8, 4), np.uint8), access=tcod.sdl.render.TextureAccess.TARGET) with render.set_render_target(rgba): render.copy(rgb) with pytest.raises(TypeError): render.upload_texture(np.zeros((8, 8, 5), np.uint8)) - assert (render.read_pixels() == (0, 0, 0, 255)).all() + assert render.read_pixels(rect=(0, 0, 3, 4)).shape == (4, 3, 4) assert (render.read_pixels(format="RGB") == (0, 0, 0)).all() - assert render.read_pixels(rect=(1, 2, 3, 4)).shape == (4, 3, 4) + assert (render.read_pixels() == (0, 0, 0, 255)).all() + + render.draw_point((0, 0)) + render.draw_line((0, 0), (1, 1)) + render.draw_rect((0, 0, 1, 1)) + render.fill_rect((0, 0, 1, 1)) + + render.draw_points([(0, 0)]) + render.draw_lines([(0, 0), (1, 1)]) + render.draw_rects([(0, 0, 1, 1)]) + render.fill_rects([(0, 0, 1, 1)]) + + for dtype in (np.intc, np.int8, np.uint8, np.float32, np.float16): + render.draw_points(np.ones((3, 2), dtype=dtype)) + render.draw_lines(np.ones((3, 2), dtype=dtype)) + render.draw_rects(np.ones((3, 4), dtype=dtype)) + render.fill_rects(np.ones((3, 4), dtype=dtype)) + + with pytest.raises(TypeError, match=r"shape\[1\] must be 2"): + render.draw_points(np.ones((3, 1), dtype=dtype)) + with pytest.raises(TypeError, match=r"must have 2 axes"): + render.draw_points(np.ones((3,), dtype=dtype)) + + render.geometry( + None, + np.zeros((1, 2), np.float32), + np.zeros((1, 4), np.float32), + np.zeros((1, 2), np.float32), + np.zeros((3,), np.uint8), + ) def test_sdl_render_bad_types() -> None: diff --git a/tests/test_sdl_audio.py b/tests/test_sdl_audio.py index 6441bda1..94bd151e 100644 --- a/tests/test_sdl_audio.py +++ b/tests/test_sdl_audio.py @@ -1,7 +1,9 @@ """Test tcod.sdl.audio module.""" + import contextlib import sys import time +from collections.abc import Callable from typing import Any import numpy as np @@ -10,14 +12,20 @@ import tcod.sdl.audio -# ruff: noqa: D103 + +def device_works(device: Callable[[], tcod.sdl.audio.AudioDevice]) -> bool: + try: + device().open().close() + except RuntimeError: + return False + return True needs_audio_device = pytest.mark.xfail( - not list(tcod.sdl.audio.get_devices()), reason="This test requires an audio device" + not device_works(tcod.sdl.audio.get_default_playback), reason="This test requires an audio device" ) needs_audio_capture = pytest.mark.xfail( - not list(tcod.sdl.audio.get_capture_devices()), reason="This test requires an audio capture device" + not device_works(tcod.sdl.audio.get_default_recording), reason="This test requires an audio capture device" ) @@ -30,20 +38,14 @@ def test_devices() -> None: def test_audio_device() -> None: with tcod.sdl.audio.open(frequency=44100, format=np.float32, channels=2, paused=True) as device: assert not device.stopped - assert device.convert(np.zeros(4, dtype=np.float32), 22050).shape[0] == 8 # noqa: PLR2004 - assert device.convert(np.zeros((4, 4), dtype=np.float32)).shape == (4, 2) - assert device.convert(np.zeros(4, dtype=np.int8)).shape[0] == 4 # noqa: PLR2004 + device.convert(np.zeros(4, dtype=np.float32), 22050) + assert device.convert(np.zeros((4, 4), dtype=np.float32)).shape[1] == device.channels + device.convert(np.zeros(4, dtype=np.int8)).shape[0] assert device.paused is True device.paused = False assert device.paused is False device.paused = True - assert device.queued_samples == 0 - with pytest.raises(TypeError): - device.callback # noqa: B018 - with pytest.raises(TypeError): - device.callback = lambda _device, _stream: None - with contextlib.closing(tcod.sdl.audio.BasicMixer(device)) as mixer: - assert mixer.daemon + with contextlib.closing(tcod.sdl.audio.BasicMixer(device, frequency=44100, channels=2)) as mixer: assert mixer.play(np.zeros(4, np.float32)).busy mixer.play(np.zeros(0, np.float32)) mixer.play(np.full(1, 0.01, np.float32), on_end=lambda _: None) @@ -58,18 +60,15 @@ def test_audio_device() -> None: @needs_audio_capture def test_audio_capture() -> None: - with tcod.sdl.audio.open(capture=True) as device: - assert not device.stopped - assert isinstance(device.dequeue_audio(), np.ndarray) + with contextlib.closing(tcod.sdl.audio.get_default_recording().open()) as device: + device.new_stream(np.float32, 1, 11025).dequeue_audio() @needs_audio_device def test_audio_device_repr() -> None: - with tcod.sdl.audio.open(format=np.uint16, paused=True, callback=True) as device: + with contextlib.closing(tcod.sdl.audio.get_default_playback().open()) as device: assert not device.stopped - assert "silence=" in repr(device) - assert "callback=" in repr(device) - assert "stopped=" in repr(device) + assert "paused=False" in repr(device) def test_convert_bad_shape() -> None: @@ -82,7 +81,7 @@ def test_convert_bad_shape() -> None: def test_convert_bad_type() -> None: with pytest.raises(TypeError, match=r".*bool"): tcod.sdl.audio.convert_audio(np.zeros(8, bool), 8000, out_rate=8000, out_format=np.float32, out_channels=1) - with pytest.raises(RuntimeError, match=r"Invalid source format"): + with pytest.raises(RuntimeError, match=r"Parameter 'src_spec->format' is invalid"): tcod.sdl.audio.convert_audio(np.zeros(8, np.int64), 8000, out_rate=8000, out_format=np.float32, out_channels=1) @@ -109,13 +108,13 @@ def __call__(self, device: tcod.sdl.audio.AudioDevice, stream: NDArray[Any]) -> check_called = CheckCalled() with tcod.sdl.audio.open(callback=check_called, paused=False) as device: assert not device.stopped - device.callback = device.callback while not check_called.was_called: time.sleep(0.001) @pytest.mark.skipif(sys.version_info < (3, 8), reason="Needs sys.unraisablehook support") @pytest.mark.filterwarnings("ignore::pytest.PytestUnraisableExceptionWarning") +@pytest.mark.skip(reason="Unsupported, causes too many issues") @needs_audio_device def test_audio_callback_unraisable() -> None: """Test unraisable error in audio callback. @@ -126,9 +125,9 @@ def test_audio_callback_unraisable() -> None: class CheckCalled: was_called: bool = False - def __call__(self, device: tcod.sdl.audio.AudioDevice, stream: NDArray[Any]) -> None: + def __call__(self, _device: tcod.sdl.audio.AudioDevice, _stream: NDArray[Any]) -> None: self.was_called = True - raise Exception("Test unraisable error") # noqa + raise Exception("Test unraisable error") # noqa: EM101, TRY002, TRY003 check_called = CheckCalled() with tcod.sdl.audio.open(callback=check_called, paused=False) as device: diff --git a/tests/test_tcod.py b/tests/test_tcod.py index 33e50195..3f8c1d9b 100644 --- a/tests/test_tcod.py +++ b/tests/test_tcod.py @@ -1,4 +1,5 @@ """Tests for newer tcod API.""" + import copy import pickle from typing import Any, NoReturn @@ -8,19 +9,23 @@ from numpy.typing import DTypeLike, NDArray import tcod +import tcod.bsp import tcod.console - -# ruff: noqa: D103 +import tcod.context +import tcod.map +import tcod.path +import tcod.random +from tcod import libtcodpy -def raise_Exception(*args: object) -> NoReturn: +def raise_exception(*_args: object) -> NoReturn: raise RuntimeError("testing exception") # noqa: TRY003, EM101 def test_line_error() -> None: """Test exception propagation.""" with pytest.raises(RuntimeError), pytest.warns(): - tcod.line(0, 0, 10, 10, py_callback=raise_Exception) + libtcodpy.line(0, 0, 10, 10, py_callback=raise_exception) @pytest.mark.filterwarnings("ignore:Iterate over nodes using") @@ -34,7 +39,7 @@ def test_tcod_bsp() -> None: assert not bsp.children with pytest.raises(RuntimeError): - tcod.bsp_traverse_pre_order(bsp, raise_Exception) + libtcodpy.bsp_traverse_pre_order(bsp, raise_exception) bsp.split_recursive(3, 4, 4, 1, 1) for node in bsp.walk(): @@ -44,7 +49,7 @@ def test_tcod_bsp() -> None: # test that operations on deep BSP nodes preserve depth sub_bsp = bsp.children[0] - sub_bsp.split_recursive(3, 2, 2, 1, 1) + sub_bsp.split_recursive(3, 2, 2, 1, 1, seed=tcod.random.Random(seed=42)) assert sub_bsp.children[0].level == 2 # noqa: PLR2004 # cover find_node method @@ -65,11 +70,11 @@ def test_tcod_map_set_bits() -> None: assert not map_.fov[:].any() map_.transparent[1, 0] = True - assert tcod.map_is_transparent(map_, 0, 1) + assert libtcodpy.map_is_transparent(map_, 0, 1) map_.walkable[1, 0] = True - assert tcod.map_is_walkable(map_, 0, 1) + assert libtcodpy.map_is_walkable(map_, 0, 1) map_.fov[1, 0] = True - assert tcod.map_is_in_fov(map_, 0, 1) + assert libtcodpy.map_is_in_fov(map_, 0, 1) @pytest.mark.filterwarnings("ignore:This class may perform poorly") @@ -97,7 +102,7 @@ def test_tcod_map_pickle() -> None: def test_tcod_map_pickle_fortran() -> None: map_ = tcod.map.Map(2, 3, order="F") map2: tcod.map.Map = pickle.loads(pickle.dumps(copy.copy(map_))) - assert map_._Map__buffer.strides == map2._Map__buffer.strides # type: ignore + assert map_._buffer.strides == map2._buffer.strides assert map_.transparent.strides == map2.transparent.strides assert map_.walkable.strides == map2.walkable.strides assert map_.fov.strides == map2.fov.strides @@ -114,7 +119,7 @@ def test_color_class() -> None: assert tcod.white - tcod.white == tcod.black assert tcod.black + (2, 2, 2) - (1, 1, 1) == (1, 1, 1) # noqa: RUF005 - color = tcod.Color() + color = libtcodpy.Color() color.r = 1 color.g = 2 color.b = 3 @@ -143,7 +148,7 @@ def test_path_numpy(dtype: DTypeLike) -> None: tcod.path.AStar(np.ones((2, 2), dtype=np.float64)) -def path_cost(this_x: int, this_y: int, dest_x: int, dest_y: int) -> bool: +def path_cost(_this_x: int, _this_y: int, _dest_x: int, _dest_y: int) -> bool: return True @@ -155,21 +160,21 @@ def test_path_callback() -> None: def test_key_repr() -> None: - Key = tcod.Key + Key = libtcodpy.Key # noqa: N806 key = Key(vk=1, c=2, shift=True) assert key.vk == 1 assert key.c == 2 # noqa: PLR2004 assert key.shift - key_copy = eval(repr(key)) + key_copy = eval(repr(key)) # noqa: S307 assert key.vk == key_copy.vk assert key.c == key_copy.c assert key.shift == key_copy.shift def test_mouse_repr() -> None: - Mouse = tcod.Mouse + Mouse = libtcodpy.Mouse # noqa: N806 mouse = Mouse(x=1, lbutton=True) - mouse_copy = eval(repr(mouse)) + mouse_copy = eval(repr(mouse)) # noqa: S307 assert mouse.x == mouse_copy.x assert mouse.lbutton == mouse_copy.lbutton @@ -177,20 +182,19 @@ def test_mouse_repr() -> None: def test_cffi_structs() -> None: # Make sure cffi structures are the correct size. tcod.ffi.new("SDL_Event*") - tcod.ffi.new("SDL_AudioCVT*") @pytest.mark.filterwarnings("ignore") -def test_recommended_size(console: tcod.console.Console) -> None: +def test_recommended_size(console: tcod.console.Console) -> None: # noqa: ARG001 tcod.console.recommended_size() @pytest.mark.filterwarnings("ignore") -def test_context() -> None: - with tcod.context.new_window(32, 32, renderer=tcod.RENDERER_SDL2): +def test_context(uses_window: None) -> None: # noqa: ARG001 + with tcod.context.new_window(32, 32, renderer=libtcodpy.RENDERER_SDL2): pass - WIDTH, HEIGHT = 16, 4 - with tcod.context.new_terminal(columns=WIDTH, rows=HEIGHT, renderer=tcod.RENDERER_SDL2) as context: + width, height = 16, 4 + with tcod.context.new_terminal(columns=width, rows=height, renderer=libtcodpy.RENDERER_SDL2) as context: console = tcod.console.Console(*context.recommended_console_size()) context.present(console) assert context.sdl_window_p is not None @@ -198,5 +202,18 @@ def test_context() -> None: context.change_tileset(tcod.tileset.Tileset(16, 16)) context.pixel_to_tile(0, 0) context.pixel_to_subtile(0, 0) - with pytest.raises(RuntimeError, match=".*context has been closed"): + with pytest.raises(RuntimeError, match=r".*context has been closed"): context.present(console) + + +def test_event_watch() -> None: + def handle_events(_event: tcod.event.Event) -> None: + pass + + tcod.event.add_watch(handle_events) + with pytest.warns(RuntimeWarning, match=r"nothing was added"): + tcod.event.add_watch(handle_events) + + tcod.event.remove_watch(handle_events) + with pytest.warns(RuntimeWarning, match=r"nothing was removed"): + tcod.event.remove_watch(handle_events) diff --git a/tests/test_tileset.py b/tests/test_tileset.py index aba88b98..78b458dd 100644 --- a/tests/test_tileset.py +++ b/tests/test_tileset.py @@ -1,11 +1,103 @@ """Test for tcod.tileset module.""" + +from pathlib import Path + +import pytest + +import tcod.console import tcod.tileset -# ruff: noqa: D103 +PROJECT_DIR = Path(__file__).parent / ".." + +TERMINAL_FONT = PROJECT_DIR / "fonts/libtcod/terminal8x8_aa_ro.png" +BDF_FONT = PROJECT_DIR / "libtcod/data/fonts/Tamzen5x9r.bdf" + +BAD_FILE = PROJECT_DIR / "CHANGELOG.md" # Any existing non-font file def test_proc_block_elements() -> None: - tileset = tcod.tileset.Tileset(8, 8) - tcod.tileset.procedural_block_elements(tileset=tileset) tileset = tcod.tileset.Tileset(0, 0) - tcod.tileset.procedural_block_elements(tileset=tileset) + with pytest.deprecated_call(): + tcod.tileset.procedural_block_elements(tileset=tileset) + tileset += tcod.tileset.procedural_block_elements(shape=tileset.tile_shape) + + tileset = tcod.tileset.Tileset(8, 8) + with pytest.deprecated_call(): + tcod.tileset.procedural_block_elements(tileset=tileset) + tileset += tcod.tileset.procedural_block_elements(shape=tileset.tile_shape) + + +def test_tileset_mix() -> None: + tileset1 = tcod.tileset.Tileset(1, 1) + tileset1[ord("a")] = [[0]] + + tileset2 = tcod.tileset.Tileset(1, 1) + tileset2[ord("a")] = [[1]] + tileset2[ord("b")] = [[1]] + + assert (tileset1 + tileset2)[ord("a")].tolist() == [[[255, 255, 255, 1]]] # Replaces tile + assert (tileset1 | tileset2)[ord("a")].tolist() == [[[255, 255, 255, 0]]] # Skips existing tile + + +def test_tileset_contains() -> None: + tileset = tcod.tileset.Tileset(1, 1) + + # Missing keys + assert None not in tileset + assert ord("x") not in tileset + assert -1 not in tileset + with pytest.raises(KeyError, match=rf"{ord('x')}"): + tileset[ord("x")] + with pytest.raises(KeyError, match=rf"{ord('x')}"): + del tileset[ord("x")] + assert len(tileset) == 0 + + # Assigned tile is found + tileset[ord("x")] = [[255]] + assert ord("x") in tileset + assert len(tileset) == 1 + + # Can be deleted and reassigned + del tileset[ord("x")] + assert ord("x") not in tileset + assert len(tileset) == 0 + tileset[ord("x")] = [[255]] + assert ord("x") in tileset + assert len(tileset) == 1 + + +def test_tileset_assignment() -> None: + tileset = tcod.tileset.Tileset(1, 2) + tileset[ord("a")] = [[1], [1]] + tileset[ord("b")] = [[[255, 255, 255, 2]], [[255, 255, 255, 2]]] + + with pytest.raises(ValueError, match=r".*must be \(2, 1, 4\) or \(2, 1\), got \(2, 1, 3\)"): + tileset[ord("c")] = [[[255, 255, 255]], [[255, 255, 255]]] + + assert tileset.get_tile(ord("d")).shape == (2, 1, 4) + + +def test_tileset_render() -> None: + tileset = tcod.tileset.Tileset(1, 2) + tileset[ord("x")] = [[255], [0]] + console = tcod.console.Console(3, 2) + console.rgb[0, 0] = (ord("x"), (255, 0, 0), (0, 255, 0)) + output = tileset.render(console) + assert output.shape == (4, 3, 4) + assert output[0:2, 0].tolist() == [[255, 0, 0, 255], [0, 255, 0, 255]] + + +def test_tileset_tilesheet() -> None: + tileset = tcod.tileset.load_tilesheet(TERMINAL_FONT, 16, 16, tcod.tileset.CHARMAP_CP437) + assert tileset.tile_shape == (8, 8) + + with pytest.raises(RuntimeError): + tcod.tileset.load_tilesheet(BAD_FILE, 16, 16, tcod.tileset.CHARMAP_CP437) + + +def test_tileset_bdf() -> None: + tileset = tcod.tileset.load_bdf(BDF_FONT) + assert tileset.tile_shape == (9, 5) + + with pytest.raises(RuntimeError): + tileset = tcod.tileset.load_bdf(BAD_FILE) diff --git a/tox.ini b/tox.ini new file mode 100644 index 00000000..784e5f57 --- /dev/null +++ b/tox.ini @@ -0,0 +1,17 @@ +[tox] +isolated_build = True +env_list = + py3 +minversion = 4.4.11 + +[testenv] +description = run the tests with pytest +package = wheel +wheel_build_env = .pkg +deps = + pytest>=6 + pytest-cov + pytest-benchmark + pytest-timeout +commands = + pytest --no-window {tty:--color=yes} {posargs}