Merge remote-tracking branch 'upstream/master'

This commit is contained in:
John Cote 2026-01-27 20:58:16 -05:00
commit 935c9cd80a
No known key found for this signature in database
GPG Key ID: A5250CCD017DFCA5
377 changed files with 21592 additions and 12032 deletions

View File

@ -20,7 +20,7 @@ jobs:
include: include:
- profile: ubuntu-bionic-double-standard-unity-makefile - profile: ubuntu-bionic-double-standard-unity-makefile
os: ubuntu-20.04 os: ubuntu-22.04
config: Standard config: Standard
unity: YES unity: YES
generator: Unix Makefiles generator: Unix Makefiles
@ -29,9 +29,10 @@ jobs:
python: YES python: YES
eigen: NO eigen: NO
double: YES double: YES
asan: YES
- profile: ubuntu-bionic-coverage-ninja - profile: ubuntu-bionic-coverage-ninja
os: ubuntu-20.04 os: ubuntu-22.04
config: Coverage config: Coverage
unity: NO unity: NO
generator: Ninja generator: Ninja
@ -40,9 +41,10 @@ jobs:
python: YES python: YES
eigen: NO eigen: NO
double: NO double: NO
asan: YES
#- profile: macos-coverage-unity-xcode #- profile: macos-coverage-unity-xcode
# os: macOS-13 # os: macOS-15
# config: Coverage # config: Coverage
# unity: YES # unity: YES
# generator: Xcode # generator: Xcode
@ -53,7 +55,7 @@ jobs:
# double: NO # double: NO
- profile: macos-nometa-standard-makefile - profile: macos-nometa-standard-makefile
os: macOS-13 os: macOS-15
config: Standard config: Standard
unity: NO unity: NO
generator: Unix Makefiles generator: Unix Makefiles
@ -62,6 +64,7 @@ jobs:
python: YES python: YES
eigen: NO eigen: NO
double: NO double: NO
asan: YES
- profile: windows-standard-unity-msvc - profile: windows-standard-unity-msvc
os: windows-2022 os: windows-2022
@ -88,7 +91,7 @@ jobs:
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v6
with: with:
fetch-depth: 10 fetch-depth: 10
@ -110,7 +113,7 @@ jobs:
- name: Set up XCode (macOS) - name: Set up XCode (macOS)
if: runner.os == 'macOS' if: runner.os == 'macOS'
run: sudo xcode-select -s /Applications/Xcode_14.3.1.app/Contents/Developer run: sudo xcode-select -s /Applications/Xcode_16.app/Contents/Developer
- name: Install dependencies (Ubuntu) - name: Install dependencies (Ubuntu)
if: startsWith(matrix.os, 'ubuntu') if: startsWith(matrix.os, 'ubuntu')
@ -127,7 +130,7 @@ jobs:
- name: Cache dependencies (Windows) - name: Cache dependencies (Windows)
if: runner.os == 'Windows' if: runner.os == 'Windows'
uses: actions/cache@v4 uses: actions/cache@v5
with: with:
path: thirdparty path: thirdparty
key: ci-cmake-${{ runner.OS }}-thirdparty-v1.10.15-r1 key: ci-cmake-${{ runner.OS }}-thirdparty-v1.10.15-r1
@ -144,7 +147,7 @@ jobs:
- name: ccache (non-Windows) - name: ccache (non-Windows)
if: runner.os != 'Windows' if: runner.os != 'Windows'
uses: actions/cache@v4 uses: actions/cache@v5
with: with:
path: ccache path: ccache
key: ci-cmake-ccache-${{ matrix.profile }} key: ci-cmake-ccache-${{ matrix.profile }}
@ -182,8 +185,13 @@ jobs:
-D HAVE_PYTHON=${{ runner.os != 'Windows' && matrix.python || 'NO' }} -D HAVE_PYTHON=${{ runner.os != 'Windows' && matrix.python || 'NO' }}
-D HAVE_EIGEN=${{ matrix.eigen }} -D HAVE_EIGEN=${{ matrix.eigen }}
-D STDFLOAT_DOUBLE=${{ matrix.double }} -D STDFLOAT_DOUBLE=${{ matrix.double }}
-D ENABLE_ASAN=${{ matrix.asan || 'NO' }}
.. ..
echo "$PWD/${{ matrix.config }}/bin" >> $GITHUB_PATH
echo "$PWD/bin" >> $GITHUB_PATH
- name: Build (no Python) - name: Build (no Python)
if: contains(matrix.python, 'NO') if: contains(matrix.python, 'NO')
# BEGIN A # BEGIN A
@ -193,7 +201,7 @@ jobs:
- name: Setup Python (Python 3.8) - name: Setup Python (Python 3.8)
if: contains(matrix.python, 'YES') if: contains(matrix.python, 'YES')
uses: actions/setup-python@v5 uses: actions/setup-python@v6
with: with:
python-version: '3.8' python-version: '3.8'
- name: Configure (Python 3.8) - name: Configure (Python 3.8)
@ -216,16 +224,17 @@ jobs:
shell: bash shell: bash
env: env:
PYTHONPATH: ${{ matrix.config }} PYTHONPATH: ${{ matrix.config }}
ASAN_OPTIONS: detect_leaks=0:detect_odr_violation=0:log_path=asan.log.py38
run: | run: |
PYTHON_EXECUTABLE=$(grep 'Python_EXECUTABLE:' CMakeCache.txt | sed 's/.*=//') PYTHON_EXECUTABLE=$(grep 'Python_EXECUTABLE:' CMakeCache.txt | sed 's/.*=//')
$PYTHON_EXECUTABLE -m pip install -r ../requirements-test.txt $PYTHON_EXECUTABLE -m pip install -r ../requirements-test.txt
export COVERAGE_FILE=.coverage.$RANDOM LLVM_PROFILE_FILE=$PWD/pid-%p.profraw export COVERAGE_FILE=.coverage.$RANDOM LLVM_PROFILE_FILE=$PWD/pid-%p.profraw
$PYTHON_EXECUTABLE -m pytest ../tests --cov=. run_pytest ../tests --cov=.
# END B # END B
- name: Setup Python (Python 3.9) - name: Setup Python (Python 3.9)
if: contains(matrix.python, 'YES') if: contains(matrix.python, 'YES')
uses: actions/setup-python@v5 uses: actions/setup-python@v6
with: with:
python-version: '3.9' python-version: '3.9'
- name: Configure (Python 3.9) - name: Configure (Python 3.9)
@ -248,16 +257,17 @@ jobs:
shell: bash shell: bash
env: env:
PYTHONPATH: ${{ matrix.config }} PYTHONPATH: ${{ matrix.config }}
ASAN_OPTIONS: detect_leaks=0:detect_odr_violation=0:log_path=asan.log.py39
run: | run: |
PYTHON_EXECUTABLE=$(grep 'Python_EXECUTABLE:' CMakeCache.txt | sed 's/.*=//') PYTHON_EXECUTABLE=$(grep 'Python_EXECUTABLE:' CMakeCache.txt | sed 's/.*=//')
$PYTHON_EXECUTABLE -m pip install -r ../requirements-test.txt $PYTHON_EXECUTABLE -m pip install -r ../requirements-test.txt
export COVERAGE_FILE=.coverage.$RANDOM LLVM_PROFILE_FILE=$PWD/pid-%p.profraw export COVERAGE_FILE=.coverage.$RANDOM LLVM_PROFILE_FILE=$PWD/pid-%p.profraw
$PYTHON_EXECUTABLE -m pytest ../tests --cov=. run_pytest ../tests --cov=.
# END B # END B
- name: Setup Python (Python 3.10) - name: Setup Python (Python 3.10)
if: contains(matrix.python, 'YES') if: contains(matrix.python, 'YES')
uses: actions/setup-python@v5 uses: actions/setup-python@v6
with: with:
python-version: '3.10' python-version: '3.10'
- name: Configure (Python 3.10) - name: Configure (Python 3.10)
@ -280,16 +290,17 @@ jobs:
shell: bash shell: bash
env: env:
PYTHONPATH: ${{ matrix.config }} PYTHONPATH: ${{ matrix.config }}
ASAN_OPTIONS: detect_leaks=0:detect_odr_violation=0:log_path=asan.log.py310
run: | run: |
PYTHON_EXECUTABLE=$(grep 'Python_EXECUTABLE:' CMakeCache.txt | sed 's/.*=//') PYTHON_EXECUTABLE=$(grep 'Python_EXECUTABLE:' CMakeCache.txt | sed 's/.*=//')
$PYTHON_EXECUTABLE -m pip install -r ../requirements-test.txt $PYTHON_EXECUTABLE -m pip install -r ../requirements-test.txt
export COVERAGE_FILE=.coverage.$RANDOM LLVM_PROFILE_FILE=$PWD/pid-%p.profraw export COVERAGE_FILE=.coverage.$RANDOM LLVM_PROFILE_FILE=$PWD/pid-%p.profraw
$PYTHON_EXECUTABLE -m pytest ../tests --cov=. run_pytest ../tests --cov=.
# END B # END B
- name: Setup Python (Python 3.11) - name: Setup Python (Python 3.11)
if: contains(matrix.python, 'YES') if: contains(matrix.python, 'YES')
uses: actions/setup-python@v5 uses: actions/setup-python@v6
with: with:
python-version: '3.11' python-version: '3.11'
- name: Configure (Python 3.11) - name: Configure (Python 3.11)
@ -312,16 +323,17 @@ jobs:
shell: bash shell: bash
env: env:
PYTHONPATH: ${{ matrix.config }} PYTHONPATH: ${{ matrix.config }}
ASAN_OPTIONS: detect_leaks=0:detect_odr_violation=0:log_path=asan.log.py311
run: | run: |
PYTHON_EXECUTABLE=$(grep 'Python_EXECUTABLE:' CMakeCache.txt | sed 's/.*=//') PYTHON_EXECUTABLE=$(grep 'Python_EXECUTABLE:' CMakeCache.txt | sed 's/.*=//')
$PYTHON_EXECUTABLE -m pip install -r ../requirements-test.txt $PYTHON_EXECUTABLE -m pip install -r ../requirements-test.txt
export COVERAGE_FILE=.coverage.$RANDOM LLVM_PROFILE_FILE=$PWD/pid-%p.profraw export COVERAGE_FILE=.coverage.$RANDOM LLVM_PROFILE_FILE=$PWD/pid-%p.profraw
$PYTHON_EXECUTABLE -m pytest ../tests --cov=. run_pytest ../tests --cov=.
# END B # END B
- name: Setup Python (Python 3.12) - name: Setup Python (Python 3.12)
if: contains(matrix.python, 'YES') if: contains(matrix.python, 'YES')
uses: actions/setup-python@v5 uses: actions/setup-python@v6
with: with:
python-version: '3.12' python-version: '3.12'
- name: Configure (Python 3.12) - name: Configure (Python 3.12)
@ -344,13 +356,26 @@ jobs:
shell: bash shell: bash
env: env:
PYTHONPATH: ${{ matrix.config }} PYTHONPATH: ${{ matrix.config }}
ASAN_OPTIONS: detect_leaks=0:detect_odr_violation=0:log_path=asan.log.py312
run: | run: |
PYTHON_EXECUTABLE=$(grep 'Python_EXECUTABLE:' CMakeCache.txt | sed 's/.*=//') PYTHON_EXECUTABLE=$(grep 'Python_EXECUTABLE:' CMakeCache.txt | sed 's/.*=//')
$PYTHON_EXECUTABLE -m pip install -r ../requirements-test.txt $PYTHON_EXECUTABLE -m pip install -r ../requirements-test.txt
export COVERAGE_FILE=.coverage.$RANDOM LLVM_PROFILE_FILE=$PWD/pid-%p.profraw export COVERAGE_FILE=.coverage.$RANDOM LLVM_PROFILE_FILE=$PWD/pid-%p.profraw
$PYTHON_EXECUTABLE -m pytest ../tests --cov=. run_pytest ../tests --cov=.
# END B # END B
- name: Show ASan errors
if: failure() && contains(matrix.asan, 'YES')
working-directory: build
run: |
ls -lAh asan.log.* || true
if cat asan.log.* 2>/dev/null | grep -q .; then
echo '### Address Sanitizer Report' >> $GITHUB_STEP_SUMMARY
echo '```' >> $GITHUB_STEP_SUMMARY
cat asan.log.* >> $GITHUB_STEP_SUMMARY 2>/dev/null || echo "No ASan errors found"
echo '```' >> $GITHUB_STEP_SUMMARY
fi
- name: Upload coverage reports - name: Upload coverage reports
if: always() && matrix.config == 'Coverage' if: always() && matrix.config == 'Coverage'
working-directory: build working-directory: build
@ -364,6 +389,13 @@ jobs:
python -m pip install coverage python -m pip install coverage
python -m coverage combine $(find . -name '.coverage.*') python -m coverage combine $(find . -name '.coverage.*')
TOTAL=$(python -m coverage report --format=total)
echo '### Coverage Report' >> $GITHUB_STEP_SUMMARY
echo "<details><summary>Coverage: <b>${TOTAL}%</b></summary>" >> $GITHUB_STEP_SUMMARY
echo '' >> $GITHUB_STEP_SUMMARY
python -m coverage report --format=markdown >> $GITHUB_STEP_SUMMARY
echo '</details>' >> $GITHUB_STEP_SUMMARY
llvm-profdata merge pid-*.profraw -o coverage.profdata llvm-profdata merge pid-*.profraw -o coverage.profdata
llvm-cov show $(grep -Rl LLVM_PROFILE_FILE . | sed 's/^/-object /') -instr-profile=coverage.profdata > coverage.txt llvm-cov show $(grep -Rl LLVM_PROFILE_FILE . | sed 's/^/-object /') -instr-profile=coverage.profdata > coverage.txt
bash <(curl -s https://codecov.io/bash) -y ../.github/codecov.yml bash <(curl -s https://codecov.io/bash) -y ../.github/codecov.yml
@ -372,12 +404,12 @@ jobs:
if: "!contains(github.event.head_commit.message, '[skip ci]') && !contains(github.event.head_commit.message, '[ci skip]')" if: "!contains(github.event.head_commit.message, '[skip ci]') && !contains(github.event.head_commit.message, '[ci skip]')"
strategy: strategy:
matrix: matrix:
os: [ubuntu-20.04, windows-2019, macOS-13] os: [ubuntu-22.04, windows-2022, macOS-15]
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v6
- name: Install dependencies (Ubuntu) - name: Install dependencies (Ubuntu)
if: matrix.os == 'ubuntu-20.04' if: matrix.os == 'ubuntu-22.04'
run: | run: |
sudo apt-get update sudo apt-get update
sudo apt-get install build-essential bison flex libfreetype6-dev libgl1-mesa-dev libjpeg-dev libode-dev libopenal-dev libpng-dev libssl-dev libvorbis-dev libx11-dev libxcursor-dev libxrandr-dev nvidia-cg-toolkit zlib1g-dev sudo apt-get install build-essential bison flex libfreetype6-dev libgl1-mesa-dev libjpeg-dev libode-dev libopenal-dev libpng-dev libssl-dev libvorbis-dev libx11-dev libxcursor-dev libxrandr-dev nvidia-cg-toolkit zlib1g-dev
@ -386,30 +418,30 @@ jobs:
shell: powershell shell: powershell
run: | run: |
$wc = New-Object System.Net.WebClient $wc = New-Object System.Net.WebClient
$wc.DownloadFile("https://www.panda3d.org/download/panda3d-1.10.15/panda3d-1.10.15-tools-win64.zip", "thirdparty-tools.zip") $wc.DownloadFile("https://www.panda3d.org/download/panda3d-1.10.16/panda3d-1.10.16-tools-win64.zip", "thirdparty-tools.zip")
Expand-Archive -Path thirdparty-tools.zip Expand-Archive -Path thirdparty-tools.zip
Move-Item -Path thirdparty-tools/panda3d-1.10.15/thirdparty -Destination . Move-Item -Path thirdparty-tools/panda3d-1.10.16/thirdparty -Destination .
- name: Get thirdparty packages (macOS) - name: Get thirdparty packages (macOS)
if: runner.os == 'macOS' if: runner.os == 'macOS'
run: | run: |
curl -O https://www.panda3d.org/download/panda3d-1.10.15/panda3d-1.10.15-tools-mac.tar.gz curl -O https://www.panda3d.org/download/panda3d-1.10.16/panda3d-1.10.16-tools-mac.tar.gz
tar -xf panda3d-1.10.15-tools-mac.tar.gz tar -xf panda3d-1.10.16-tools-mac.tar.gz
mv panda3d-1.10.15/thirdparty thirdparty mv panda3d-1.10.16/thirdparty thirdparty
rmdir panda3d-1.10.15 rmdir panda3d-1.10.16
(cd thirdparty/darwin-libs-a && rm -rf rocket) (cd thirdparty/darwin-libs-a && rm -rf rocket)
- name: Set up XCode (macOS) - name: Set up XCode (macOS)
if: runner.os == 'macOS' if: runner.os == 'macOS'
run: sudo xcode-select -s /Applications/Xcode_14.3.1.app/Contents/Developer run: sudo xcode-select -s /Applications/Xcode_16.app/Contents/Developer
- name: Set up Python 3.13 - name: Set up Python 3.13
uses: actions/setup-python@v5 uses: actions/setup-python@v6
with: with:
python-version: '3.13' python-version: '3.13'
- name: Build Python 3.13 - name: Build Python 3.13
shell: bash shell: bash
run: | run: |
python makepanda/makepanda.py --git-commit=${{github.sha}} --outputdir=built --everything --no-eigen --python-incdir="$pythonLocation/include" --python-libdir="$pythonLocation/lib" --verbose --threads=4 --windows-sdk=10 --msvc-version=14.2 python makepanda/makepanda.py --git-commit=${{github.sha}} --outputdir=built --everything --no-eigen --python-incdir="$pythonLocation/include" --python-libdir="$pythonLocation/lib" --verbose --threads=4 --windows-sdk=10 --msvc-version=14.3
- name: Test Python 3.13 - name: Test Python 3.13
shell: bash shell: bash
run: | run: |
@ -417,13 +449,13 @@ jobs:
PYTHONPATH=built LD_LIBRARY_PATH=built/lib:$pythonLocation/lib DYLD_LIBRARY_PATH=built/lib python -m pytest PYTHONPATH=built LD_LIBRARY_PATH=built/lib:$pythonLocation/lib DYLD_LIBRARY_PATH=built/lib python -m pytest
- name: Set up Python 3.12 - name: Set up Python 3.12
uses: actions/setup-python@v5 uses: actions/setup-python@v6
with: with:
python-version: '3.12' python-version: '3.12'
- name: Build Python 3.12 (double) - name: Build Python 3.12 (double)
shell: bash shell: bash
run: | run: |
python makepanda/makepanda.py --override STDFLOAT_DOUBLE=1 --git-commit=${{github.sha}} --outputdir=built_dbl --everything --no-eigen --python-incdir="$pythonLocation/include" --python-libdir="$pythonLocation/lib" --verbose --threads=4 --windows-sdk=10 --msvc-version=14.2 python makepanda/makepanda.py --override STDFLOAT_DOUBLE=1 --git-commit=${{github.sha}} --outputdir=built_dbl --everything --no-eigen --python-incdir="$pythonLocation/include" --python-libdir="$pythonLocation/lib" --verbose --threads=4 --windows-sdk=10 --msvc-version=14.3
- name: Test Python 3.12 (double) - name: Test Python 3.12 (double)
shell: bash shell: bash
run: | run: |
@ -435,13 +467,13 @@ jobs:
python makepanda/makepackage.py --verbose --lzma --outputdir=built_dbl python makepanda/makepackage.py --verbose --lzma --outputdir=built_dbl
- name: Set up Python 3.11 - name: Set up Python 3.11
uses: actions/setup-python@v5 uses: actions/setup-python@v6
with: with:
python-version: '3.11' python-version: '3.11'
- name: Build Python 3.11 - name: Build Python 3.11
shell: bash shell: bash
run: | run: |
python makepanda/makepanda.py --git-commit=${{github.sha}} --outputdir=built --everything --no-eigen --python-incdir="$pythonLocation/include" --python-libdir="$pythonLocation/lib" --verbose --threads=4 --windows-sdk=10 --msvc-version=14.2 python makepanda/makepanda.py --git-commit=${{github.sha}} --outputdir=built --everything --no-eigen --python-incdir="$pythonLocation/include" --python-libdir="$pythonLocation/lib" --verbose --threads=4 --windows-sdk=10 --msvc-version=14.3
- name: Test Python 3.11 - name: Test Python 3.11
shell: bash shell: bash
run: | run: |
@ -449,13 +481,13 @@ jobs:
PYTHONPATH=built LD_LIBRARY_PATH=built/lib:$pythonLocation/lib DYLD_LIBRARY_PATH=built/lib python -m pytest PYTHONPATH=built LD_LIBRARY_PATH=built/lib:$pythonLocation/lib DYLD_LIBRARY_PATH=built/lib python -m pytest
- name: Set up Python 3.10 - name: Set up Python 3.10
uses: actions/setup-python@v5 uses: actions/setup-python@v6
with: with:
python-version: '3.10' python-version: '3.10'
- name: Build Python 3.10 - name: Build Python 3.10
shell: bash shell: bash
run: | run: |
python makepanda/makepanda.py --git-commit=${{github.sha}} --outputdir=built --everything --no-eigen --python-incdir="$pythonLocation/include" --python-libdir="$pythonLocation/lib" --verbose --threads=4 --windows-sdk=10 --msvc-version=14.2 python makepanda/makepanda.py --git-commit=${{github.sha}} --outputdir=built --everything --no-eigen --python-incdir="$pythonLocation/include" --python-libdir="$pythonLocation/lib" --verbose --threads=4 --windows-sdk=10 --msvc-version=14.3
- name: Test Python 3.10 - name: Test Python 3.10
shell: bash shell: bash
run: | run: |
@ -463,13 +495,13 @@ jobs:
PYTHONPATH=built LD_LIBRARY_PATH=built/lib:$pythonLocation/lib DYLD_LIBRARY_PATH=built/lib python -m pytest PYTHONPATH=built LD_LIBRARY_PATH=built/lib:$pythonLocation/lib DYLD_LIBRARY_PATH=built/lib python -m pytest
- name: Set up Python 3.9 - name: Set up Python 3.9
uses: actions/setup-python@v5 uses: actions/setup-python@v6
with: with:
python-version: '3.9' python-version: '3.9'
- name: Build Python 3.9 - name: Build Python 3.9
shell: bash shell: bash
run: | run: |
python makepanda/makepanda.py --git-commit=${{github.sha}} --outputdir=built --everything --no-eigen --python-incdir="$pythonLocation/include" --python-libdir="$pythonLocation/lib" --verbose --threads=4 --windows-sdk=10 --msvc-version=14.2 python makepanda/makepanda.py --git-commit=${{github.sha}} --outputdir=built --everything --no-eigen --python-incdir="$pythonLocation/include" --python-libdir="$pythonLocation/lib" --verbose --threads=4 --windows-sdk=10 --msvc-version=14.3
- name: Test Python 3.9 - name: Test Python 3.9
shell: bash shell: bash
run: | run: |
@ -477,13 +509,13 @@ jobs:
PYTHONPATH=built LD_LIBRARY_PATH=built/lib:$pythonLocation/lib DYLD_LIBRARY_PATH=built/lib python -m pytest PYTHONPATH=built LD_LIBRARY_PATH=built/lib:$pythonLocation/lib DYLD_LIBRARY_PATH=built/lib python -m pytest
- name: Set up Python 3.8 - name: Set up Python 3.8
uses: actions/setup-python@v5 uses: actions/setup-python@v6
with: with:
python-version: '3.8' python-version: '3.8'
- name: Build Python 3.8 - name: Build Python 3.8
shell: bash shell: bash
run: | run: |
python makepanda/makepanda.py --git-commit=${{github.sha}} --outputdir=built --everything --no-eigen --python-incdir="$pythonLocation/include" --python-libdir="$pythonLocation/lib" --verbose --threads=4 --windows-sdk=10 --msvc-version=14.2 python makepanda/makepanda.py --git-commit=${{github.sha}} --outputdir=built --everything --no-eigen --python-incdir="$pythonLocation/include" --python-libdir="$pythonLocation/lib" --verbose --threads=4 --windows-sdk=10 --msvc-version=14.3
- name: Test Python 3.8 - name: Test Python 3.8
shell: bash shell: bash
run: | run: |
@ -498,7 +530,7 @@ jobs:
if: "!contains(github.event.head_commit.message, '[skip ci]') && !contains(github.event.head_commit.message, '[ci skip]')" if: "!contains(github.event.head_commit.message, '[skip ci]') && !contains(github.event.head_commit.message, '[ci skip]')"
runs-on: ubuntu-24.04 runs-on: ubuntu-24.04
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v6
- name: Install dependencies - name: Install dependencies
run: | run: |
@ -508,32 +540,32 @@ jobs:
- name: Setup emsdk - name: Setup emsdk
uses: mymindstorm/setup-emsdk@v14 uses: mymindstorm/setup-emsdk@v14
with: with:
version: 3.1.70 version: 4.0.2
actions-cache-folder: 'emsdk-cache' actions-cache-folder: 'emsdk-cache'
- name: Restore Python build cache - name: Restore Python build cache
id: cache-emscripten-python-restore id: cache-emscripten-python-restore
uses: actions/cache/restore@v4 uses: actions/cache/restore@v5
with: with:
path: ~/python path: ~/python
key: cache-emscripten-python-3.12.7 key: cache-emscripten-python-3.12.8
- name: Build Python 3.12 - name: Build Python 3.12
if: steps.cache-emscripten-python-restore.outputs.cache-hit != 'true' if: steps.cache-emscripten-python-restore.outputs.cache-hit != 'true'
run: | run: |
wget https://www.python.org/ftp/python/3.12.7/Python-3.12.7.tar.xz wget https://www.python.org/ftp/python/3.12.8/Python-3.12.8.tar.xz
tar -xJf Python-3.12.7.tar.xz tar -xJf Python-3.12.8.tar.xz
(cd Python-3.12.7 && EM_CONFIG=$EMSDK/.emscripten python3 Tools/wasm/wasm_build.py emscripten-browser) (cd Python-3.12.8 && EM_CONFIG=$EMSDK/.emscripten python3 Tools/wasm/wasm_build.py emscripten-browser)
(cd Python-3.12.7/builddir/emscripten-browser && make install DESTDIR=~/python) (cd Python-3.12.8/builddir/emscripten-browser && make install DESTDIR=~/python)
cp Python-3.12.7/builddir/emscripten-browser/Modules/_hacl/libHacl_Hash_SHA2.a ~/python/usr/local/lib cp Python-3.12.8/builddir/emscripten-browser/Modules/_hacl/libHacl_Hash_SHA2.a ~/python/usr/local/lib
cp Python-3.12.7/builddir/emscripten-browser/Modules/_decimal/libmpdec/libmpdec.a ~/python/usr/local/lib cp Python-3.12.8/builddir/emscripten-browser/Modules/_decimal/libmpdec/libmpdec.a ~/python/usr/local/lib
cp Python-3.12.7/builddir/emscripten-browser/Modules/expat/libexpat.a ~/python/usr/local/lib cp Python-3.12.8/builddir/emscripten-browser/Modules/expat/libexpat.a ~/python/usr/local/lib
rm -rf Python-3.12.7 rm -rf Python-3.12.8
- name: Save Python build cache - name: Save Python build cache
id: cache-emscripten-python-save id: cache-emscripten-python-save
if: steps.cache-emscripten-python-restore.outputs.cache-hit != 'true' if: steps.cache-emscripten-python-restore.outputs.cache-hit != 'true'
uses: actions/cache/save@v4 uses: actions/cache/save@v5
with: with:
path: ~/python path: ~/python
key: ${{ steps.cache-emscripten-python-restore.outputs.cache-primary-key }} key: ${{ steps.cache-emscripten-python-restore.outputs.cache-primary-key }}
@ -552,3 +584,156 @@ jobs:
shell: bash shell: bash
run: | run: |
PYTHONHOME=~/python/usr/local PYTHONPATH=built node built/bin/run_tests.js tests PYTHONHOME=~/python/usr/local PYTHONPATH=built node built/bin/run_tests.js tests
android:
if: "!contains(github.event.head_commit.message, '[skip ci]') && !contains(github.event.head_commit.message, '[ci skip]')"
strategy:
fail-fast: false
matrix:
include:
- android-abi: arm64-v8a
android-arch: arm64
android-triple: aarch64-linux-android
ndk-version: 29.0.14206865
runner: ubuntu-latest
- android-abi: armeabi-v7a
android-arch: arm
android-triple: arm-linux-androideabi
ndk-version: 29.0.14206865
runner: ubuntu-latest
- android-abi: x86_64
android-arch: x86_64
android-triple: x86_64-linux-android
ndk-version: 29.0.14206865
runner: ubuntu-latest
- android-abi: x86
android-arch: x86
android-triple: i686-linux-android
ndk-version: 29.0.14206865
runner: ubuntu-latest
runs-on: ${{ matrix.runner }}
steps:
- uses: actions/checkout@v6
- name: Setup Android SDK
uses: android-actions/setup-android@v3
with:
packages: 'tools platform-tools platforms;android-21 ndk;${{ matrix.ndk-version }}'
- name: Set up Android NDK
run: |
echo "ANDROID_NDK_ROOT=$ANDROID_SDK_ROOT/ndk/${{ matrix.ndk-version }}" >> $GITHUB_ENV
- uses: actions/setup-python@v6
with:
python-version: '3.13'
- name: Restore Python build cache
id: cache-android-python-restore
uses: actions/cache/restore@v5
with:
path: ~/python-prefix
key: cache-android-python-3.13.9-${{ matrix.android-abi }}-ndk${{ matrix.ndk-version }}
- name: Build Python 3.13
if: steps.cache-android-python-restore.outputs.cache-hit != 'true'
run: |
wget https://www.python.org/ftp/python/3.13.9/Python-3.13.9.tar.xz
tar -xJf Python-3.13.9.tar.xz
sed -i.bak "s/aarch64-linux-android/${{ matrix.android-triple }}/" Python-3.13.9/Android/android.py
sed -i.bak "s/ndk_version=[0-9.]\{1,\}/ndk_version=${{ matrix.ndk-version }}/" Python-3.13.9/Android/android-env.sh
(cd Python-3.13.9/Android && python3 android.py build ${{ matrix.android-triple }})
cp -R Python-3.13.9/cross-build/${{ matrix.android-triple }}/prefix ~/python-prefix
rm -rf Python-3.13.9
- name: Save Python build cache
id: cache-android-python-save
if: steps.cache-android-python-restore.outputs.cache-hit != 'true'
uses: actions/cache/save@v5
with:
path: ~/python-prefix
key: ${{ steps.cache-android-python-restore.outputs.cache-primary-key }}
- name: Restore thirdparty build cache
id: cache-android-thirdparty-restore
uses: actions/cache/restore@v5
with:
path: ./thirdparty
key: cache-android-thirdparty-935c80380ca171e08587c570e9dad678d29db3c8-${{ matrix.android-abi }}-ndk${{ matrix.ndk-version }}
- name: Install yasm
if: steps.cache-android-thirdparty-restore.outputs.cache-hit != 'true'
run: ${{runner.os == 'macOS' && 'brew' || 'sudo apt-get'}} install yasm
- name: Build thirdparty packages
if: steps.cache-android-thirdparty-restore.outputs.cache-hit != 'true'
run: |
git clone --revision=935c80380ca171e08587c570e9dad678d29db3c8 --depth=1 https://github.com/rdb/panda3d-thirdparty.git thirdparty
cd thirdparty
cmake -B build \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK_ROOT/build/cmake/android.toolchain.cmake \
-DCMAKE_ANDROID_ARCH_ABI=${{ matrix.android-abi }} \
-DCMAKE_ANDROID_ARCH=${{ matrix.android-arch }} \
-DCMAKE_SYSTEM_VERSION=21 \
-DANDROID_ABI=${{ matrix.android-abi }} \
-DANDROID_PLATFORM=android-21 \
-DBUILD_FCOLLADA=OFF \
-DBUILD_OPENSSL=OFF \
-DBUILD_VRPN=OFF \
-DBUILD_ARTOOLKIT=OFF
cmake --build build --config Release -j4
rm -rf build
- name: Save thirdparty build cache
id: cache-android-thirdparty-save
if: steps.cache-android-thirdparty-restore.outputs.cache-hit != 'true'
uses: actions/cache/save@v5
with:
path: ./thirdparty
key: ${{ steps.cache-android-thirdparty-restore.outputs.cache-primary-key }}
- name: Build for Android
shell: bash
run: |
python makepanda/makepanda.py --git-commit=${{github.sha}} --target android-21 --python-incdir=~/python-prefix/include --python-libdir=~/python-prefix/lib --arch ${{ matrix.android-arch }} --everything --no-openssl --no-tinydisplay --no-pandatool --verbose --threads=4
- name: Install test dependencies
shell: bash
run: |
python -m pip install -t ~/python-prefix/lib/python3.13/site-packages -r requirements-test.txt
- name: Enable KVM group perms
if: startsWith(matrix.runner, 'ubuntu')
run: |
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
sudo udevadm control --reload-rules
sudo udevadm trigger --name-match=kvm
- name: Setup Android Emulator and Run Tests
if: startsWith(matrix.android-abi, 'x86')
uses: reactivecircus/android-emulator-runner@v2
with:
api-level: 27
arch: ${{ matrix.android-abi }}
target: default
profile: pixel_6
script: |
adb shell "rm -rf /data/local/tmp/panda3d-tests"
adb push built/bin/run_tests /data/local/tmp/panda3d-tests/run_tests
adb shell "mkdir /data/local/tmp/panda3d-tests/lib"
adb push built/lib/*.so /data/local/tmp/panda3d-tests/lib
adb push ~/python-prefix/lib/*.so /data/local/tmp/panda3d-tests/lib
adb push ~/python-prefix/lib/python3.13 /data/local/tmp/panda3d-tests/lib/python3.13
adb push built/panda3d /data/local/tmp/panda3d-tests/lib/python3.13/site-packages/
adb push built/direct /data/local/tmp/panda3d-tests/lib/python3.13/site-packages/
adb push built/models /data/local/tmp/panda3d-tests/models
adb push built/etc /data/local/tmp/panda3d-tests/etc
adb push tests /data/local/tmp/panda3d-tests/tests
adb shell "cd /data/local/tmp/panda3d-tests && LD_LIBRARY_PATH=/data/local/tmp/panda3d-tests/lib PYTHONPATH=/data/local/tmp/panda3d-tests/lib/python3.13/site-packages ./run_tests -v tests"

View File

@ -7,17 +7,17 @@ jobs:
strategy: strategy:
matrix: matrix:
os: [ubuntu-latest, macos-latest, windows-latest] os: [ubuntu-latest, macos-latest, windows-latest]
python-version: ['3.8', '3.11'] python-version: ['3.9', '3.13']
fail-fast: false fail-fast: false
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v6
- name: Set up Python ${{ matrix.python-version }} - name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5 uses: actions/setup-python@v6
with: with:
python-version: ${{ matrix.python-version }} python-version: ${{ matrix.python-version }}
- name: Install dependencies - name: Install dependencies
run: | run: |
python -m pip install --upgrade pip python -m pip install --upgrade pip
pip install mypy==1.4.0 pip install mypy==1.14.1
- name: Run mypy on direct - name: Run mypy on direct
run: python tests/run_mypy.py run: python tests/run_mypy.py

View File

@ -5,10 +5,12 @@ This is a list of all the people who are contributing financially to Panda3D. I
## Bronze Sponsors ## Bronze Sponsors
[<img src="https://www.panda3d.org/wp-content/uploads/2024/08/Route4MeLogo1185x300-2-1-1024x259.png" alt="Route4Me" height="48">](https://route4me.com/) [<img src="https://www.panda3d.org/wp-content/uploads/2024/08/Route4MeLogo1185x300-2-1-1024x259.png" alt="Route4Me" height="48">](https://route4me.com/)
[<img src="https://www.panda3d.org/wp-content/uploads/2026/01/black-logo-1-e1768931551108.png" alt="TestMu AI" height="48">](https://www.testmu.ai/?utm_source=panda3d&utm_medium=sponsor)
* [Daniel Stokes](https://opencollective.com/daniel-stokes) * [Daniel Stokes](https://opencollective.com/daniel-stokes)
* [David Rose](https://opencollective.com/david-rose) * [David Rose](https://opencollective.com/david-rose)
* [Route4Me](https://route4me.com/) * [Route4Me](https://route4me.com/)
* [TestMu AI](https://www.testmu.ai/?utm_source=panda3d&utm_medium=sponsor)
## Benefactors ## Benefactors

View File

@ -109,6 +109,7 @@ option(BUILD_DIRECT "Build the direct source tree." ON)
option(BUILD_PANDATOOL "Build the pandatool source tree." ON) option(BUILD_PANDATOOL "Build the pandatool source tree." ON)
option(BUILD_CONTRIB "Build the contrib source tree." ON) option(BUILD_CONTRIB "Build the contrib source tree." ON)
option(BUILD_MODELS "Build/install the built-in models." ON) option(BUILD_MODELS "Build/install the built-in models." ON)
option(BUILD_TESTS "Build unit test runner." ON)
option(BUILD_TOOLS "Build binary tools." ON) option(BUILD_TOOLS "Build binary tools." ON)
# Include Panda3D packages # Include Panda3D packages
@ -166,17 +167,8 @@ if(BUILD_MODELS)
COMPONENT Models DESTINATION ${CMAKE_INSTALL_DATADIR}/panda3d) COMPONENT Models DESTINATION ${CMAKE_INSTALL_DATADIR}/panda3d)
endif() endif()
if(INTERROGATE_PYTHON_INTERFACE) if(BUILD_TESTS)
# If we built the Python interface, run the test suite. Note, we do NOT test add_subdirectory(tests "${CMAKE_BINARY_DIR}/tests")
# for pytest before adding this test. If the user doesn't have pytest, we'd
# like for the tests to fail.
# In the Coverage configuration, we also require pytest-cov
add_test(NAME pytest
COMMAND "${PYTHON_EXECUTABLE}" -m pytest "${PROJECT_SOURCE_DIR}/tests"
$<$<CONFIG:Coverage>:--cov=.>
WORKING_DIRECTORY "${PANDA_OUTPUT_DIR}")
endif() endif()
# Generate the Panda3DConfig.cmake file so find_package(Panda3D) works, and # Generate the Panda3DConfig.cmake file so find_package(Panda3D) works, and

View File

@ -24,7 +24,7 @@ Installing Panda3D
================== ==================
The latest Panda3D SDK can be downloaded from The latest Panda3D SDK can be downloaded from
[this page](https://www.panda3d.org/download/sdk-1-10-15/). [this page](https://www.panda3d.org/download/sdk-1-10-16/).
If you are familiar with installing Python packages, you can use If you are familiar with installing Python packages, you can use
the following command: the following command:
@ -64,8 +64,8 @@ depending on whether you are on a 32-bit or 64-bit system, or you can
[click here](https://github.com/rdb/panda3d-thirdparty) for instructions on [click here](https://github.com/rdb/panda3d-thirdparty) for instructions on
building them from source. building them from source.
- https://www.panda3d.org/download/panda3d-1.10.15/panda3d-1.10.15-tools-win64.zip - https://www.panda3d.org/download/panda3d-1.10.16/panda3d-1.10.16-tools-win64.zip
- https://www.panda3d.org/download/panda3d-1.10.15/panda3d-1.10.15-tools-win32.zip - https://www.panda3d.org/download/panda3d-1.10.16/panda3d-1.10.16-tools-win32.zip
After acquiring these dependencies, you can build Panda3D from the command After acquiring these dependencies, you can build Panda3D from the command
prompt using the following command. Change the `--msvc-version` option based prompt using the following command. Change the `--msvc-version` option based
@ -136,7 +136,7 @@ macOS
----- -----
On macOS, you will need to download a set of precompiled thirdparty packages in order to On macOS, you will need to download a set of precompiled thirdparty packages in order to
compile Panda3D, which can be acquired from [here](https://www.panda3d.org/download/panda3d-1.10.15/panda3d-1.10.15-tools-mac.tar.gz). compile Panda3D, which can be acquired from [here](https://www.panda3d.org/download/panda3d-1.10.16/panda3d-1.10.16-tools-mac.tar.gz).
After placing the thirdparty directory inside the panda3d source directory, After placing the thirdparty directory inside the panda3d source directory,
you may build Panda3D using a command like the following: you may build Panda3D using a command like the following:
@ -181,22 +181,21 @@ Although it's possible to build Panda3D on an Android device using the
[termux](https://termux.com/) shell, the recommended route is to cross-compile [termux](https://termux.com/) shell, the recommended route is to cross-compile
.whl files using the SDK and NDK, which can then be used by the `build_apps` .whl files using the SDK and NDK, which can then be used by the `build_apps`
command to build a Python application into an .apk or .aab bundle. You will command to build a Python application into an .apk or .aab bundle. You will
need to get the latest thirdparty packages, which can be obtained from the need to get the latest thirdparty packages, which can be obtained from here:
artifacts page of the last successful run here:
https://github.com/rdb/panda3d-thirdparty/actions?query=branch%3Amain+is%3Asuccess+event%3Apush https://rdb.name/thirdparty-android.tar.gz
This does not include Python at the moment, which can be extracted from This includes a copy of Python 3.13 compiled for Android. You will need to
[this archive](https://rdb.name/thirdparty-android.tar.gz) instead. use Python 3.13 on the host as well.
These commands show how to compile wheels for the supported Android ABIs: These commands show how to compile wheels for the supported Android ABIs:
```bash ```bash
export ANDROID_SDK_ROOT=/home/rdb/local/android export ANDROID_SDK_ROOT=/home/rdb/local/android
python3.8 makepanda/makepanda.py --everything --outputdir built-droid-arm64 --arch arm64 --target android-21 --threads 6 --wheel python3.13 makepanda/makepanda.py --everything --outputdir built-droid-arm64 --arch arm64 --target android-21 --threads 6 --wheel
python3.8 makepanda/makepanda.py --everything --outputdir built-droid-armv7a --arch armv7a --target android-19 --threads 6 --wheel python3.13 makepanda/makepanda.py --everything --outputdir built-droid-armv7a --arch arm --target android-21 --threads 6 --wheel
python3.8 makepanda/makepanda.py --everything --outputdir built-droid-x86_64 --arch x86_64 --target android-21 --threads 6 --wheel python3.13 makepanda/makepanda.py --everything --outputdir built-droid-x86_64 --arch x86_64 --target android-21 --threads 6 --wheel
python3.8 makepanda/makepanda.py --everything --outputdir built-droid-x86 --arch x86 --target android-19 --threads 6 --wheel python3.13 makepanda/makepanda.py --everything --outputdir built-droid-x86 --arch x86 --target android-21 --threads 6 --wheel
``` ```
It is now possible to use the generated wheels with `build_apps`, as explained It is now possible to use the generated wheels with `build_apps`, as explained
@ -229,8 +228,8 @@ information as possible to help the developers track down the issue, such as
your version of Panda3D, operating system, architecture, and any code and your version of Panda3D, operating system, architecture, and any code and
models that are necessary for the developers to reproduce the issue. models that are necessary for the developers to reproduce the issue.
If you're not sure whether you've encountered a bug, feel free to ask about If you're unsure whether you've encountered a bug, feel free to ask in the [forums](https://discourse.panda3d.org) or the [IRC channel](https://web.libera.chat/#panda3d) before opening an issue.
it in the forums or the IRC channel first.
Supporting the Project Supporting the Project
====================== ======================
@ -239,9 +238,12 @@ If you would like to support the project financially, visit
[our campaign on OpenCollective](https://opencollective.com/panda3d). Your [our campaign on OpenCollective](https://opencollective.com/panda3d). Your
contributions help us accelerate the development of Panda3D. contributions help us accelerate the development of Panda3D.
For the list of backers, see the [BACKERS.md](BACKERS.md) file or visit the For the complete list of backers, see the [BACKERS.md](BACKERS.md) file or
[Sponsors page](https://www.panda3d.org/sponsors) on our web site. Thank you visit the [Sponsors page](https://www.panda3d.org/sponsors) on our web site.
to everyone who has donated! Thank you to everyone who has donated!
[<img src="https://www.panda3d.org/wp-content/uploads/2024/08/Route4MeLogo1185x300-2-1-1024x259.png" alt="Route4Me" height="48">](https://route4me.com/)
[<img src="https://www.panda3d.org/wp-content/uploads/2026/01/black-logo-1-e1768931551108.png" alt="TestMu AI" height="48">](https://www.testmu.ai/?utm_source=panda3d&utm_medium=sponsor)
<a href="https://opencollective.com/panda3d" target="_blank"> <a href="https://opencollective.com/panda3d" target="_blank">
<img src="https://opencollective.com/panda3d/contribute/button@2x.png?color=blue" width=300 /> <img src="https://opencollective.com/panda3d/contribute/button@2x.png?color=blue" width=300 />

View File

@ -283,7 +283,7 @@ endfunction(interrogate_sources)
# #
# Function: add_python_module(module [lib1 [lib2 ...]] [LINK lib1 ...] # Function: add_python_module(module [lib1 [lib2 ...]] [LINK lib1 ...]
# [IMPORT mod1 ...]) # [IMPORT mod1 ...] [INIT func1 ...])
# Uses interrogate to create a Python module. If the LINK keyword is specified, # Uses interrogate to create a Python module. If the LINK keyword is specified,
# the Python module is linked against the specified libraries instead of those # the Python module is linked against the specified libraries instead of those
# listed before. The IMPORT keyword makes the output module import another # listed before. The IMPORT keyword makes the output module import another
@ -305,7 +305,7 @@ function(add_python_module module)
set(keyword) set(keyword)
foreach(arg ${ARGN}) foreach(arg ${ARGN})
if(arg STREQUAL "LINK" OR arg STREQUAL "IMPORT" OR arg STREQUAL "COMPONENT") if(arg STREQUAL "LINK" OR arg STREQUAL "IMPORT" OR arg STREQUAL "INIT" OR arg STREQUAL "COMPONENT")
set(keyword "${arg}") set(keyword "${arg}")
elseif(keyword STREQUAL "LINK") elseif(keyword STREQUAL "LINK")
@ -316,6 +316,10 @@ function(add_python_module module)
list(APPEND import_flags "-import" "${arg}") list(APPEND import_flags "-import" "${arg}")
set(keyword) set(keyword)
elseif(keyword STREQUAL "INIT")
list(APPEND import_flags "-init" "${arg}")
set(keyword)
elseif(keyword STREQUAL "COMPONENT") elseif(keyword STREQUAL "COMPONENT")
set(component "${arg}") set(component "${arg}")
set(keyword) set(keyword)

View File

@ -1,6 +1,7 @@
"""PANDA3D Particle Panel""" """PANDA3D Particle Panel"""
# Import Tkinter, Pmw, and the floater code from this directory tree. # Import Tkinter, Pmw, and the floater code from this directory tree.
from panda3d.core import ConfigVariableSearchPath, Point2, Point3, Vec3, Vec4
from direct.tkwidgets.AppShell import AppShell from direct.tkwidgets.AppShell import AppShell
import os, Pmw import os, Pmw
from direct.tkwidgets.Dial import AngleDial from direct.tkwidgets.Dial import AngleDial
@ -17,6 +18,10 @@ from tkinter.filedialog import *
from tkinter.simpledialog import askstring from tkinter.simpledialog import askstring
particlePath = ConfigVariableSearchPath("particle-path",
"The directories to search for particle files to be loaded.")
class ParticlePanel(AppShell): class ParticlePanel(AppShell):
# Override class variables # Override class variables
appname = 'Particle Panel' appname = 'Particle Panel'
@ -1041,7 +1046,7 @@ class ParticlePanel(AppShell):
def loadParticleEffectFromFile(self): def loadParticleEffectFromFile(self):
# Find path to particle directory # Find path to particle directory
pPath = getParticlePath() pPath = particlePath
if pPath.getNumDirectories() > 0: if pPath.getNumDirectories() > 0:
if repr(pPath.getDirectory(0)) == '.': if repr(pPath.getDirectory(0)) == '.':
path = '.' path = '.'
@ -1070,7 +1075,7 @@ class ParticlePanel(AppShell):
def saveParticleEffectToFile(self): def saveParticleEffectToFile(self):
# Find path to particle directory # Find path to particle directory
pPath = getParticlePath() pPath = particlePath
if pPath.getNumDirectories() > 0: if pPath.getNumDirectories() > 0:
if repr(pPath.getDirectory(0)) == '.': if repr(pPath.getDirectory(0)) == '.':
path = '.' path = '.'

View File

@ -113,7 +113,7 @@ receive_update(PyObject *distobj, DatagramIterator &di) const {
int field_id = packer.raw_unpack_uint16(); int field_id = packer.raw_unpack_uint16();
DCField *field = _this->get_field_by_index(field_id); DCField *field = _this->get_field_by_index(field_id);
if (field == nullptr) { if (field == nullptr) {
ostringstream strm; std::ostringstream strm;
strm strm
<< "Received update for field " << field_id << ", not in class " << "Received update for field " << field_id << ", not in class "
<< _this->get_name(); << _this->get_name();

View File

@ -42,25 +42,25 @@ class DirectNotify:
""" """
return list(self.__categories.keys()) return list(self.__categories.keys())
def getCategory(self, categoryName: str) -> Notifier.Notifier | None: def getCategory(self, name: str) -> Notifier.Notifier | None:
"""getCategory(self, string) """getCategory(self, string)
Return the category with given name if present, None otherwise Return the category with given name if present, None otherwise
""" """
return self.__categories.get(categoryName, None) return self.__categories.get(name, None)
def newCategory(self, categoryName: str, logger: Logger.Logger | None = None) -> Notifier.Notifier: def newCategory(self, name: str, logger: Logger.Logger | None = None) -> Notifier.Notifier:
"""newCategory(self, string) """newCategory(self, string)
Make a new notify category named categoryName. Return new category Make a new notify category named name. Return new category
if no such category exists, else return existing category if no such category exists, else return existing category
""" """
if categoryName not in self.__categories: if name not in self.__categories:
self.__categories[categoryName] = Notifier.Notifier(categoryName, logger) self.__categories[name] = Notifier.Notifier(name, logger)
self.setDconfigLevel(categoryName) self.setDconfigLevel(name)
notifier = self.getCategory(categoryName) notifier = self.getCategory(name)
assert notifier is not None assert notifier is not None
return notifier return notifier
def setDconfigLevel(self, categoryName: str) -> None: def setDconfigLevel(self, name: str) -> None:
""" """
Check to see if this category has a dconfig variable Check to see if this category has a dconfig variable
to set the notify severity and then set that level. You cannot to set the notify severity and then set that level. You cannot
@ -71,7 +71,7 @@ class DirectNotify:
# we're running before ShowBase has finished initializing # we're running before ShowBase has finished initializing
from panda3d.core import ConfigVariableString from panda3d.core import ConfigVariableString
dconfigParam = ("notify-level-" + categoryName) dconfigParam = ("notify-level-" + name)
cvar = ConfigVariableString(dconfigParam, "") cvar = ConfigVariableString(dconfigParam, "")
level = cvar.getValue() level = cvar.getValue()
@ -82,11 +82,11 @@ class DirectNotify:
if not level: if not level:
level = 'error' level = 'error'
category = self.getCategory(categoryName) category = self.getCategory(name)
assert category is not None, f'failed to find category: {categoryName!r}' assert category is not None, f'failed to find category: {name!r}'
# Note - this print statement is making it difficult to # Note - this print statement is making it difficult to
# achieve "no output unless there's an error" operation - Josh # achieve "no output unless there's an error" operation - Josh
# print ("Setting DirectNotify category: " + categoryName + # print ("Setting DirectNotify category: " + name +
# " to severity: " + level) # " to severity: " + level)
if level == "error": if level == "error":
category.setWarning(False) category.setWarning(False)
@ -106,7 +106,7 @@ class DirectNotify:
category.setDebug(True) category.setDebug(True)
else: else:
print("DirectNotify: unknown notify level: " + str(level) print("DirectNotify: unknown notify level: " + str(level)
+ " for category: " + str(categoryName)) + " for category: " + str(name))
def setDconfigLevels(self) -> None: def setDconfigLevels(self) -> None:
for categoryName in self.getCategories(): for categoryName in self.getCategories():
@ -129,3 +129,12 @@ class DirectNotify:
def giveNotify(self, cls) -> None: def giveNotify(self, cls) -> None:
cls.notify = self.newCategory(cls.__name__) cls.notify = self.newCategory(cls.__name__)
get_categories = getCategories
get_category = getCategory
new_category = newCategory
set_dconfig_level = setDconfigLevel
set_dconfig_levels = setDconfigLevels
set_verbose = setVerbose
popup_controls = popupControls
give_notify = giveNotify

View File

@ -79,8 +79,8 @@ class LineNodePath(NodePath):
def getVertex(self, index): def getVertex(self, index):
return self.lineSegs.getVertex(index) return self.lineSegs.getVertex(index)
def getVertexColor(self): def getVertexColor(self, index):
return self.lineSegs.getVertexColor() return self.lineSegs.getVertexColor(index)
def drawArrow(self, sv, ev, arrowAngle, arrowLength): def drawArrow(self, sv, ev, arrowAngle, arrowLength):
""" """

View File

@ -33,7 +33,7 @@ class DirectGrid(NodePath, DirectObject):
self.centerLines = LineNodePath(self.lines) self.centerLines = LineNodePath(self.lines)
self.centerLines.lineNode.setName('centerLines') self.centerLines.lineNode.setName('centerLines')
self.centerLines.setColor(VBase4(1, 0, 0, 0)) self.centerLines.setColor(VBase4(1, 0, 0, 1))
self.centerLines.setThickness(3) self.centerLines.setThickness(3)
# Small marker to hilight snap-to-grid point # Small marker to hilight snap-to-grid point

View File

@ -1,4 +1,4 @@
from panda3d.core import VBase4 from panda3d.core import NodePath, VBase4
from direct.task.Task import Task from direct.task.Task import Task
from direct.task.TaskManagerGlobal import taskMgr from direct.task.TaskManagerGlobal import taskMgr
@ -55,7 +55,7 @@ def lerpBackgroundColor(r, g, b, duration):
# Set direct drawing style for an object # Set direct drawing style for an object
# Never light object or draw in wireframe # Never light object or draw in wireframe
def useDirectRenderStyle(nodePath, priority = 0): def useDirectRenderStyle(nodePath: NodePath, priority: int = 0) -> None:
""" """
Function to force a node path to use direct render style: Function to force a node path to use direct render style:
no lighting, and no wireframe no lighting, and no wireframe

View File

@ -88,6 +88,7 @@ defaultHiddenImports = {
'numpy.core._dtype_ctypes', 'numpy.core._dtype_ctypes',
'numpy.core._methods', 'numpy.core._methods',
], ],
'panda3d.core': ['enum'],
'pandas.compat': ['lzma', 'cmath'], 'pandas.compat': ['lzma', 'cmath'],
'pandas._libs.tslibs.conversion': ['pandas._libs.tslibs.base'], 'pandas._libs.tslibs.conversion': ['pandas._libs.tslibs.base'],
'plyer': ['plyer.platforms'], 'plyer': ['plyer.platforms'],
@ -101,6 +102,7 @@ defaultHiddenImports = {
'scipy.stats._stats': ['scipy.special.cython_special'], 'scipy.stats._stats': ['scipy.special.cython_special'],
'setuptools.monkey': ['setuptools.msvc'], 'setuptools.monkey': ['setuptools.msvc'],
'shapely._geometry_helpers': ['shapely._geos'], 'shapely._geometry_helpers': ['shapely._geos'],
'jnius': ['jnius_config'],
} }
@ -113,7 +115,7 @@ ignoreImports = {
'toml.encoder': ['numpy'], 'toml.encoder': ['numpy'],
'py._builtin': ['__builtin__'], 'py._builtin': ['__builtin__'],
'site': ['android_log'], 'site': ['android_support'],
} }
if sys.version_info >= (3, 8): if sys.version_info >= (3, 8):
@ -138,12 +140,23 @@ def getline(filename, lineno, module_globals=None):
return '' return ''
def clearcache(): def clearcache():
global cache cache.clear()
cache = {}
def getlines(filename, module_globals=None): def getlines(filename, module_globals=None):
return [] return []
def _getline_from_code(filename, lineno):
return ''
def _make_key(code):
return (code.co_filename, code.co_qualname, code.co_firstlineno)
def _getlines_from_code(code):
return []
def _source_unavailable(filename):
return True
def checkcache(filename=None): def checkcache(filename=None):
pass pass
@ -1236,8 +1249,12 @@ class Freezer:
pass pass
# Check if any new modules we found have "hidden" imports # Check if any new modules we found have "hidden" imports
for origName in list(self.mf.modules.keys()): checkHiddenImports = set(self.mf.modules.keys())
while checkHiddenImports:
origName = next(iter(checkHiddenImports))
checkHiddenImports.discard(origName)
hidden = self.hiddenImports.get(origName, []) hidden = self.hiddenImports.get(origName, [])
preModules = frozenset(self.mf.modules.keys())
for modname in hidden: for modname in hidden:
if modname.endswith('.*'): if modname.endswith('.*'):
mdefs = self._gatherSubmodules(modname, implicit = True) mdefs = self._gatherSubmodules(modname, implicit = True)
@ -1251,6 +1268,9 @@ class Freezer:
self.__loadModule(self.ModuleDef(modname, implicit = True)) self.__loadModule(self.ModuleDef(modname, implicit = True))
except ImportError: except ImportError:
pass pass
addedModules = set(self.mf.modules.keys())
addedModules -= preModules
checkHiddenImports |= addedModules
# Special case for sysconfig, which depends on a platform-specific # Special case for sysconfig, which depends on a platform-specific
# sysconfigdata module on POSIX systems. # sysconfigdata module on POSIX systems.
@ -1834,7 +1854,8 @@ class Freezer:
return target return target
def generateRuntimeFromStub(self, target, stub_file, use_console, fields={}, def generateRuntimeFromStub(self, target, stub_file, use_console, fields={},
log_append=False, log_filename_strftime=False): log_append=False, log_filename_strftime=False,
blob_path=None):
self.__replacePaths() self.__replacePaths()
# We must have a __main__ module to make an exe file. # We must have a __main__ module to make an exe file.
@ -1969,6 +1990,9 @@ class Freezer:
elif self.platform.endswith('_aarch64') or self.platform.endswith('_arm64'): elif self.platform.endswith('_aarch64') or self.platform.endswith('_arm64'):
# Most arm64 operating systems are configured with 16 KiB pages. # Most arm64 operating systems are configured with 16 KiB pages.
blob_align = 16384 blob_align = 16384
elif self.platform.replace('-', '_') == 'android_x86_64':
# Android nowadays requires 16 KiB pages on 64-bit Intel as well.
blob_align = 16384
else: else:
# Align to page size, so that it can be mmapped. # Align to page size, so that it can be mmapped.
blob_align = 4096 blob_align = 4096
@ -1979,29 +2003,33 @@ class Freezer:
pad = (blob_align - (blob_size & (blob_align - 1))) pad = (blob_align - (blob_size & (blob_align - 1)))
blob_size += pad blob_size += pad
# TODO: Support creating custom sections in universal binaries. if blob_path is not None:
append_blob = True # We'll be writing the blob to a separate location.
if self.platform.startswith('macosx') and len(bitnesses) == 1: blob_offset = 0
# If our deploy-stub has a __PANDA segment, we know we're meant to
# put our blob there rather than attach it to the end.
load_commands = self._parse_macho_load_commands(stub_data)
if b'__PANDA' in load_commands.keys():
append_blob = False
if self.platform.startswith("macosx") and not append_blob:
# Take this time to shift any Mach-O structures around to fit our
# blob. We don't need to worry about aligning the offset since the
# compiler already took care of that when creating the segment.
blob_offset = self._shift_macho_structures(stub_data, load_commands, blob_size)
else: else:
# Add padding before the blob if necessary. # TODO: Support creating custom sections in universal binaries.
blob_offset = len(stub_data) append_blob = True
if (blob_offset & (blob_align - 1)) != 0: if self.platform.startswith('macosx') and len(bitnesses) == 1:
pad = (blob_align - (blob_offset & (blob_align - 1))) # If our deploy-stub has a __PANDA segment, we know we're meant to
stub_data += (b'\0' * pad) # put our blob there rather than attach it to the end.
blob_offset += pad load_commands = self._parse_macho_load_commands(stub_data)
assert (blob_offset % blob_align) == 0 if b'__PANDA' in load_commands.keys():
assert blob_offset == len(stub_data) append_blob = False
if self.platform.startswith("macosx") and not append_blob:
# Take this time to shift any Mach-O structures around to fit our
# blob. We don't need to worry about aligning the offset since the
# compiler already took care of that when creating the segment.
blob_offset = self._shift_macho_structures(stub_data, load_commands, blob_size)
else:
# Add padding before the blob if necessary.
blob_offset = len(stub_data)
if (blob_offset & (blob_align - 1)) != 0:
pad = (blob_align - (blob_offset & (blob_align - 1)))
stub_data += (b'\0' * pad)
blob_offset += pad
assert (blob_offset % blob_align) == 0
assert blob_offset == len(stub_data)
# Calculate the offsets for the variables. These are pointers, # Calculate the offsets for the variables. These are pointers,
# relative to the beginning of the blob. # relative to the beginning of the blob.
@ -2087,7 +2115,9 @@ class Freezer:
blob += struct.pack('<Q', blob_offset) blob += struct.pack('<Q', blob_offset)
with open(target, 'wb') as f: with open(target, 'wb') as f:
if append_blob: if blob_path is not None:
f.write(stub_data)
elif append_blob:
f.write(stub_data) f.write(stub_data)
assert f.tell() == blob_offset assert f.tell() == blob_offset
f.write(blob) f.write(blob)
@ -2095,6 +2125,10 @@ class Freezer:
stub_data[blob_offset:blob_offset + blob_size] = blob stub_data[blob_offset:blob_offset + blob_size] = blob
f.write(stub_data) f.write(stub_data)
if blob_path is not None:
with open(blob_path, 'wb') as f:
f.write(blob)
os.chmod(target, 0o755) os.chmod(target, 0o755)
return target return target

View File

@ -3,7 +3,7 @@
import xml.etree.ElementTree as ET import xml.etree.ElementTree as ET
from ._proto.targeting_pb2 import Abi from ._proto.targeting_pb2 import Abi
from ._proto.config_pb2 import BundleConfig # pylint: disable=unused-import from ._proto.config_pb2 import BundleConfig, UncompressNativeLibraries # pylint: disable=unused-import
from ._proto.files_pb2 import NativeLibraries # pylint: disable=unused-import from ._proto.files_pb2 import NativeLibraries # pylint: disable=unused-import
from ._proto.Resources_pb2 import ResourceTable # pylint: disable=unused-import from ._proto.Resources_pb2 import ResourceTable # pylint: disable=unused-import
from ._proto.Resources_pb2 import XmlNode from ._proto.Resources_pb2 import XmlNode
@ -174,6 +174,7 @@ ANDROID_ATTRIBUTES = {
'debuggable': bool_resource(0x0101000f), 'debuggable': bool_resource(0x0101000f),
'documentLaunchMode': enum_resource(0x1010445, "none", "intoExisting", "always", "never"), 'documentLaunchMode': enum_resource(0x1010445, "none", "intoExisting", "always", "never"),
'enabled': bool_resource(0x101000e), 'enabled': bool_resource(0x101000e),
'enableOnBackInvokedCallback': bool_resource(0x0101066c),
'excludeFromRecents': bool_resource(0x1010017), 'excludeFromRecents': bool_resource(0x1010017),
'exported': bool_resource(0x1010010), 'exported': bool_resource(0x1010010),
'extractNativeLibs': bool_resource(0x10104ea), 'extractNativeLibs': bool_resource(0x10104ea),
@ -195,8 +196,10 @@ ANDROID_ATTRIBUTES = {
'multiprocess': bool_resource(0x1010013), 'multiprocess': bool_resource(0x1010013),
'name': str_resource(0x1010003), 'name': str_resource(0x1010003),
'noHistory': bool_resource(0x101022d), 'noHistory': bool_resource(0x101022d),
'pageSizeCompat': bool_resource(0x010106ab),
'pathPattern': str_resource(0x101002c), 'pathPattern': str_resource(0x101002c),
'preferMinimalPostProcessing': bool_resource(0x101060c), 'preferMinimalPostProcessing': bool_resource(0x101060c),
'resource': ref_resource(0x01010025),
'required': bool_resource(0x101028e), 'required': bool_resource(0x101028e),
'resizeableActivity': bool_resource(0x10104f6), 'resizeableActivity': bool_resource(0x10104f6),
'scheme': str_resource(0x1010027), 'scheme': str_resource(0x1010027),
@ -220,6 +223,7 @@ class AndroidManifest:
self.root = XmlNode() self.root = XmlNode()
self.resource_types = [] self.resource_types = []
self.resources = {} self.resources = {}
self.extract_native_libs = None
def parse_xml(self, data): def parse_xml(self, data):
parser = ET.XMLParser(target=self) parser = ET.XMLParser(target=self)
@ -240,6 +244,15 @@ class AndroidManifest:
element = node.element element = node.element
element.name = tag element.name = tag
if tag == 'application':
value = attribs.get('{http://schemas.android.com/apk/res/android}extractNativeLibs')
if value == 'false':
self.extract_native_libs = False
elif value == 'true':
self.extract_native_libs = True
else:
print(f'Warning: invalid value for android:extractNativeLibs: {value}')
self._stack.append(element) self._stack.append(element)
for key, value in attribs.items(): for key, value in attribs.items():

View File

@ -1,11 +1,22 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Generated by the protocol buffer compiler. DO NOT EDIT! # Generated by the protocol buffer compiler. DO NOT EDIT!
# NO CHECKED-IN PROTOBUF GENCODE
# source: frameworks/base/tools/aapt2/Configuration.proto # source: frameworks/base/tools/aapt2/Configuration.proto
# Protobuf Python Version: 6.33.0
"""Generated protocol buffer code.""" """Generated protocol buffer code."""
from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor as _descriptor
from google.protobuf import message as _message from google.protobuf import descriptor_pool as _descriptor_pool
from google.protobuf import reflection as _reflection from google.protobuf import runtime_version as _runtime_version
from google.protobuf import symbol_database as _symbol_database from google.protobuf import symbol_database as _symbol_database
from google.protobuf.internal import builder as _builder
_runtime_version.ValidateProtobufRuntimeVersion(
_runtime_version.Domain.PUBLIC,
6,
33,
0,
'',
'frameworks/base/tools/aapt2/Configuration.proto'
)
# @@protoc_insertion_point(imports) # @@protoc_insertion_point(imports)
_sym_db = _symbol_database.Default() _sym_db = _symbol_database.Default()
@ -13,754 +24,44 @@ _sym_db = _symbol_database.Default()
DESCRIPTOR = _descriptor.FileDescriptor( DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n/frameworks/base/tools/aapt2/Configuration.proto\x12\x07\x61\x61pt.pb\"\x97\x16\n\rConfiguration\x12\x0b\n\x03mcc\x18\x01 \x01(\r\x12\x0b\n\x03mnc\x18\x02 \x01(\r\x12\x0e\n\x06locale\x18\x03 \x01(\t\x12@\n\x10layout_direction\x18\x04 \x01(\x0e\x32&.aapt.pb.Configuration.LayoutDirection\x12\x14\n\x0cscreen_width\x18\x05 \x01(\r\x12\x15\n\rscreen_height\x18\x06 \x01(\r\x12\x17\n\x0fscreen_width_dp\x18\x07 \x01(\r\x12\x18\n\x10screen_height_dp\x18\x08 \x01(\r\x12 \n\x18smallest_screen_width_dp\x18\t \x01(\r\x12\x43\n\x12screen_layout_size\x18\n \x01(\x0e\x32\'.aapt.pb.Configuration.ScreenLayoutSize\x12\x43\n\x12screen_layout_long\x18\x0b \x01(\x0e\x32\'.aapt.pb.Configuration.ScreenLayoutLong\x12\x38\n\x0cscreen_round\x18\x0c \x01(\x0e\x32\".aapt.pb.Configuration.ScreenRound\x12?\n\x10wide_color_gamut\x18\r \x01(\x0e\x32%.aapt.pb.Configuration.WideColorGamut\x12\'\n\x03hdr\x18\x0e \x01(\x0e\x32\x1a.aapt.pb.Configuration.Hdr\x12\x37\n\x0borientation\x18\x0f \x01(\x0e\x32\".aapt.pb.Configuration.Orientation\x12\x37\n\x0cui_mode_type\x18\x10 \x01(\x0e\x32!.aapt.pb.Configuration.UiModeType\x12\x39\n\rui_mode_night\x18\x11 \x01(\x0e\x32\".aapt.pb.Configuration.UiModeNight\x12\x0f\n\x07\x64\x65nsity\x18\x12 \x01(\r\x12\x37\n\x0btouchscreen\x18\x13 \x01(\x0e\x32\".aapt.pb.Configuration.Touchscreen\x12\x36\n\x0bkeys_hidden\x18\x14 \x01(\x0e\x32!.aapt.pb.Configuration.KeysHidden\x12\x31\n\x08keyboard\x18\x15 \x01(\x0e\x32\x1f.aapt.pb.Configuration.Keyboard\x12\x34\n\nnav_hidden\x18\x16 \x01(\x0e\x32 .aapt.pb.Configuration.NavHidden\x12\x35\n\nnavigation\x18\x17 \x01(\x0e\x32!.aapt.pb.Configuration.Navigation\x12\x13\n\x0bsdk_version\x18\x18 \x01(\r\x12\x44\n\x12grammatical_gender\x18\x1a \x01(\x0e\x32(.aapt.pb.Configuration.GrammaticalGender\x12\x0f\n\x07product\x18\x19 \x01(\t\"a\n\x0fLayoutDirection\x12\x1a\n\x16LAYOUT_DIRECTION_UNSET\x10\x00\x12\x18\n\x14LAYOUT_DIRECTION_LTR\x10\x01\x12\x18\n\x14LAYOUT_DIRECTION_RTL\x10\x02\"\xaa\x01\n\x10ScreenLayoutSize\x12\x1c\n\x18SCREEN_LAYOUT_SIZE_UNSET\x10\x00\x12\x1c\n\x18SCREEN_LAYOUT_SIZE_SMALL\x10\x01\x12\x1d\n\x19SCREEN_LAYOUT_SIZE_NORMAL\x10\x02\x12\x1c\n\x18SCREEN_LAYOUT_SIZE_LARGE\x10\x03\x12\x1d\n\x19SCREEN_LAYOUT_SIZE_XLARGE\x10\x04\"m\n\x10ScreenLayoutLong\x12\x1c\n\x18SCREEN_LAYOUT_LONG_UNSET\x10\x00\x12\x1b\n\x17SCREEN_LAYOUT_LONG_LONG\x10\x01\x12\x1e\n\x1aSCREEN_LAYOUT_LONG_NOTLONG\x10\x02\"X\n\x0bScreenRound\x12\x16\n\x12SCREEN_ROUND_UNSET\x10\x00\x12\x16\n\x12SCREEN_ROUND_ROUND\x10\x01\x12\x19\n\x15SCREEN_ROUND_NOTROUND\x10\x02\"h\n\x0eWideColorGamut\x12\x1a\n\x16WIDE_COLOR_GAMUT_UNSET\x10\x00\x12\x1b\n\x17WIDE_COLOR_GAMUT_WIDECG\x10\x01\x12\x1d\n\x19WIDE_COLOR_GAMUT_NOWIDECG\x10\x02\"3\n\x03Hdr\x12\r\n\tHDR_UNSET\x10\x00\x12\x0e\n\nHDR_HIGHDR\x10\x01\x12\r\n\tHDR_LOWDR\x10\x02\"h\n\x0bOrientation\x12\x15\n\x11ORIENTATION_UNSET\x10\x00\x12\x14\n\x10ORIENTATION_PORT\x10\x01\x12\x14\n\x10ORIENTATION_LAND\x10\x02\x12\x16\n\x12ORIENTATION_SQUARE\x10\x03\"\xd7\x01\n\nUiModeType\x12\x16\n\x12UI_MODE_TYPE_UNSET\x10\x00\x12\x17\n\x13UI_MODE_TYPE_NORMAL\x10\x01\x12\x15\n\x11UI_MODE_TYPE_DESK\x10\x02\x12\x14\n\x10UI_MODE_TYPE_CAR\x10\x03\x12\x1b\n\x17UI_MODE_TYPE_TELEVISION\x10\x04\x12\x1a\n\x16UI_MODE_TYPE_APPLIANCE\x10\x05\x12\x16\n\x12UI_MODE_TYPE_WATCH\x10\x06\x12\x1a\n\x16UI_MODE_TYPE_VRHEADSET\x10\x07\"[\n\x0bUiModeNight\x12\x17\n\x13UI_MODE_NIGHT_UNSET\x10\x00\x12\x17\n\x13UI_MODE_NIGHT_NIGHT\x10\x01\x12\x1a\n\x16UI_MODE_NIGHT_NOTNIGHT\x10\x02\"m\n\x0bTouchscreen\x12\x15\n\x11TOUCHSCREEN_UNSET\x10\x00\x12\x17\n\x13TOUCHSCREEN_NOTOUCH\x10\x01\x12\x16\n\x12TOUCHSCREEN_STYLUS\x10\x02\x12\x16\n\x12TOUCHSCREEN_FINGER\x10\x03\"v\n\nKeysHidden\x12\x15\n\x11KEYS_HIDDEN_UNSET\x10\x00\x12\x1b\n\x17KEYS_HIDDEN_KEYSEXPOSED\x10\x01\x12\x1a\n\x16KEYS_HIDDEN_KEYSHIDDEN\x10\x02\x12\x18\n\x14KEYS_HIDDEN_KEYSSOFT\x10\x03\"`\n\x08Keyboard\x12\x12\n\x0eKEYBOARD_UNSET\x10\x00\x12\x13\n\x0fKEYBOARD_NOKEYS\x10\x01\x12\x13\n\x0fKEYBOARD_QWERTY\x10\x02\x12\x16\n\x12KEYBOARD_TWELVEKEY\x10\x03\"V\n\tNavHidden\x12\x14\n\x10NAV_HIDDEN_UNSET\x10\x00\x12\x19\n\x15NAV_HIDDEN_NAVEXPOSED\x10\x01\x12\x18\n\x14NAV_HIDDEN_NAVHIDDEN\x10\x02\"}\n\nNavigation\x12\x14\n\x10NAVIGATION_UNSET\x10\x00\x12\x14\n\x10NAVIGATION_NONAV\x10\x01\x12\x13\n\x0fNAVIGATION_DPAD\x10\x02\x12\x18\n\x14NAVIGATION_TRACKBALL\x10\x03\x12\x14\n\x10NAVIGATION_WHEEL\x10\x04\"v\n\x11GrammaticalGender\x12\x14\n\x10GRAM_GENDER_USET\x10\x00\x12\x16\n\x12GRAM_GENDER_NEUTER\x10\x01\x12\x18\n\x14GRAM_GENDER_FEMININE\x10\x02\x12\x19\n\x15GRAM_GENDER_MASCULINE\x10\x03\x42\x12\n\x10\x63om.android.aaptb\x06proto3')
name='frameworks/base/tools/aapt2/Configuration.proto',
package='aapt.pb',
syntax='proto3',
serialized_options=b'\n\020com.android.aapt',
create_key=_descriptor._internal_create_key,
serialized_pb=b'\n/frameworks/base/tools/aapt2/Configuration.proto\x12\x07\x61\x61pt.pb\"\xd9\x14\n\rConfiguration\x12\x0b\n\x03mcc\x18\x01 \x01(\r\x12\x0b\n\x03mnc\x18\x02 \x01(\r\x12\x0e\n\x06locale\x18\x03 \x01(\t\x12@\n\x10layout_direction\x18\x04 \x01(\x0e\x32&.aapt.pb.Configuration.LayoutDirection\x12\x14\n\x0cscreen_width\x18\x05 \x01(\r\x12\x15\n\rscreen_height\x18\x06 \x01(\r\x12\x17\n\x0fscreen_width_dp\x18\x07 \x01(\r\x12\x18\n\x10screen_height_dp\x18\x08 \x01(\r\x12 \n\x18smallest_screen_width_dp\x18\t \x01(\r\x12\x43\n\x12screen_layout_size\x18\n \x01(\x0e\x32\'.aapt.pb.Configuration.ScreenLayoutSize\x12\x43\n\x12screen_layout_long\x18\x0b \x01(\x0e\x32\'.aapt.pb.Configuration.ScreenLayoutLong\x12\x38\n\x0cscreen_round\x18\x0c \x01(\x0e\x32\".aapt.pb.Configuration.ScreenRound\x12?\n\x10wide_color_gamut\x18\r \x01(\x0e\x32%.aapt.pb.Configuration.WideColorGamut\x12\'\n\x03hdr\x18\x0e \x01(\x0e\x32\x1a.aapt.pb.Configuration.Hdr\x12\x37\n\x0borientation\x18\x0f \x01(\x0e\x32\".aapt.pb.Configuration.Orientation\x12\x37\n\x0cui_mode_type\x18\x10 \x01(\x0e\x32!.aapt.pb.Configuration.UiModeType\x12\x39\n\rui_mode_night\x18\x11 \x01(\x0e\x32\".aapt.pb.Configuration.UiModeNight\x12\x0f\n\x07\x64\x65nsity\x18\x12 \x01(\r\x12\x37\n\x0btouchscreen\x18\x13 \x01(\x0e\x32\".aapt.pb.Configuration.Touchscreen\x12\x36\n\x0bkeys_hidden\x18\x14 \x01(\x0e\x32!.aapt.pb.Configuration.KeysHidden\x12\x31\n\x08keyboard\x18\x15 \x01(\x0e\x32\x1f.aapt.pb.Configuration.Keyboard\x12\x34\n\nnav_hidden\x18\x16 \x01(\x0e\x32 .aapt.pb.Configuration.NavHidden\x12\x35\n\nnavigation\x18\x17 \x01(\x0e\x32!.aapt.pb.Configuration.Navigation\x12\x13\n\x0bsdk_version\x18\x18 \x01(\r\x12\x0f\n\x07product\x18\x19 \x01(\t\"a\n\x0fLayoutDirection\x12\x1a\n\x16LAYOUT_DIRECTION_UNSET\x10\x00\x12\x18\n\x14LAYOUT_DIRECTION_LTR\x10\x01\x12\x18\n\x14LAYOUT_DIRECTION_RTL\x10\x02\"\xaa\x01\n\x10ScreenLayoutSize\x12\x1c\n\x18SCREEN_LAYOUT_SIZE_UNSET\x10\x00\x12\x1c\n\x18SCREEN_LAYOUT_SIZE_SMALL\x10\x01\x12\x1d\n\x19SCREEN_LAYOUT_SIZE_NORMAL\x10\x02\x12\x1c\n\x18SCREEN_LAYOUT_SIZE_LARGE\x10\x03\x12\x1d\n\x19SCREEN_LAYOUT_SIZE_XLARGE\x10\x04\"m\n\x10ScreenLayoutLong\x12\x1c\n\x18SCREEN_LAYOUT_LONG_UNSET\x10\x00\x12\x1b\n\x17SCREEN_LAYOUT_LONG_LONG\x10\x01\x12\x1e\n\x1aSCREEN_LAYOUT_LONG_NOTLONG\x10\x02\"X\n\x0bScreenRound\x12\x16\n\x12SCREEN_ROUND_UNSET\x10\x00\x12\x16\n\x12SCREEN_ROUND_ROUND\x10\x01\x12\x19\n\x15SCREEN_ROUND_NOTROUND\x10\x02\"h\n\x0eWideColorGamut\x12\x1a\n\x16WIDE_COLOR_GAMUT_UNSET\x10\x00\x12\x1b\n\x17WIDE_COLOR_GAMUT_WIDECG\x10\x01\x12\x1d\n\x19WIDE_COLOR_GAMUT_NOWIDECG\x10\x02\"3\n\x03Hdr\x12\r\n\tHDR_UNSET\x10\x00\x12\x0e\n\nHDR_HIGHDR\x10\x01\x12\r\n\tHDR_LOWDR\x10\x02\"h\n\x0bOrientation\x12\x15\n\x11ORIENTATION_UNSET\x10\x00\x12\x14\n\x10ORIENTATION_PORT\x10\x01\x12\x14\n\x10ORIENTATION_LAND\x10\x02\x12\x16\n\x12ORIENTATION_SQUARE\x10\x03\"\xd7\x01\n\nUiModeType\x12\x16\n\x12UI_MODE_TYPE_UNSET\x10\x00\x12\x17\n\x13UI_MODE_TYPE_NORMAL\x10\x01\x12\x15\n\x11UI_MODE_TYPE_DESK\x10\x02\x12\x14\n\x10UI_MODE_TYPE_CAR\x10\x03\x12\x1b\n\x17UI_MODE_TYPE_TELEVISION\x10\x04\x12\x1a\n\x16UI_MODE_TYPE_APPLIANCE\x10\x05\x12\x16\n\x12UI_MODE_TYPE_WATCH\x10\x06\x12\x1a\n\x16UI_MODE_TYPE_VRHEADSET\x10\x07\"[\n\x0bUiModeNight\x12\x17\n\x13UI_MODE_NIGHT_UNSET\x10\x00\x12\x17\n\x13UI_MODE_NIGHT_NIGHT\x10\x01\x12\x1a\n\x16UI_MODE_NIGHT_NOTNIGHT\x10\x02\"m\n\x0bTouchscreen\x12\x15\n\x11TOUCHSCREEN_UNSET\x10\x00\x12\x17\n\x13TOUCHSCREEN_NOTOUCH\x10\x01\x12\x16\n\x12TOUCHSCREEN_STYLUS\x10\x02\x12\x16\n\x12TOUCHSCREEN_FINGER\x10\x03\"v\n\nKeysHidden\x12\x15\n\x11KEYS_HIDDEN_UNSET\x10\x00\x12\x1b\n\x17KEYS_HIDDEN_KEYSEXPOSED\x10\x01\x12\x1a\n\x16KEYS_HIDDEN_KEYSHIDDEN\x10\x02\x12\x18\n\x14KEYS_HIDDEN_KEYSSOFT\x10\x03\"`\n\x08Keyboard\x12\x12\n\x0eKEYBOARD_UNSET\x10\x00\x12\x13\n\x0fKEYBOARD_NOKEYS\x10\x01\x12\x13\n\x0fKEYBOARD_QWERTY\x10\x02\x12\x16\n\x12KEYBOARD_TWELVEKEY\x10\x03\"V\n\tNavHidden\x12\x14\n\x10NAV_HIDDEN_UNSET\x10\x00\x12\x19\n\x15NAV_HIDDEN_NAVEXPOSED\x10\x01\x12\x18\n\x14NAV_HIDDEN_NAVHIDDEN\x10\x02\"}\n\nNavigation\x12\x14\n\x10NAVIGATION_UNSET\x10\x00\x12\x14\n\x10NAVIGATION_NONAV\x10\x01\x12\x13\n\x0fNAVIGATION_DPAD\x10\x02\x12\x18\n\x14NAVIGATION_TRACKBALL\x10\x03\x12\x14\n\x10NAVIGATION_WHEEL\x10\x04\x42\x12\n\x10\x63om.android.aaptb\x06proto3'
)
_globals = globals()
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
_CONFIGURATION_LAYOUTDIRECTION = _descriptor.EnumDescriptor( _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'frameworks.base.tools.aapt2.Configuration_pb2', _globals)
name='LayoutDirection', if not _descriptor._USE_C_DESCRIPTORS:
full_name='aapt.pb.Configuration.LayoutDirection', _globals['DESCRIPTOR']._loaded_options = None
filename=None, _globals['DESCRIPTOR']._serialized_options = b'\n\020com.android.aapt'
file=DESCRIPTOR, _globals['_CONFIGURATION']._serialized_start=61
create_key=_descriptor._internal_create_key, _globals['_CONFIGURATION']._serialized_end=2900
values=[ _globals['_CONFIGURATION_LAYOUTDIRECTION']._serialized_start=1189
_descriptor.EnumValueDescriptor( _globals['_CONFIGURATION_LAYOUTDIRECTION']._serialized_end=1286
name='LAYOUT_DIRECTION_UNSET', index=0, number=0, _globals['_CONFIGURATION_SCREENLAYOUTSIZE']._serialized_start=1289
serialized_options=None, _globals['_CONFIGURATION_SCREENLAYOUTSIZE']._serialized_end=1459
type=None, _globals['_CONFIGURATION_SCREENLAYOUTLONG']._serialized_start=1461
create_key=_descriptor._internal_create_key), _globals['_CONFIGURATION_SCREENLAYOUTLONG']._serialized_end=1570
_descriptor.EnumValueDescriptor( _globals['_CONFIGURATION_SCREENROUND']._serialized_start=1572
name='LAYOUT_DIRECTION_LTR', index=1, number=1, _globals['_CONFIGURATION_SCREENROUND']._serialized_end=1660
serialized_options=None, _globals['_CONFIGURATION_WIDECOLORGAMUT']._serialized_start=1662
type=None, _globals['_CONFIGURATION_WIDECOLORGAMUT']._serialized_end=1766
create_key=_descriptor._internal_create_key), _globals['_CONFIGURATION_HDR']._serialized_start=1768
_descriptor.EnumValueDescriptor( _globals['_CONFIGURATION_HDR']._serialized_end=1819
name='LAYOUT_DIRECTION_RTL', index=2, number=2, _globals['_CONFIGURATION_ORIENTATION']._serialized_start=1821
serialized_options=None, _globals['_CONFIGURATION_ORIENTATION']._serialized_end=1925
type=None, _globals['_CONFIGURATION_UIMODETYPE']._serialized_start=1928
create_key=_descriptor._internal_create_key), _globals['_CONFIGURATION_UIMODETYPE']._serialized_end=2143
], _globals['_CONFIGURATION_UIMODENIGHT']._serialized_start=2145
containing_type=None, _globals['_CONFIGURATION_UIMODENIGHT']._serialized_end=2236
serialized_options=None, _globals['_CONFIGURATION_TOUCHSCREEN']._serialized_start=2238
serialized_start=1119, _globals['_CONFIGURATION_TOUCHSCREEN']._serialized_end=2347
serialized_end=1216, _globals['_CONFIGURATION_KEYSHIDDEN']._serialized_start=2349
) _globals['_CONFIGURATION_KEYSHIDDEN']._serialized_end=2467
_sym_db.RegisterEnumDescriptor(_CONFIGURATION_LAYOUTDIRECTION) _globals['_CONFIGURATION_KEYBOARD']._serialized_start=2469
_globals['_CONFIGURATION_KEYBOARD']._serialized_end=2565
_CONFIGURATION_SCREENLAYOUTSIZE = _descriptor.EnumDescriptor( _globals['_CONFIGURATION_NAVHIDDEN']._serialized_start=2567
name='ScreenLayoutSize', _globals['_CONFIGURATION_NAVHIDDEN']._serialized_end=2653
full_name='aapt.pb.Configuration.ScreenLayoutSize', _globals['_CONFIGURATION_NAVIGATION']._serialized_start=2655
filename=None, _globals['_CONFIGURATION_NAVIGATION']._serialized_end=2780
file=DESCRIPTOR, _globals['_CONFIGURATION_GRAMMATICALGENDER']._serialized_start=2782
create_key=_descriptor._internal_create_key, _globals['_CONFIGURATION_GRAMMATICALGENDER']._serialized_end=2900
values=[
_descriptor.EnumValueDescriptor(
name='SCREEN_LAYOUT_SIZE_UNSET', index=0, number=0,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
_descriptor.EnumValueDescriptor(
name='SCREEN_LAYOUT_SIZE_SMALL', index=1, number=1,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
_descriptor.EnumValueDescriptor(
name='SCREEN_LAYOUT_SIZE_NORMAL', index=2, number=2,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
_descriptor.EnumValueDescriptor(
name='SCREEN_LAYOUT_SIZE_LARGE', index=3, number=3,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
_descriptor.EnumValueDescriptor(
name='SCREEN_LAYOUT_SIZE_XLARGE', index=4, number=4,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
],
containing_type=None,
serialized_options=None,
serialized_start=1219,
serialized_end=1389,
)
_sym_db.RegisterEnumDescriptor(_CONFIGURATION_SCREENLAYOUTSIZE)
_CONFIGURATION_SCREENLAYOUTLONG = _descriptor.EnumDescriptor(
name='ScreenLayoutLong',
full_name='aapt.pb.Configuration.ScreenLayoutLong',
filename=None,
file=DESCRIPTOR,
create_key=_descriptor._internal_create_key,
values=[
_descriptor.EnumValueDescriptor(
name='SCREEN_LAYOUT_LONG_UNSET', index=0, number=0,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
_descriptor.EnumValueDescriptor(
name='SCREEN_LAYOUT_LONG_LONG', index=1, number=1,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
_descriptor.EnumValueDescriptor(
name='SCREEN_LAYOUT_LONG_NOTLONG', index=2, number=2,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
],
containing_type=None,
serialized_options=None,
serialized_start=1391,
serialized_end=1500,
)
_sym_db.RegisterEnumDescriptor(_CONFIGURATION_SCREENLAYOUTLONG)
_CONFIGURATION_SCREENROUND = _descriptor.EnumDescriptor(
name='ScreenRound',
full_name='aapt.pb.Configuration.ScreenRound',
filename=None,
file=DESCRIPTOR,
create_key=_descriptor._internal_create_key,
values=[
_descriptor.EnumValueDescriptor(
name='SCREEN_ROUND_UNSET', index=0, number=0,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
_descriptor.EnumValueDescriptor(
name='SCREEN_ROUND_ROUND', index=1, number=1,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
_descriptor.EnumValueDescriptor(
name='SCREEN_ROUND_NOTROUND', index=2, number=2,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
],
containing_type=None,
serialized_options=None,
serialized_start=1502,
serialized_end=1590,
)
_sym_db.RegisterEnumDescriptor(_CONFIGURATION_SCREENROUND)
_CONFIGURATION_WIDECOLORGAMUT = _descriptor.EnumDescriptor(
name='WideColorGamut',
full_name='aapt.pb.Configuration.WideColorGamut',
filename=None,
file=DESCRIPTOR,
create_key=_descriptor._internal_create_key,
values=[
_descriptor.EnumValueDescriptor(
name='WIDE_COLOR_GAMUT_UNSET', index=0, number=0,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
_descriptor.EnumValueDescriptor(
name='WIDE_COLOR_GAMUT_WIDECG', index=1, number=1,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
_descriptor.EnumValueDescriptor(
name='WIDE_COLOR_GAMUT_NOWIDECG', index=2, number=2,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
],
containing_type=None,
serialized_options=None,
serialized_start=1592,
serialized_end=1696,
)
_sym_db.RegisterEnumDescriptor(_CONFIGURATION_WIDECOLORGAMUT)
_CONFIGURATION_HDR = _descriptor.EnumDescriptor(
name='Hdr',
full_name='aapt.pb.Configuration.Hdr',
filename=None,
file=DESCRIPTOR,
create_key=_descriptor._internal_create_key,
values=[
_descriptor.EnumValueDescriptor(
name='HDR_UNSET', index=0, number=0,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
_descriptor.EnumValueDescriptor(
name='HDR_HIGHDR', index=1, number=1,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
_descriptor.EnumValueDescriptor(
name='HDR_LOWDR', index=2, number=2,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
],
containing_type=None,
serialized_options=None,
serialized_start=1698,
serialized_end=1749,
)
_sym_db.RegisterEnumDescriptor(_CONFIGURATION_HDR)
_CONFIGURATION_ORIENTATION = _descriptor.EnumDescriptor(
name='Orientation',
full_name='aapt.pb.Configuration.Orientation',
filename=None,
file=DESCRIPTOR,
create_key=_descriptor._internal_create_key,
values=[
_descriptor.EnumValueDescriptor(
name='ORIENTATION_UNSET', index=0, number=0,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
_descriptor.EnumValueDescriptor(
name='ORIENTATION_PORT', index=1, number=1,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
_descriptor.EnumValueDescriptor(
name='ORIENTATION_LAND', index=2, number=2,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
_descriptor.EnumValueDescriptor(
name='ORIENTATION_SQUARE', index=3, number=3,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
],
containing_type=None,
serialized_options=None,
serialized_start=1751,
serialized_end=1855,
)
_sym_db.RegisterEnumDescriptor(_CONFIGURATION_ORIENTATION)
_CONFIGURATION_UIMODETYPE = _descriptor.EnumDescriptor(
name='UiModeType',
full_name='aapt.pb.Configuration.UiModeType',
filename=None,
file=DESCRIPTOR,
create_key=_descriptor._internal_create_key,
values=[
_descriptor.EnumValueDescriptor(
name='UI_MODE_TYPE_UNSET', index=0, number=0,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
_descriptor.EnumValueDescriptor(
name='UI_MODE_TYPE_NORMAL', index=1, number=1,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
_descriptor.EnumValueDescriptor(
name='UI_MODE_TYPE_DESK', index=2, number=2,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
_descriptor.EnumValueDescriptor(
name='UI_MODE_TYPE_CAR', index=3, number=3,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
_descriptor.EnumValueDescriptor(
name='UI_MODE_TYPE_TELEVISION', index=4, number=4,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
_descriptor.EnumValueDescriptor(
name='UI_MODE_TYPE_APPLIANCE', index=5, number=5,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
_descriptor.EnumValueDescriptor(
name='UI_MODE_TYPE_WATCH', index=6, number=6,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
_descriptor.EnumValueDescriptor(
name='UI_MODE_TYPE_VRHEADSET', index=7, number=7,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
],
containing_type=None,
serialized_options=None,
serialized_start=1858,
serialized_end=2073,
)
_sym_db.RegisterEnumDescriptor(_CONFIGURATION_UIMODETYPE)
_CONFIGURATION_UIMODENIGHT = _descriptor.EnumDescriptor(
name='UiModeNight',
full_name='aapt.pb.Configuration.UiModeNight',
filename=None,
file=DESCRIPTOR,
create_key=_descriptor._internal_create_key,
values=[
_descriptor.EnumValueDescriptor(
name='UI_MODE_NIGHT_UNSET', index=0, number=0,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
_descriptor.EnumValueDescriptor(
name='UI_MODE_NIGHT_NIGHT', index=1, number=1,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
_descriptor.EnumValueDescriptor(
name='UI_MODE_NIGHT_NOTNIGHT', index=2, number=2,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
],
containing_type=None,
serialized_options=None,
serialized_start=2075,
serialized_end=2166,
)
_sym_db.RegisterEnumDescriptor(_CONFIGURATION_UIMODENIGHT)
_CONFIGURATION_TOUCHSCREEN = _descriptor.EnumDescriptor(
name='Touchscreen',
full_name='aapt.pb.Configuration.Touchscreen',
filename=None,
file=DESCRIPTOR,
create_key=_descriptor._internal_create_key,
values=[
_descriptor.EnumValueDescriptor(
name='TOUCHSCREEN_UNSET', index=0, number=0,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
_descriptor.EnumValueDescriptor(
name='TOUCHSCREEN_NOTOUCH', index=1, number=1,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
_descriptor.EnumValueDescriptor(
name='TOUCHSCREEN_STYLUS', index=2, number=2,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
_descriptor.EnumValueDescriptor(
name='TOUCHSCREEN_FINGER', index=3, number=3,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
],
containing_type=None,
serialized_options=None,
serialized_start=2168,
serialized_end=2277,
)
_sym_db.RegisterEnumDescriptor(_CONFIGURATION_TOUCHSCREEN)
_CONFIGURATION_KEYSHIDDEN = _descriptor.EnumDescriptor(
name='KeysHidden',
full_name='aapt.pb.Configuration.KeysHidden',
filename=None,
file=DESCRIPTOR,
create_key=_descriptor._internal_create_key,
values=[
_descriptor.EnumValueDescriptor(
name='KEYS_HIDDEN_UNSET', index=0, number=0,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
_descriptor.EnumValueDescriptor(
name='KEYS_HIDDEN_KEYSEXPOSED', index=1, number=1,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
_descriptor.EnumValueDescriptor(
name='KEYS_HIDDEN_KEYSHIDDEN', index=2, number=2,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
_descriptor.EnumValueDescriptor(
name='KEYS_HIDDEN_KEYSSOFT', index=3, number=3,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
],
containing_type=None,
serialized_options=None,
serialized_start=2279,
serialized_end=2397,
)
_sym_db.RegisterEnumDescriptor(_CONFIGURATION_KEYSHIDDEN)
_CONFIGURATION_KEYBOARD = _descriptor.EnumDescriptor(
name='Keyboard',
full_name='aapt.pb.Configuration.Keyboard',
filename=None,
file=DESCRIPTOR,
create_key=_descriptor._internal_create_key,
values=[
_descriptor.EnumValueDescriptor(
name='KEYBOARD_UNSET', index=0, number=0,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
_descriptor.EnumValueDescriptor(
name='KEYBOARD_NOKEYS', index=1, number=1,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
_descriptor.EnumValueDescriptor(
name='KEYBOARD_QWERTY', index=2, number=2,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
_descriptor.EnumValueDescriptor(
name='KEYBOARD_TWELVEKEY', index=3, number=3,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
],
containing_type=None,
serialized_options=None,
serialized_start=2399,
serialized_end=2495,
)
_sym_db.RegisterEnumDescriptor(_CONFIGURATION_KEYBOARD)
_CONFIGURATION_NAVHIDDEN = _descriptor.EnumDescriptor(
name='NavHidden',
full_name='aapt.pb.Configuration.NavHidden',
filename=None,
file=DESCRIPTOR,
create_key=_descriptor._internal_create_key,
values=[
_descriptor.EnumValueDescriptor(
name='NAV_HIDDEN_UNSET', index=0, number=0,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
_descriptor.EnumValueDescriptor(
name='NAV_HIDDEN_NAVEXPOSED', index=1, number=1,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
_descriptor.EnumValueDescriptor(
name='NAV_HIDDEN_NAVHIDDEN', index=2, number=2,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
],
containing_type=None,
serialized_options=None,
serialized_start=2497,
serialized_end=2583,
)
_sym_db.RegisterEnumDescriptor(_CONFIGURATION_NAVHIDDEN)
_CONFIGURATION_NAVIGATION = _descriptor.EnumDescriptor(
name='Navigation',
full_name='aapt.pb.Configuration.Navigation',
filename=None,
file=DESCRIPTOR,
create_key=_descriptor._internal_create_key,
values=[
_descriptor.EnumValueDescriptor(
name='NAVIGATION_UNSET', index=0, number=0,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
_descriptor.EnumValueDescriptor(
name='NAVIGATION_NONAV', index=1, number=1,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
_descriptor.EnumValueDescriptor(
name='NAVIGATION_DPAD', index=2, number=2,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
_descriptor.EnumValueDescriptor(
name='NAVIGATION_TRACKBALL', index=3, number=3,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
_descriptor.EnumValueDescriptor(
name='NAVIGATION_WHEEL', index=4, number=4,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
],
containing_type=None,
serialized_options=None,
serialized_start=2585,
serialized_end=2710,
)
_sym_db.RegisterEnumDescriptor(_CONFIGURATION_NAVIGATION)
_CONFIGURATION = _descriptor.Descriptor(
name='Configuration',
full_name='aapt.pb.Configuration',
filename=None,
file=DESCRIPTOR,
containing_type=None,
create_key=_descriptor._internal_create_key,
fields=[
_descriptor.FieldDescriptor(
name='mcc', full_name='aapt.pb.Configuration.mcc', index=0,
number=1, type=13, cpp_type=3, label=1,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
_descriptor.FieldDescriptor(
name='mnc', full_name='aapt.pb.Configuration.mnc', index=1,
number=2, type=13, cpp_type=3, label=1,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
_descriptor.FieldDescriptor(
name='locale', full_name='aapt.pb.Configuration.locale', index=2,
number=3, type=9, cpp_type=9, label=1,
has_default_value=False, default_value=b"".decode('utf-8'),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
_descriptor.FieldDescriptor(
name='layout_direction', full_name='aapt.pb.Configuration.layout_direction', index=3,
number=4, type=14, cpp_type=8, label=1,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
_descriptor.FieldDescriptor(
name='screen_width', full_name='aapt.pb.Configuration.screen_width', index=4,
number=5, type=13, cpp_type=3, label=1,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
_descriptor.FieldDescriptor(
name='screen_height', full_name='aapt.pb.Configuration.screen_height', index=5,
number=6, type=13, cpp_type=3, label=1,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
_descriptor.FieldDescriptor(
name='screen_width_dp', full_name='aapt.pb.Configuration.screen_width_dp', index=6,
number=7, type=13, cpp_type=3, label=1,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
_descriptor.FieldDescriptor(
name='screen_height_dp', full_name='aapt.pb.Configuration.screen_height_dp', index=7,
number=8, type=13, cpp_type=3, label=1,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
_descriptor.FieldDescriptor(
name='smallest_screen_width_dp', full_name='aapt.pb.Configuration.smallest_screen_width_dp', index=8,
number=9, type=13, cpp_type=3, label=1,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
_descriptor.FieldDescriptor(
name='screen_layout_size', full_name='aapt.pb.Configuration.screen_layout_size', index=9,
number=10, type=14, cpp_type=8, label=1,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
_descriptor.FieldDescriptor(
name='screen_layout_long', full_name='aapt.pb.Configuration.screen_layout_long', index=10,
number=11, type=14, cpp_type=8, label=1,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
_descriptor.FieldDescriptor(
name='screen_round', full_name='aapt.pb.Configuration.screen_round', index=11,
number=12, type=14, cpp_type=8, label=1,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
_descriptor.FieldDescriptor(
name='wide_color_gamut', full_name='aapt.pb.Configuration.wide_color_gamut', index=12,
number=13, type=14, cpp_type=8, label=1,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
_descriptor.FieldDescriptor(
name='hdr', full_name='aapt.pb.Configuration.hdr', index=13,
number=14, type=14, cpp_type=8, label=1,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
_descriptor.FieldDescriptor(
name='orientation', full_name='aapt.pb.Configuration.orientation', index=14,
number=15, type=14, cpp_type=8, label=1,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
_descriptor.FieldDescriptor(
name='ui_mode_type', full_name='aapt.pb.Configuration.ui_mode_type', index=15,
number=16, type=14, cpp_type=8, label=1,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
_descriptor.FieldDescriptor(
name='ui_mode_night', full_name='aapt.pb.Configuration.ui_mode_night', index=16,
number=17, type=14, cpp_type=8, label=1,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
_descriptor.FieldDescriptor(
name='density', full_name='aapt.pb.Configuration.density', index=17,
number=18, type=13, cpp_type=3, label=1,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
_descriptor.FieldDescriptor(
name='touchscreen', full_name='aapt.pb.Configuration.touchscreen', index=18,
number=19, type=14, cpp_type=8, label=1,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
_descriptor.FieldDescriptor(
name='keys_hidden', full_name='aapt.pb.Configuration.keys_hidden', index=19,
number=20, type=14, cpp_type=8, label=1,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
_descriptor.FieldDescriptor(
name='keyboard', full_name='aapt.pb.Configuration.keyboard', index=20,
number=21, type=14, cpp_type=8, label=1,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
_descriptor.FieldDescriptor(
name='nav_hidden', full_name='aapt.pb.Configuration.nav_hidden', index=21,
number=22, type=14, cpp_type=8, label=1,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
_descriptor.FieldDescriptor(
name='navigation', full_name='aapt.pb.Configuration.navigation', index=22,
number=23, type=14, cpp_type=8, label=1,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
_descriptor.FieldDescriptor(
name='sdk_version', full_name='aapt.pb.Configuration.sdk_version', index=23,
number=24, type=13, cpp_type=3, label=1,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
_descriptor.FieldDescriptor(
name='product', full_name='aapt.pb.Configuration.product', index=24,
number=25, type=9, cpp_type=9, label=1,
has_default_value=False, default_value=b"".decode('utf-8'),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
],
extensions=[
],
nested_types=[],
enum_types=[
_CONFIGURATION_LAYOUTDIRECTION,
_CONFIGURATION_SCREENLAYOUTSIZE,
_CONFIGURATION_SCREENLAYOUTLONG,
_CONFIGURATION_SCREENROUND,
_CONFIGURATION_WIDECOLORGAMUT,
_CONFIGURATION_HDR,
_CONFIGURATION_ORIENTATION,
_CONFIGURATION_UIMODETYPE,
_CONFIGURATION_UIMODENIGHT,
_CONFIGURATION_TOUCHSCREEN,
_CONFIGURATION_KEYSHIDDEN,
_CONFIGURATION_KEYBOARD,
_CONFIGURATION_NAVHIDDEN,
_CONFIGURATION_NAVIGATION,
],
serialized_options=None,
is_extendable=False,
syntax='proto3',
extension_ranges=[],
oneofs=[
],
serialized_start=61,
serialized_end=2710,
)
_CONFIGURATION.fields_by_name['layout_direction'].enum_type = _CONFIGURATION_LAYOUTDIRECTION
_CONFIGURATION.fields_by_name['screen_layout_size'].enum_type = _CONFIGURATION_SCREENLAYOUTSIZE
_CONFIGURATION.fields_by_name['screen_layout_long'].enum_type = _CONFIGURATION_SCREENLAYOUTLONG
_CONFIGURATION.fields_by_name['screen_round'].enum_type = _CONFIGURATION_SCREENROUND
_CONFIGURATION.fields_by_name['wide_color_gamut'].enum_type = _CONFIGURATION_WIDECOLORGAMUT
_CONFIGURATION.fields_by_name['hdr'].enum_type = _CONFIGURATION_HDR
_CONFIGURATION.fields_by_name['orientation'].enum_type = _CONFIGURATION_ORIENTATION
_CONFIGURATION.fields_by_name['ui_mode_type'].enum_type = _CONFIGURATION_UIMODETYPE
_CONFIGURATION.fields_by_name['ui_mode_night'].enum_type = _CONFIGURATION_UIMODENIGHT
_CONFIGURATION.fields_by_name['touchscreen'].enum_type = _CONFIGURATION_TOUCHSCREEN
_CONFIGURATION.fields_by_name['keys_hidden'].enum_type = _CONFIGURATION_KEYSHIDDEN
_CONFIGURATION.fields_by_name['keyboard'].enum_type = _CONFIGURATION_KEYBOARD
_CONFIGURATION.fields_by_name['nav_hidden'].enum_type = _CONFIGURATION_NAVHIDDEN
_CONFIGURATION.fields_by_name['navigation'].enum_type = _CONFIGURATION_NAVIGATION
_CONFIGURATION_LAYOUTDIRECTION.containing_type = _CONFIGURATION
_CONFIGURATION_SCREENLAYOUTSIZE.containing_type = _CONFIGURATION
_CONFIGURATION_SCREENLAYOUTLONG.containing_type = _CONFIGURATION
_CONFIGURATION_SCREENROUND.containing_type = _CONFIGURATION
_CONFIGURATION_WIDECOLORGAMUT.containing_type = _CONFIGURATION
_CONFIGURATION_HDR.containing_type = _CONFIGURATION
_CONFIGURATION_ORIENTATION.containing_type = _CONFIGURATION
_CONFIGURATION_UIMODETYPE.containing_type = _CONFIGURATION
_CONFIGURATION_UIMODENIGHT.containing_type = _CONFIGURATION
_CONFIGURATION_TOUCHSCREEN.containing_type = _CONFIGURATION
_CONFIGURATION_KEYSHIDDEN.containing_type = _CONFIGURATION
_CONFIGURATION_KEYBOARD.containing_type = _CONFIGURATION
_CONFIGURATION_NAVHIDDEN.containing_type = _CONFIGURATION
_CONFIGURATION_NAVIGATION.containing_type = _CONFIGURATION
DESCRIPTOR.message_types_by_name['Configuration'] = _CONFIGURATION
_sym_db.RegisterFileDescriptor(DESCRIPTOR)
Configuration = _reflection.GeneratedProtocolMessageType('Configuration', (_message.Message,), {
'DESCRIPTOR' : _CONFIGURATION,
'__module__' : 'frameworks.base.tools.aapt2.Configuration_pb2'
# @@protoc_insertion_point(class_scope:aapt.pb.Configuration)
})
_sym_db.RegisterMessage(Configuration)
DESCRIPTOR._options = None
# @@protoc_insertion_point(module_scope) # @@protoc_insertion_point(module_scope)

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,11 +1,22 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Generated by the protocol buffer compiler. DO NOT EDIT! # Generated by the protocol buffer compiler. DO NOT EDIT!
# NO CHECKED-IN PROTOBUF GENCODE
# source: files.proto # source: files.proto
# Protobuf Python Version: 6.33.0
"""Generated protocol buffer code.""" """Generated protocol buffer code."""
from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor as _descriptor
from google.protobuf import message as _message from google.protobuf import descriptor_pool as _descriptor_pool
from google.protobuf import reflection as _reflection from google.protobuf import runtime_version as _runtime_version
from google.protobuf import symbol_database as _symbol_database from google.protobuf import symbol_database as _symbol_database
from google.protobuf.internal import builder as _builder
_runtime_version.ValidateProtobufRuntimeVersion(
_runtime_version.Domain.PUBLIC,
6,
33,
0,
'',
'files.proto'
)
# @@protoc_insertion_point(imports) # @@protoc_insertion_point(imports)
_sym_db = _symbol_database.Default() _sym_db = _symbol_database.Default()
@ -14,294 +25,24 @@ _sym_db = _symbol_database.Default()
from . import targeting_pb2 as targeting__pb2 from . import targeting_pb2 as targeting__pb2
DESCRIPTOR = _descriptor.FileDescriptor( DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0b\x66iles.proto\x12\x0e\x61ndroid.bundle\x1a\x0ftargeting.proto\"D\n\x06\x41ssets\x12:\n\tdirectory\x18\x01 \x03(\x0b\x32\'.android.bundle.TargetedAssetsDirectory\"M\n\x0fNativeLibraries\x12:\n\tdirectory\x18\x01 \x03(\x0b\x32\'.android.bundle.TargetedNativeDirectory\"D\n\nApexImages\x12\x30\n\x05image\x18\x01 \x03(\x0b\x32!.android.bundle.TargetedApexImageJ\x04\x08\x02\x10\x03\"d\n\x17TargetedAssetsDirectory\x12\x0c\n\x04path\x18\x01 \x01(\t\x12;\n\ttargeting\x18\x02 \x01(\x0b\x32(.android.bundle.AssetsDirectoryTargeting\"d\n\x17TargetedNativeDirectory\x12\x0c\n\x04path\x18\x01 \x01(\t\x12;\n\ttargeting\x18\x02 \x01(\x0b\x32(.android.bundle.NativeDirectoryTargeting\"q\n\x11TargetedApexImage\x12\x0c\n\x04path\x18\x01 \x01(\t\x12\x17\n\x0f\x62uild_info_path\x18\x03 \x01(\t\x12\x35\n\ttargeting\x18\x02 \x01(\x0b\x32\".android.bundle.ApexImageTargetingB\x14\n\x12\x63om.android.bundleb\x06proto3')
name='files.proto',
package='android.bundle',
syntax='proto3',
serialized_options=b'\n\022com.android.bundle',
create_key=_descriptor._internal_create_key,
serialized_pb=b'\n\x0b\x66iles.proto\x12\x0e\x61ndroid.bundle\x1a\x0ftargeting.proto\"D\n\x06\x41ssets\x12:\n\tdirectory\x18\x01 \x03(\x0b\x32\'.android.bundle.TargetedAssetsDirectory\"M\n\x0fNativeLibraries\x12:\n\tdirectory\x18\x01 \x03(\x0b\x32\'.android.bundle.TargetedNativeDirectory\"D\n\nApexImages\x12\x30\n\x05image\x18\x01 \x03(\x0b\x32!.android.bundle.TargetedApexImageJ\x04\x08\x02\x10\x03\"d\n\x17TargetedAssetsDirectory\x12\x0c\n\x04path\x18\x01 \x01(\t\x12;\n\ttargeting\x18\x02 \x01(\x0b\x32(.android.bundle.AssetsDirectoryTargeting\"d\n\x17TargetedNativeDirectory\x12\x0c\n\x04path\x18\x01 \x01(\t\x12;\n\ttargeting\x18\x02 \x01(\x0b\x32(.android.bundle.NativeDirectoryTargeting\"q\n\x11TargetedApexImage\x12\x0c\n\x04path\x18\x01 \x01(\t\x12\x17\n\x0f\x62uild_info_path\x18\x03 \x01(\t\x12\x35\n\ttargeting\x18\x02 \x01(\x0b\x32\".android.bundle.ApexImageTargetingB\x14\n\x12\x63om.android.bundleb\x06proto3'
,
dependencies=[targeting__pb2.DESCRIPTOR,])
_globals = globals()
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'files_pb2', _globals)
_ASSETS = _descriptor.Descriptor( if not _descriptor._USE_C_DESCRIPTORS:
name='Assets', _globals['DESCRIPTOR']._loaded_options = None
full_name='android.bundle.Assets', _globals['DESCRIPTOR']._serialized_options = b'\n\022com.android.bundle'
filename=None, _globals['_ASSETS']._serialized_start=48
file=DESCRIPTOR, _globals['_ASSETS']._serialized_end=116
containing_type=None, _globals['_NATIVELIBRARIES']._serialized_start=118
create_key=_descriptor._internal_create_key, _globals['_NATIVELIBRARIES']._serialized_end=195
fields=[ _globals['_APEXIMAGES']._serialized_start=197
_descriptor.FieldDescriptor( _globals['_APEXIMAGES']._serialized_end=265
name='directory', full_name='android.bundle.Assets.directory', index=0, _globals['_TARGETEDASSETSDIRECTORY']._serialized_start=267
number=1, type=11, cpp_type=10, label=3, _globals['_TARGETEDASSETSDIRECTORY']._serialized_end=367
has_default_value=False, default_value=[], _globals['_TARGETEDNATIVEDIRECTORY']._serialized_start=369
message_type=None, enum_type=None, containing_type=None, _globals['_TARGETEDNATIVEDIRECTORY']._serialized_end=469
is_extension=False, extension_scope=None, _globals['_TARGETEDAPEXIMAGE']._serialized_start=471
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), _globals['_TARGETEDAPEXIMAGE']._serialized_end=584
],
extensions=[
],
nested_types=[],
enum_types=[
],
serialized_options=None,
is_extendable=False,
syntax='proto3',
extension_ranges=[],
oneofs=[
],
serialized_start=48,
serialized_end=116,
)
_NATIVELIBRARIES = _descriptor.Descriptor(
name='NativeLibraries',
full_name='android.bundle.NativeLibraries',
filename=None,
file=DESCRIPTOR,
containing_type=None,
create_key=_descriptor._internal_create_key,
fields=[
_descriptor.FieldDescriptor(
name='directory', full_name='android.bundle.NativeLibraries.directory', index=0,
number=1, type=11, cpp_type=10, label=3,
has_default_value=False, default_value=[],
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
],
extensions=[
],
nested_types=[],
enum_types=[
],
serialized_options=None,
is_extendable=False,
syntax='proto3',
extension_ranges=[],
oneofs=[
],
serialized_start=118,
serialized_end=195,
)
_APEXIMAGES = _descriptor.Descriptor(
name='ApexImages',
full_name='android.bundle.ApexImages',
filename=None,
file=DESCRIPTOR,
containing_type=None,
create_key=_descriptor._internal_create_key,
fields=[
_descriptor.FieldDescriptor(
name='image', full_name='android.bundle.ApexImages.image', index=0,
number=1, type=11, cpp_type=10, label=3,
has_default_value=False, default_value=[],
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
],
extensions=[
],
nested_types=[],
enum_types=[
],
serialized_options=None,
is_extendable=False,
syntax='proto3',
extension_ranges=[],
oneofs=[
],
serialized_start=197,
serialized_end=265,
)
_TARGETEDASSETSDIRECTORY = _descriptor.Descriptor(
name='TargetedAssetsDirectory',
full_name='android.bundle.TargetedAssetsDirectory',
filename=None,
file=DESCRIPTOR,
containing_type=None,
create_key=_descriptor._internal_create_key,
fields=[
_descriptor.FieldDescriptor(
name='path', full_name='android.bundle.TargetedAssetsDirectory.path', index=0,
number=1, type=9, cpp_type=9, label=1,
has_default_value=False, default_value=b"".decode('utf-8'),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
_descriptor.FieldDescriptor(
name='targeting', full_name='android.bundle.TargetedAssetsDirectory.targeting', index=1,
number=2, type=11, cpp_type=10, label=1,
has_default_value=False, default_value=None,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
],
extensions=[
],
nested_types=[],
enum_types=[
],
serialized_options=None,
is_extendable=False,
syntax='proto3',
extension_ranges=[],
oneofs=[
],
serialized_start=267,
serialized_end=367,
)
_TARGETEDNATIVEDIRECTORY = _descriptor.Descriptor(
name='TargetedNativeDirectory',
full_name='android.bundle.TargetedNativeDirectory',
filename=None,
file=DESCRIPTOR,
containing_type=None,
create_key=_descriptor._internal_create_key,
fields=[
_descriptor.FieldDescriptor(
name='path', full_name='android.bundle.TargetedNativeDirectory.path', index=0,
number=1, type=9, cpp_type=9, label=1,
has_default_value=False, default_value=b"".decode('utf-8'),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
_descriptor.FieldDescriptor(
name='targeting', full_name='android.bundle.TargetedNativeDirectory.targeting', index=1,
number=2, type=11, cpp_type=10, label=1,
has_default_value=False, default_value=None,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
],
extensions=[
],
nested_types=[],
enum_types=[
],
serialized_options=None,
is_extendable=False,
syntax='proto3',
extension_ranges=[],
oneofs=[
],
serialized_start=369,
serialized_end=469,
)
_TARGETEDAPEXIMAGE = _descriptor.Descriptor(
name='TargetedApexImage',
full_name='android.bundle.TargetedApexImage',
filename=None,
file=DESCRIPTOR,
containing_type=None,
create_key=_descriptor._internal_create_key,
fields=[
_descriptor.FieldDescriptor(
name='path', full_name='android.bundle.TargetedApexImage.path', index=0,
number=1, type=9, cpp_type=9, label=1,
has_default_value=False, default_value=b"".decode('utf-8'),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
_descriptor.FieldDescriptor(
name='build_info_path', full_name='android.bundle.TargetedApexImage.build_info_path', index=1,
number=3, type=9, cpp_type=9, label=1,
has_default_value=False, default_value=b"".decode('utf-8'),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
_descriptor.FieldDescriptor(
name='targeting', full_name='android.bundle.TargetedApexImage.targeting', index=2,
number=2, type=11, cpp_type=10, label=1,
has_default_value=False, default_value=None,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
],
extensions=[
],
nested_types=[],
enum_types=[
],
serialized_options=None,
is_extendable=False,
syntax='proto3',
extension_ranges=[],
oneofs=[
],
serialized_start=471,
serialized_end=584,
)
_ASSETS.fields_by_name['directory'].message_type = _TARGETEDASSETSDIRECTORY
_NATIVELIBRARIES.fields_by_name['directory'].message_type = _TARGETEDNATIVEDIRECTORY
_APEXIMAGES.fields_by_name['image'].message_type = _TARGETEDAPEXIMAGE
_TARGETEDASSETSDIRECTORY.fields_by_name['targeting'].message_type = targeting__pb2._ASSETSDIRECTORYTARGETING
_TARGETEDNATIVEDIRECTORY.fields_by_name['targeting'].message_type = targeting__pb2._NATIVEDIRECTORYTARGETING
_TARGETEDAPEXIMAGE.fields_by_name['targeting'].message_type = targeting__pb2._APEXIMAGETARGETING
DESCRIPTOR.message_types_by_name['Assets'] = _ASSETS
DESCRIPTOR.message_types_by_name['NativeLibraries'] = _NATIVELIBRARIES
DESCRIPTOR.message_types_by_name['ApexImages'] = _APEXIMAGES
DESCRIPTOR.message_types_by_name['TargetedAssetsDirectory'] = _TARGETEDASSETSDIRECTORY
DESCRIPTOR.message_types_by_name['TargetedNativeDirectory'] = _TARGETEDNATIVEDIRECTORY
DESCRIPTOR.message_types_by_name['TargetedApexImage'] = _TARGETEDAPEXIMAGE
_sym_db.RegisterFileDescriptor(DESCRIPTOR)
Assets = _reflection.GeneratedProtocolMessageType('Assets', (_message.Message,), {
'DESCRIPTOR' : _ASSETS,
'__module__' : 'files_pb2'
# @@protoc_insertion_point(class_scope:android.bundle.Assets)
})
_sym_db.RegisterMessage(Assets)
NativeLibraries = _reflection.GeneratedProtocolMessageType('NativeLibraries', (_message.Message,), {
'DESCRIPTOR' : _NATIVELIBRARIES,
'__module__' : 'files_pb2'
# @@protoc_insertion_point(class_scope:android.bundle.NativeLibraries)
})
_sym_db.RegisterMessage(NativeLibraries)
ApexImages = _reflection.GeneratedProtocolMessageType('ApexImages', (_message.Message,), {
'DESCRIPTOR' : _APEXIMAGES,
'__module__' : 'files_pb2'
# @@protoc_insertion_point(class_scope:android.bundle.ApexImages)
})
_sym_db.RegisterMessage(ApexImages)
TargetedAssetsDirectory = _reflection.GeneratedProtocolMessageType('TargetedAssetsDirectory', (_message.Message,), {
'DESCRIPTOR' : _TARGETEDASSETSDIRECTORY,
'__module__' : 'files_pb2'
# @@protoc_insertion_point(class_scope:android.bundle.TargetedAssetsDirectory)
})
_sym_db.RegisterMessage(TargetedAssetsDirectory)
TargetedNativeDirectory = _reflection.GeneratedProtocolMessageType('TargetedNativeDirectory', (_message.Message,), {
'DESCRIPTOR' : _TARGETEDNATIVEDIRECTORY,
'__module__' : 'files_pb2'
# @@protoc_insertion_point(class_scope:android.bundle.TargetedNativeDirectory)
})
_sym_db.RegisterMessage(TargetedNativeDirectory)
TargetedApexImage = _reflection.GeneratedProtocolMessageType('TargetedApexImage', (_message.Message,), {
'DESCRIPTOR' : _TARGETEDAPEXIMAGE,
'__module__' : 'files_pb2'
# @@protoc_insertion_point(class_scope:android.bundle.TargetedApexImage)
})
_sym_db.RegisterMessage(TargetedApexImage)
DESCRIPTOR._options = None
# @@protoc_insertion_point(module_scope) # @@protoc_insertion_point(module_scope)

File diff suppressed because one or more lines are too long

View File

@ -79,6 +79,10 @@ def _model_to_bam(_build_cmd, srcpath, dstpath):
_register_python_loaders() _register_python_loaders()
if src_fn.get_extension() == 'egg' and zipfile.is_zipfile(src_fn.to_os_specific()):
_build_cmd.warn('Skipping %s as it appears to be a Python .egg archive, was this included by mistake?' % (src_fn.to_os_specific()))
return
loader = p3d.Loader.get_global_ptr() loader = p3d.Loader.get_global_ptr()
options = p3d.LoaderOptions(p3d.LoaderOptions.LF_report_errors | options = p3d.LoaderOptions(p3d.LoaderOptions.LF_report_errors |
p3d.LoaderOptions.LF_no_ram_cache) p3d.LoaderOptions.LF_no_ram_cache)
@ -192,14 +196,14 @@ SITE_PY_ANDROID = """
# module. # module.
import sys, os import sys, os
from importlib import _bootstrap, _bootstrap_external from importlib import _bootstrap, _bootstrap_external
from android_support import log_write as android_log_write
from android_support import find_library
class AndroidExtensionFinder: class AndroidExtensionFinder:
@classmethod @classmethod
def find_spec(cls, fullname, path=None, target=None): def find_spec(cls, fullname, path=None, target=None):
soname = 'libpy.' + fullname + '.so' path = find_library('py.' + fullname)
path = os.path.join(sys.platlibdir, soname) if path:
if os.path.exists(path):
loader = _bootstrap_external.ExtensionFileLoader(fullname, path) loader = _bootstrap_external.ExtensionFileLoader(fullname, path)
return _bootstrap.ModuleSpec(fullname, loader, origin=path) return _bootstrap.ModuleSpec(fullname, loader, origin=path)
@ -210,8 +214,6 @@ sys.meta_path.append(AndroidExtensionFinder)
from _frozen_importlib import _imp, FrozenImporter from _frozen_importlib import _imp, FrozenImporter
from io import RawIOBase, TextIOWrapper from io import RawIOBase, TextIOWrapper
from android_log import write as android_log_write
sys.frozen = True sys.frozen = True
@ -300,7 +302,8 @@ class build_apps(setuptools.Command):
self.android_version_code = 1 self.android_version_code = 1
self.android_min_sdk_version = 21 self.android_min_sdk_version = 21
self.android_max_sdk_version = None self.android_max_sdk_version = None
self.android_target_sdk_version = 30 self.android_target_sdk_version = 35
self.android_manifest_file = None
self.gui_apps = {} self.gui_apps = {}
self.console_apps = {} self.console_apps = {}
self.macos_main_app = None self.macos_main_app = None
@ -316,7 +319,10 @@ class build_apps(setuptools.Command):
'win_amd64', 'win_amd64',
] ]
if sys.version_info >= (3, 13): if sys.version_info >= (3, 14):
# This version of Python is only available for 10.15+.
self.platforms[1] = 'macosx_10_15_x86_64'
elif sys.version_info >= (3, 13):
# This version of Python is only available for 10.13+. # This version of Python is only available for 10.13+.
self.platforms[1] = 'macosx_10_13_x86_64' self.platforms[1] = 'macosx_10_13_x86_64'
@ -586,6 +592,10 @@ class build_apps(setuptools.Command):
data_dir = os.path.join(build_dir, 'assets') data_dir = os.path.join(build_dir, 'assets')
os.makedirs(data_dir, exist_ok=True) os.makedirs(data_dir, exist_ok=True)
res_dir = os.path.join(build_dir, 'res')
res_raw_dir = os.path.join(res_dir, 'raw')
os.makedirs(res_raw_dir, exist_ok=True)
for abi in self.android_abis: for abi in self.android_abis:
lib_dir = os.path.join(build_dir, 'lib', abi) lib_dir = os.path.join(build_dir, 'lib', abi)
os.makedirs(lib_dir, exist_ok=True) os.makedirs(lib_dir, exist_ok=True)
@ -602,7 +612,7 @@ class build_apps(setuptools.Command):
# We end up copying the data multiple times to the same # We end up copying the data multiple times to the same
# directory, but that's probably fine for now. # directory, but that's probably fine for now.
self.build_binaries(platform + suffix, lib_dir, data_dir) self.build_binaries(platform + suffix, lib_dir, data_dir, res_raw_dir)
# Write out the icons to the res directory. # Write out the icons to the res directory.
for appname, icon in self.icon_objects.items(): for appname, icon in self.icon_objects.items():
@ -610,9 +620,9 @@ class build_apps(setuptools.Command):
# Conventional name for icon on Android. # Conventional name for icon on Android.
basename = 'ic_launcher.png' basename = 'ic_launcher.png'
else: else:
basename = f'ic_{appname}.png' appname_sane = appname.replace(' ', '_')
basename = f'ic_{appname_sane}.png'
res_dir = os.path.join(build_dir, 'res')
icon.writeSize(48, os.path.join(res_dir, 'mipmap-mdpi-v4', basename)) icon.writeSize(48, os.path.join(res_dir, 'mipmap-mdpi-v4', basename))
icon.writeSize(72, os.path.join(res_dir, 'mipmap-hdpi-v4', basename)) icon.writeSize(72, os.path.join(res_dir, 'mipmap-hdpi-v4', basename))
icon.writeSize(96, os.path.join(res_dir, 'mipmap-xhdpi-v4', basename)) icon.writeSize(96, os.path.join(res_dir, 'mipmap-xhdpi-v4', basename))
@ -623,8 +633,17 @@ class build_apps(setuptools.Command):
self.build_assets(platform, data_dir) self.build_assets(platform, data_dir)
# Generate an AndroidManifest.xml # Generate an AndroidManifest.xml if none was provided
self.generate_android_manifest(os.path.join(build_dir, 'AndroidManifest.xml')) manifest_path = os.path.join(build_dir, 'AndroidManifest.xml')
if self.android_manifest_file:
try:
self.check_android_manifest(self.android_manifest_file)
except Exception as e:
self.announce(f"Failed to use provided manifest file from {self.android_manifest_file}", distutils.log.FATAL)
raise
self.copy(self.android_manifest_file, manifest_path)
else:
self.generate_android_manifest(manifest_path)
else: else:
self.build_binaries(platform, build_dir, build_dir) self.build_binaries(platform, build_dir, build_dir)
self.build_assets(platform, build_dir) self.build_assets(platform, build_dir)
@ -691,8 +710,14 @@ class build_apps(setuptools.Command):
subprocess.check_call([sys.executable, '-m', 'pip'] + pip_args) subprocess.check_call([sys.executable, '-m', 'pip'] + pip_args)
except: except:
# Display a more helpful message for these common issues. # Display a more helpful message for these common issues.
if platform.startswith('macosx_10_9_') and sys.version_info >= (3, 13): if platform.startswith('macosx_10_13_') and sys.version_info >= (3, 14):
new_platform = platform.replace('macosx_10_9_', 'macosx_10_13_') new_platform = platform.replace('macosx_10_13_', 'macosx_10_15_')
self.announce('This error likely occurs because {} is not a supported target as of Python 3.14.\nChange the target platform to {} instead.'.format(platform, new_platform), distutils.log.ERROR)
elif platform.startswith('macosx_10_9_') and sys.version_info >= (3, 13):
if sys.version_info >= (3, 14):
new_platform = platform.replace('macosx_10_9_', 'macosx_10_15_')
else:
new_platform = platform.replace('macosx_10_9_', 'macosx_10_13_')
self.announce('This error likely occurs because {} is not a supported target as of Python 3.13.\nChange the target platform to {} instead.'.format(platform, new_platform), distutils.log.ERROR) self.announce('This error likely occurs because {} is not a supported target as of Python 3.13.\nChange the target platform to {} instead.'.format(platform, new_platform), distutils.log.ERROR)
elif platform.startswith('manylinux2010_') and sys.version_info >= (3, 11): elif platform.startswith('manylinux2010_') and sys.version_info >= (3, 11):
new_platform = platform.replace('manylinux2010_', 'manylinux2014_') new_platform = platform.replace('manylinux2010_', 'manylinux2014_')
@ -701,7 +726,9 @@ class build_apps(setuptools.Command):
new_platform = platform.replace('manylinux1_', 'manylinux2014_') new_platform = platform.replace('manylinux1_', 'manylinux2014_')
self.announce('This error likely occurs because {} is not a supported target as of Python 3.10.\nChange the target platform to {} instead.'.format(platform, new_platform), distutils.log.ERROR) self.announce('This error likely occurs because {} is not a supported target as of Python 3.10.\nChange the target platform to {} instead.'.format(platform, new_platform), distutils.log.ERROR)
elif platform.startswith('macosx_10_6_') and sys.version_info >= (3, 8): elif platform.startswith('macosx_10_6_') and sys.version_info >= (3, 8):
if sys.version_info >= (3, 13): if sys.version_info >= (3, 14):
new_platform = platform.replace('macosx_10_6_', 'macosx_10_15_')
elif sys.version_info >= (3, 13):
new_platform = platform.replace('macosx_10_6_', 'macosx_10_13_') new_platform = platform.replace('macosx_10_6_', 'macosx_10_13_')
else: else:
new_platform = platform.replace('macosx_10_6_', 'macosx_10_9_') new_platform = platform.replace('macosx_10_6_', 'macosx_10_9_')
@ -855,7 +882,7 @@ class build_apps(setuptools.Command):
if category: if category:
application.set('android:appCategory', category) application.set('android:appCategory', category)
application.set('android:debuggable', ('false', 'true')[self.android_debuggable]) application.set('android:debuggable', ('false', 'true')[self.android_debuggable])
application.set('android:extractNativeLibs', 'true') application.set('android:extractNativeLibs', 'false')
application.set('android:hardwareAccelerated', 'true') application.set('android:hardwareAccelerated', 'true')
app_icon = self.icon_objects.get('*', self.icon_objects.get(self.macos_main_app)) app_icon = self.icon_objects.get('*', self.icon_objects.get(self.macos_main_app))
@ -863,6 +890,8 @@ class build_apps(setuptools.Command):
application.set('android:icon', '@mipmap/ic_launcher') application.set('android:icon', '@mipmap/ic_launcher')
for appname in self.gui_apps: for appname in self.gui_apps:
appname_sane = appname.replace(' ', '_')
activity = ET.SubElement(application, 'activity') activity = ET.SubElement(application, 'activity')
activity.set('android:name', 'org.panda3d.android.PythonActivity') activity.set('android:name', 'org.panda3d.android.PythonActivity')
activity.set('android:label', appname) activity.set('android:label', appname)
@ -871,14 +900,19 @@ class build_apps(setuptools.Command):
activity.set('android:configChanges', 'layoutDirection|locale|grammaticalGender|fontScale|fontWeightAdjustment|orientation|uiMode|screenLayout|screenSize|smallestScreenSize|keyboard|keyboardHidden|navigation') activity.set('android:configChanges', 'layoutDirection|locale|grammaticalGender|fontScale|fontWeightAdjustment|orientation|uiMode|screenLayout|screenSize|smallestScreenSize|keyboard|keyboardHidden|navigation')
activity.set('android:launchMode', 'singleInstance') activity.set('android:launchMode', 'singleInstance')
activity.set('android:preferMinimalPostProcessing', 'true') activity.set('android:preferMinimalPostProcessing', 'true')
activity.set('android:exported', 'true')
act_icon = self.icon_objects.get(appname) act_icon = self.icon_objects.get(appname)
if act_icon and act_icon is not app_icon: if act_icon and act_icon is not app_icon:
activity.set('android:icon', '@mipmap/ic_' + appname) activity.set('android:icon', '@mipmap/ic_' + appname_sane)
meta_data = ET.SubElement(activity, 'meta-data') meta_data = ET.SubElement(activity, 'meta-data')
meta_data.set('android:name', 'android.app.lib_name') meta_data.set('android:name', 'android.app.lib_name')
meta_data.set('android:value', appname) meta_data.set('android:value', appname_sane)
meta_data = ET.SubElement(activity, 'meta-data')
meta_data.set('android:name', 'org.panda3d.android.BLOB_RESOURCE')
meta_data.set('android:resource', '@raw/' + appname_sane + '.so')
intent_filter = ET.SubElement(activity, 'intent-filter') intent_filter = ET.SubElement(activity, 'intent-filter')
ET.SubElement(intent_filter, 'action').set('android:name', 'android.intent.action.MAIN') ET.SubElement(intent_filter, 'action').set('android:name', 'android.intent.action.MAIN')
@ -886,10 +920,44 @@ class build_apps(setuptools.Command):
ET.SubElement(intent_filter, 'category').set('android:name', 'android.intent.category.LEANBACK_LAUNCHER') ET.SubElement(intent_filter, 'category').set('android:name', 'android.intent.category.LEANBACK_LAUNCHER')
tree = ET.ElementTree(manifest) tree = ET.ElementTree(manifest)
if sys.version_info >= (3, 9):
ET.indent(tree)
with open(path, 'wb') as fh: with open(path, 'wb') as fh:
tree.write(fh, encoding='utf-8', xml_declaration=True) tree.write(fh, encoding='utf-8', xml_declaration=True)
def build_binaries(self, platform, binary_dir, data_dir=None): def check_android_manifest(self, path):
""" Checks that the user-provided manifest file seems OK. """
# This function doesn't aim to check everything as it's the user's
# responsibility, just a basic sanity check, but if we change anything
# in our own generation logic then it would be good to check those
# things here and warn if anything needs to be updated.
import xml.etree.ElementTree as ET
android = '{http://schemas.android.com/apk/res/android}'
tree = ET.parse(path)
root = tree.getroot()
if root.tag != 'manifest':
raise RuntimeError(f"Expected <manifest> in {path}")
if root.attrib['package'] != self.application_id:
raise RuntimeError(f"<manifest> package attribute does not match given application_id {self.application_id}")
apps = root.findall('application')
if len(apps) != 1:
raise RuntimeError("<manifest> must contain exactly one <application>")
application = apps[0]
for activity in application.iter('activity'):
if f'{android}name' not in activity.attrib:
raise RuntimeError("<activity> element must have android:name attribute")
if self.android_target_sdk_version >= 31 and f'{android}exported' not in activity.attrib:
raise RuntimeError("<activity> element must have android:exported attribute when targeting Android API 31+")
def build_binaries(self, platform, binary_dir, data_dir=None, blob_dir=None):
""" Builds the binary data for the given platform. """ """ Builds the binary data for the given platform. """
use_wheels = True use_wheels = True
@ -1085,13 +1153,26 @@ class build_apps(setuptools.Command):
stub_name = 'deploy-stub' stub_name = 'deploy-stub'
target_name = appname target_name = appname
appname_sane = appname
if platform.startswith('win') or 'macosx' in platform: if platform.startswith('win') or 'macosx' in platform:
if not use_console: if not use_console:
stub_name = 'deploy-stubw' stub_name = 'deploy-stubw'
elif platform.startswith('android'): elif platform.startswith('android'):
if not use_console: if not use_console:
stub_name = 'libdeploy-stubw.so' stub_name = 'libdeploy-stubw.so'
target_name = 'lib' + target_name + '.so' appname_sane = appname.replace(' ', '_')
target_name = 'lib' + appname_sane + '.so'
if use_wheels:
dexfile = os.path.join(binary_dir, '..', '..', 'classes.dex')
self.copy(os.path.join(p3dwhlfn, 'deploy_libs', 'classes.dex'), dexfile)
# Can this wheel load the blob as a raw resource?
with open(dexfile, 'rb') as fh:
supports_blob_resource = b'org.panda3d.android.BLOB_RESOURCE' in fh.read()
assert supports_blob_resource, \
"Please use a newer Panda3D wheel to build for Android using this version of build_apps"
if platform.startswith('win'): if platform.startswith('win'):
stub_name += '.exe' stub_name += '.exe'
@ -1123,6 +1204,14 @@ class build_apps(setuptools.Command):
if not self.log_filename or '%' not in self.log_filename: if not self.log_filename or '%' not in self.log_filename:
use_strftime = False use_strftime = False
blob_path = None
if blob_dir is not None:
if platform.startswith('android'):
# Not really a .so file, but it forces bundletool to align it
blob_path = os.path.join(blob_dir, appname_sane + '.so')
else:
blob_path = os.path.join(blob_dir, appname_sane)
target_path = os.path.join(binary_dir, target_name) target_path = os.path.join(binary_dir, target_name)
freezer.generateRuntimeFromStub(target_path, stub_file, use_console, { freezer.generateRuntimeFromStub(target_path, stub_file, use_console, {
'prc_data': prcexport if self.embed_prc_data else None, 'prc_data': prcexport if self.embed_prc_data else None,
@ -1136,7 +1225,7 @@ class build_apps(setuptools.Command):
'prc_executable_args_envvar': None, 'prc_executable_args_envvar': None,
'main_dir': None, 'main_dir': None,
'log_filename': self.expand_path(self.log_filename, platform), 'log_filename': self.expand_path(self.log_filename, platform),
}, self.log_append, use_strftime) }, self.log_append, use_strftime, blob_path)
stub_file.close() stub_file.close()
if temp_file: if temp_file:
@ -1251,11 +1340,6 @@ class build_apps(setuptools.Command):
search_path = get_search_path_for(source_path) search_path = get_search_path_for(source_path)
self.copy_with_dependencies(source_path, target_path, search_path) self.copy_with_dependencies(source_path, target_path, search_path)
# Copy classes.dex on Android
if use_wheels and platform.startswith('android'):
self.copy(os.path.join(p3dwhlfn, 'deploy_libs', 'classes.dex'),
os.path.join(binary_dir, '..', '..', 'classes.dex'))
# Extract any other data files from dependency packages. # Extract any other data files from dependency packages.
if data_dir is None: if data_dir is None:
return return

View File

@ -207,7 +207,7 @@ def create_aab(command, basename, build_dir):
and use it to convert an .aab into an .apk. and use it to convert an .aab into an .apk.
""" """
from ._android import AndroidManifest, AbiAlias, BundleConfig, NativeLibraries, ResourceTable from ._android import AndroidManifest, AbiAlias, BundleConfig, NativeLibraries, ResourceTable, UncompressNativeLibraries
bundle_fn = p3d.Filename.from_os_specific(command.dist_dir) / (basename + '.aab') bundle_fn = p3d.Filename.from_os_specific(command.dist_dir) / (basename + '.aab')
build_dir_fn = p3d.Filename.from_os_specific(build_dir) build_dir_fn = p3d.Filename.from_os_specific(build_dir)
@ -230,7 +230,13 @@ def create_aab(command, basename, build_dir):
config = BundleConfig() config = BundleConfig()
config.bundletool.version = '1.1.0' config.bundletool.version = '1.1.0'
config.optimizations.splits_config.Clear() config.optimizations.splits_config.Clear()
config.optimizations.uncompress_native_libraries.enabled = False if axml.extract_native_libs:
config.optimizations.uncompress_native_libraries.enabled = False
else:
config.optimizations.uncompress_native_libraries.enabled = True
config.optimizations.uncompress_native_libraries.alignment = \
UncompressNativeLibraries.PageAlignment.PAGE_ALIGNMENT_16K
config.compression.uncompressed_glob.append('res/raw/**')
bundle.add_subfile('BundleConfig.pb', p3d.StringStream(config.SerializeToString()), 9) bundle.add_subfile('BundleConfig.pb', p3d.StringStream(config.SerializeToString()), 9)
resources = ResourceTable() resources = ResourceTable()
@ -251,13 +257,25 @@ def create_aab(command, basename, build_dir):
entry.entry_id.id = entry_id entry.entry_id.id = entry_id
entry.name = res_name entry.name = res_name
for density, tag in (160, 'mdpi'), (240, 'hdpi'), (320, 'xhdpi'), (480, 'xxhdpi'), (640, 'xxxhdpi'): if type_name == 'raw':
path = f'res/mipmap-{tag}-v4/{res_name}.png' path = f'res/raw/{res_name}'
if (build_dir_fn / path).exists(): if not (build_dir_fn / path).exists():
bundle.add_subfile('base/' + path, build_dir_fn / path, 0) command.announce(
config_value = entry.config_value.add() f'\tRaw resource {path} was not found on disk', distutils.log.ERROR)
config_value.config.density = density return
config_value.value.item.file.path = path
# These are aligned to page size for mmap.
bundle.add_subfile('base/' + path, build_dir_fn / path, 0, 16384)
config_value = entry.config_value.add()
config_value.value.item.file.path = path
else:
for density, tag in (120, 'ldpi'), (160, 'mdpi'), (240, 'hdpi'), (320, 'xhdpi'), (480, 'xxhdpi'), (640, 'xxxhdpi'):
path = f'res/mipmap-{tag}-v4/{res_name}.png'
if (build_dir_fn / path).exists():
bundle.add_subfile('base/' + path, build_dir_fn / path, 0)
config_value = entry.config_value.add()
config_value.config.density = density
config_value.value.item.file.path = path
bundle.add_subfile('base/resources.pb', p3d.StringStream(resources.SerializeToString()), 9) bundle.add_subfile('base/resources.pb', p3d.StringStream(resources.SerializeToString()), 9)
@ -273,13 +291,25 @@ def create_aab(command, basename, build_dir):
# Add the classes.dex. # Add the classes.dex.
bundle.add_subfile('base/dex/classes.dex', build_dir_fn / 'classes.dex', 9) bundle.add_subfile('base/dex/classes.dex', build_dir_fn / 'classes.dex', 9)
# Add libraries, compressed. # Add libraries, compressed, unless extractNativeLibs is false, in which
# case they have to be aligned to page boundaries (16 KiB on 64-bit).
lib_compress = 9 if axml.extract_native_libs else 0
for abi in os.listdir(os.path.join(build_dir, 'lib')): for abi in os.listdir(os.path.join(build_dir, 'lib')):
abi_dir = os.path.join(build_dir, 'lib', abi) abi_dir = os.path.join(build_dir, 'lib', abi)
if axml.extract_native_libs:
lib_align = 0
elif '64' in abi:
lib_align = 16384
else:
lib_align = 4096
for lib in os.listdir(abi_dir): for lib in os.listdir(abi_dir):
if lib.startswith('lib') and lib.endswith('.so'): if lib.startswith('lib') and lib.endswith('.so'):
bundle.add_subfile(f'base/lib/{abi}/{lib}', build_dir_fn / 'lib' / abi / lib, 9) bundle.add_subfile(f'base/lib/{abi}/{lib}',
build_dir_fn / 'lib' / abi / lib,
lib_compress, lib_align)
# Add assets, compressed. # Add assets, compressed.
assets_dir = os.path.join(build_dir, 'assets') assets_dir = os.path.join(build_dir, 'assets')

View File

@ -48,7 +48,7 @@ class ClockDelta(DirectObject.DirectObject):
notify = DirectNotifyGlobal.directNotify.newCategory('ClockDelta') notify = DirectNotifyGlobal.directNotify.newCategory('ClockDelta')
def __init__(self): def __init__(self) -> None:
self.globalClock = ClockObject.getGlobalClock() self.globalClock = ClockObject.getGlobalClock()
# self.delta is the relative delta from our clock to the # self.delta is the relative delta from our clock to the

View File

@ -1,4 +1,4 @@
from panda3d.core import DocumentSpec, Filename, HTTPClient, VirtualFileSystem, getModelPath from panda3d.core import DocumentSpec, Filename, VirtualFileSystem, getModelPath
from panda3d.direct import CConnectionRepository, DCPacker from panda3d.direct import CConnectionRepository, DCPacker
from direct.task import Task from direct.task import Task
from direct.task.TaskManagerGlobal import taskMgr from direct.task.TaskManagerGlobal import taskMgr
@ -590,6 +590,7 @@ class ConnectionRepository(
if self.http is None: if self.http is None:
try: try:
from panda3d.core import HTTPClient
self.http = HTTPClient() self.http = HTTPClient()
except Exception: except Exception:
pass pass

View File

@ -317,7 +317,7 @@ class DistributedObjectAI(DistributedObjectBase):
# setLocation destroys self._zoneData if we move away to # setLocation destroys self._zoneData if we move away to
# a different zone # a different zone
if self._zoneData is None: if self._zoneData is None:
from otp.ai.AIZoneData import AIZoneData # type: ignore[import] from otp.ai.AIZoneData import AIZoneData # type: ignore[import-not-found]
self._zoneData = AIZoneData(self.air, self.parentId, self.zoneId) self._zoneData = AIZoneData(self.air, self.parentId, self.zoneId)
return self._zoneData return self._zoneData
@ -515,7 +515,7 @@ class DistributedObjectAI(DistributedObjectBase):
# simultaneously on different lists of avatars, although they # simultaneously on different lists of avatars, although they
# should have different names. # should have different names.
from otp.ai import Barrier # type: ignore[import] from otp.ai import Barrier # type: ignore[import-not-found]
context = self.__nextBarrierContext context = self.__nextBarrierContext
# We assume the context number is passed as a uint16. # We assume the context number is passed as a uint16.
self.__nextBarrierContext = (self.__nextBarrierContext + 1) & 0xffff self.__nextBarrierContext = (self.__nextBarrierContext + 1) & 0xffff

View File

@ -432,7 +432,7 @@ class DistributedObjectUD(DistributedObjectBase):
# simultaneously on different lists of avatars, although they # simultaneously on different lists of avatars, although they
# should have different names. # should have different names.
from otp.ai import Barrier # type: ignore[import] from otp.ai import Barrier # type: ignore[import-not-found]
context = self.__nextBarrierContext context = self.__nextBarrierContext
# We assume the context number is passed as a uint16. # We assume the context number is passed as a uint16.
self.__nextBarrierContext = (self.__nextBarrierContext + 1) & 0xffff self.__nextBarrierContext = (self.__nextBarrierContext + 1) & 0xffff

View File

@ -84,8 +84,8 @@ dclass DistributedSmoothNode: DistributedNode {
suggestResync(uint32 avId, int16 timestampA, int16 timestampB, suggestResync(uint32 avId, int16 timestampA, int16 timestampB,
int32 serverTimeSec, uint16 serverTimeUSec, int32 serverTimeSec, uint16 serverTimeUSec,
uint16 / 100 uncertainty); int16 / 100 uncertainty);
returnResync(uint32 avId, int16 timestampB, returnResync(uint32 avId, int16 timestampB,
int32 serverTimeSec, uint16 serverTimeUSec, int32 serverTimeSec, uint16 serverTimeUSec,
uint16 / 100 uncertainty); int16 / 100 uncertainty);
}; };

View File

@ -1,15 +1,19 @@
from __future__ import annotations
__all__ = ["Dtool_ObjectToDict", "Dtool_funcToMethod"] __all__ = ["Dtool_ObjectToDict", "Dtool_funcToMethod"]
from collections.abc import Callable
def Dtool_ObjectToDict(cls, name, obj): def Dtool_ObjectToDict(cls, name, obj):
cls.DtoolClassDict[name] = obj cls.DtoolClassDict[name] = obj
def Dtool_funcToMethod(func, cls, method_name=None): def Dtool_funcToMethod(func: Callable, cls, method_name: str | None = None) -> None:
"""Adds func to class so it is an accessible method; use method_name to specify the name to be used for calling the method. """Adds func to class so it is an accessible method; use method_name to specify the name to be used for calling the method.
The new method is accessible to any instance immediately.""" The new method is accessible to any instance immediately."""
func.__func__ = func func.__func__ = func # type: ignore[attr-defined]
func.__self__ = None func.__self__ = None # type: ignore[attr-defined]
if not method_name: if not method_name:
method_name = func.__name__ method_name = func.__name__
cls.DtoolClassDict[method_name] = func cls.DtoolClassDict[method_name] = func

View File

@ -44,6 +44,7 @@ class DirectButton(DirectFrame):
# Sounds to be used for button events # Sounds to be used for button events
('rolloverSound', DGG.getDefaultRolloverSound(), self.setRolloverSound), ('rolloverSound', DGG.getDefaultRolloverSound(), self.setRolloverSound),
('clickSound', DGG.getDefaultClickSound(), self.setClickSound), ('clickSound', DGG.getDefaultClickSound(), self.setClickSound),
('releaseSound', DGG.getDefaultReleaseSound(), self.setReleaseSound),
# Can only be specified at time of widget contruction # Can only be specified at time of widget contruction
# Do the text/graphics appear to move when the button is clicked # Do the text/graphics appear to move when the button is clicked
('pressEffect', 1, DGG.INITOPT), ('pressEffect', 1, DGG.INITOPT),
@ -108,6 +109,13 @@ class DirectButton(DirectFrame):
# Pass any extra args to command # Pass any extra args to command
self['command'](*self['extraArgs']) self['command'](*self['extraArgs'])
def setRolloverSound(self):
rolloverSound = self['rolloverSound']
if rolloverSound:
self.guiItem.setSound(DGG.ENTER + self.guiId, rolloverSound)
else:
self.guiItem.clearSound(DGG.ENTER + self.guiId)
def setClickSound(self): def setClickSound(self):
clickSound = self['clickSound'] clickSound = self['clickSound']
# Clear out sounds # Clear out sounds
@ -122,9 +130,16 @@ class DirectButton(DirectFrame):
if DGG.RMB in self['commandButtons']: if DGG.RMB in self['commandButtons']:
self.guiItem.setSound(DGG.B3PRESS + self.guiId, clickSound) self.guiItem.setSound(DGG.B3PRESS + self.guiId, clickSound)
def setRolloverSound(self): def setReleaseSound(self):
rolloverSound = self['rolloverSound'] releaseSound = self['releaseSound']
if rolloverSound: # Clear out sounds
self.guiItem.setSound(DGG.ENTER + self.guiId, rolloverSound) self.guiItem.clearSound(DGG.B1RELEASE + self.guiId)
else: self.guiItem.clearSound(DGG.B2RELEASE + self.guiId)
self.guiItem.clearSound(DGG.ENTER + self.guiId) self.guiItem.clearSound(DGG.B3RELEASE + self.guiId)
if releaseSound:
if DGG.LMB in self['commandButtons']:
self.guiItem.setSound(DGG.B1RELEASE + self.guiId, releaseSound)
if DGG.MMB in self['commandButtons']:
self.guiItem.setSound(DGG.B2RELEASE + self.guiId, releaseSound)
if DGG.RMB in self['commandButtons']:
self.guiItem.setSound(DGG.B3RELEASE + self.guiId, releaseSound)

View File

@ -28,6 +28,7 @@ class DirectCheckBox(DirectButton):
# Sounds to be used for button events # Sounds to be used for button events
('rolloverSound', DGG.getDefaultRolloverSound(), self.setRolloverSound), ('rolloverSound', DGG.getDefaultRolloverSound(), self.setRolloverSound),
('clickSound', DGG.getDefaultClickSound(), self.setClickSound), ('clickSound', DGG.getDefaultClickSound(), self.setClickSound),
('releaseSound', DGG.getDefaultReleaseSound(), self.setReleaseSound),
# Can only be specified at time of widget contruction # Can only be specified at time of widget contruction
# Do the text/graphics appear to move when the button is clicked # Do the text/graphics appear to move when the button is clicked
('pressEffect', 1, DGG.INITOPT), ('pressEffect', 1, DGG.INITOPT),

View File

@ -17,8 +17,9 @@ from panda3d.core import (
defaultFont = None defaultFont = None
defaultFontFunc = TextNode.getDefaultFont defaultFontFunc = TextNode.getDefaultFont
defaultClickSound = None
defaultRolloverSound = None defaultRolloverSound = None
defaultClickSound = None
defaultReleaseSound = None
defaultDialogGeom = None defaultDialogGeom = None
defaultDialogRelief = PGFrameStyle.TBevelOut defaultDialogRelief = PGFrameStyle.TBevelOut
drawOrder = 100 drawOrder = 100
@ -129,6 +130,13 @@ def setDefaultClickSound(newSound):
global defaultClickSound global defaultClickSound
defaultClickSound = newSound defaultClickSound = newSound
def getDefaultReleaseSound():
return defaultReleaseSound
def setDefaultReleaseSound(newSound):
global defaultReleaseSound
defaultReleaseSound = newSound
def getDefaultFont(): def getDefaultFont():
global defaultFont global defaultFont
if defaultFont is None: if defaultFont is None:

View File

@ -1,11 +1,14 @@
"""Defines the IntervalManager class as well as the global instance of """Defines the IntervalManager class as well as the global instance of
this class, ivalMgr.""" this class, ivalMgr."""
from __future__ import annotations
__all__ = ['IntervalManager', 'ivalMgr'] __all__ = ['IntervalManager', 'ivalMgr']
from panda3d.core import EventQueue from panda3d.core import EventQueue
from panda3d.direct import CIntervalManager, Dtool_BorrowThisReference from panda3d.direct import CInterval, CIntervalManager, Dtool_BorrowThisReference
from direct.showbase import EventManager from direct.showbase import EventManager
from . import Interval
import fnmatch import fnmatch
class IntervalManager(CIntervalManager): class IntervalManager(CIntervalManager):
@ -15,7 +18,7 @@ class IntervalManager(CIntervalManager):
# the Python extensions is to add support for Python-based # the Python extensions is to add support for Python-based
# intervals (like MetaIntervals). # intervals (like MetaIntervals).
def __init__(self, globalPtr = 0): def __init__(self, globalPtr: bool = False) -> None:
# Pass globalPtr == 1 to the constructor to trick it into # Pass globalPtr == 1 to the constructor to trick it into
# "constructing" a Python wrapper around the global # "constructing" a Python wrapper around the global
# CIntervalManager object. # CIntervalManager object.
@ -28,8 +31,8 @@ class IntervalManager(CIntervalManager):
self.eventQueue = EventQueue() self.eventQueue = EventQueue()
self.MyEventmanager = EventManager.EventManager(self.eventQueue) self.MyEventmanager = EventManager.EventManager(self.eventQueue)
self.setEventQueue(self.eventQueue) self.setEventQueue(self.eventQueue)
self.ivals = [] self.ivals: list[Interval.Interval | CInterval | None] = []
self.removedIvals = {} self.removedIvals: dict = {}
def addInterval(self, interval): def addInterval(self, interval):
index = self.addCInterval(interval, 1) index = self.addCInterval(interval, 1)
@ -86,7 +89,7 @@ class IntervalManager(CIntervalManager):
ival.pause() ival.pause()
return len(ivals) return len(ivals)
def step(self): def step(self) -> None:
# This method should be called once per frame to perform all # This method should be called once per frame to perform all
# of the per-frame processing on the active intervals. # of the per-frame processing on the active intervals.
# Call C++ step, then do the Python stuff. # Call C++ step, then do the Python stuff.
@ -101,7 +104,7 @@ class IntervalManager(CIntervalManager):
CIntervalManager.interrupt(self) CIntervalManager.interrupt(self)
self.__doPythonCallbacks() self.__doPythonCallbacks()
def __doPythonCallbacks(self): def __doPythonCallbacks(self) -> None:
# This method does all of the required Python post-processing # This method does all of the required Python post-processing
# after performing some C++-level action. # after performing some C++-level action.
# It is important to call all of the python callbacks on the # It is important to call all of the python callbacks on the
@ -137,4 +140,4 @@ class IntervalManager(CIntervalManager):
self.ivals[index] = interval self.ivals[index] = interval
#: The global IntervalManager object. #: The global IntervalManager object.
ivalMgr = IntervalManager(1) ivalMgr = IntervalManager(True)

View File

@ -122,7 +122,7 @@ class LerpPosInterval(LerpNodePathInterval):
def privDoEvent(self, t, event): def privDoEvent(self, t, event):
# This function is only used if Python functors were passed in # This function is only used if Python functors were passed in
# for some of the input parameters. # for some of the input parameters.
if self.paramSetup and event == CInterval.ETInitialize: if self.paramSetup and (event == CInterval.ETInitialize or event == CInterval.ETInstant):
self.setupParam(self.setEndPos, self.endPos) self.setupParam(self.setEndPos, self.endPos)
self.setupParam(self.setStartPos, self.startPos) self.setupParam(self.setStartPos, self.startPos)
LerpNodePathInterval.privDoEvent(self, t, event) LerpNodePathInterval.privDoEvent(self, t, event)
@ -153,7 +153,7 @@ class LerpHprInterval(LerpNodePathInterval):
def privDoEvent(self, t, event): def privDoEvent(self, t, event):
# This function is only used if Python functors were passed in # This function is only used if Python functors were passed in
# for some of the input parameters. # for some of the input parameters.
if self.paramSetup and event == CInterval.ETInitialize: if self.paramSetup and (event == CInterval.ETInitialize or event == CInterval.ETInstant):
self.setupParam(self.setEndHpr, self.endHpr) self.setupParam(self.setEndHpr, self.endHpr)
self.setupParam(self.setStartHpr, self.startHpr) self.setupParam(self.setStartHpr, self.startHpr)
self.setupParam(self.setStartQuat, self.startQuat) self.setupParam(self.setStartQuat, self.startQuat)
@ -192,7 +192,7 @@ class LerpQuatInterval(LerpNodePathInterval):
def privDoEvent(self, t, event): def privDoEvent(self, t, event):
# This function is only used if Python functors were passed in # This function is only used if Python functors were passed in
# for some of the input parameters. # for some of the input parameters.
if self.paramSetup and event == CInterval.ETInitialize: if self.paramSetup and (event == CInterval.ETInitialize or event == CInterval.ETInstant):
self.setupParam(self.setEndQuat, self.endQuat) self.setupParam(self.setEndQuat, self.endQuat)
self.setupParam(self.setStartHpr, self.startHpr) self.setupParam(self.setStartHpr, self.startHpr)
self.setupParam(self.setStartQuat, self.startQuat) self.setupParam(self.setStartQuat, self.startQuat)
@ -219,7 +219,7 @@ class LerpScaleInterval(LerpNodePathInterval):
def privDoEvent(self, t, event): def privDoEvent(self, t, event):
# This function is only used if Python functors were passed in # This function is only used if Python functors were passed in
# for some of the input parameters. # for some of the input parameters.
if self.paramSetup and event == CInterval.ETInitialize: if self.paramSetup and (event == CInterval.ETInitialize or event == CInterval.ETInstant):
self.setupParam(self.setEndScale, self.endScale) self.setupParam(self.setEndScale, self.endScale)
self.setupParam(self.setStartScale, self.startScale) self.setupParam(self.setStartScale, self.startScale)
LerpNodePathInterval.privDoEvent(self, t, event) LerpNodePathInterval.privDoEvent(self, t, event)
@ -245,7 +245,7 @@ class LerpShearInterval(LerpNodePathInterval):
def privDoEvent(self, t, event): def privDoEvent(self, t, event):
# This function is only used if Python functors were passed in # This function is only used if Python functors were passed in
# for some of the input parameters. # for some of the input parameters.
if self.paramSetup and event == CInterval.ETInitialize: if self.paramSetup and (event == CInterval.ETInitialize or event == CInterval.ETInstant):
self.setupParam(self.setEndShear, self.endShear) self.setupParam(self.setEndShear, self.endShear)
self.setupParam(self.setStartShear, self.startShear) self.setupParam(self.setStartShear, self.startShear)
LerpNodePathInterval.privDoEvent(self, t, event) LerpNodePathInterval.privDoEvent(self, t, event)
@ -280,7 +280,7 @@ class LerpPosHprInterval(LerpNodePathInterval):
def privDoEvent(self, t, event): def privDoEvent(self, t, event):
# This function is only used if Python functors were passed in # This function is only used if Python functors were passed in
# for some of the input parameters. # for some of the input parameters.
if self.paramSetup and event == CInterval.ETInitialize: if self.paramSetup and (event == CInterval.ETInitialize or event == CInterval.ETInstant):
self.setupParam(self.setEndPos, self.endPos) self.setupParam(self.setEndPos, self.endPos)
self.setupParam(self.setStartPos, self.startPos) self.setupParam(self.setStartPos, self.startPos)
self.setupParam(self.setEndHpr, self.endHpr) self.setupParam(self.setEndHpr, self.endHpr)
@ -326,7 +326,7 @@ class LerpPosQuatInterval(LerpNodePathInterval):
def privDoEvent(self, t, event): def privDoEvent(self, t, event):
# This function is only used if Python functors were passed in # This function is only used if Python functors were passed in
# for some of the input parameters. # for some of the input parameters.
if self.paramSetup and event == CInterval.ETInitialize: if self.paramSetup and (event == CInterval.ETInitialize or event == CInterval.ETInstant):
self.setupParam(self.setEndPos, self.endPos) self.setupParam(self.setEndPos, self.endPos)
self.setupParam(self.setStartPos, self.startPos) self.setupParam(self.setStartPos, self.startPos)
self.setupParam(self.setEndQuat, self.endQuat) self.setupParam(self.setEndQuat, self.endQuat)
@ -365,7 +365,7 @@ class LerpHprScaleInterval(LerpNodePathInterval):
def privDoEvent(self, t, event): def privDoEvent(self, t, event):
# This function is only used if Python functors were passed in # This function is only used if Python functors were passed in
# for some of the input parameters. # for some of the input parameters.
if self.paramSetup and event == CInterval.ETInitialize: if self.paramSetup and (event == CInterval.ETInitialize or event == CInterval.ETInstant):
self.setupParam(self.setEndHpr, self.endHpr) self.setupParam(self.setEndHpr, self.endHpr)
self.setupParam(self.setStartHpr, self.startHpr) self.setupParam(self.setStartHpr, self.startHpr)
self.setupParam(self.setStartQuat, self.startQuat) self.setupParam(self.setStartQuat, self.startQuat)
@ -414,7 +414,7 @@ class LerpQuatScaleInterval(LerpNodePathInterval):
def privDoEvent(self, t, event): def privDoEvent(self, t, event):
# This function is only used if Python functors were passed in # This function is only used if Python functors were passed in
# for some of the input parameters. # for some of the input parameters.
if self.paramSetup and event == CInterval.ETInitialize: if self.paramSetup and (event == CInterval.ETInitialize or event == CInterval.ETInstant):
self.setupParam(self.setEndQuat, self.endQuat) self.setupParam(self.setEndQuat, self.endQuat)
self.setupParam(self.setStartHpr, self.startHpr) self.setupParam(self.setStartHpr, self.startHpr)
self.setupParam(self.setStartQuat, self.startQuat) self.setupParam(self.setStartQuat, self.startQuat)
@ -459,7 +459,7 @@ class LerpPosHprScaleInterval(LerpNodePathInterval):
def privDoEvent(self, t, event): def privDoEvent(self, t, event):
# This function is only used if Python functors were passed in # This function is only used if Python functors were passed in
# for some of the input parameters. # for some of the input parameters.
if self.paramSetup and event == CInterval.ETInitialize: if self.paramSetup and (event == CInterval.ETInitialize or event == CInterval.ETInstant):
self.setupParam(self.setEndPos, self.endPos) self.setupParam(self.setEndPos, self.endPos)
self.setupParam(self.setStartPos, self.startPos) self.setupParam(self.setStartPos, self.startPos)
self.setupParam(self.setEndHpr, self.endHpr) self.setupParam(self.setEndHpr, self.endHpr)
@ -516,7 +516,7 @@ class LerpPosQuatScaleInterval(LerpNodePathInterval):
def privDoEvent(self, t, event): def privDoEvent(self, t, event):
# This function is only used if Python functors were passed in # This function is only used if Python functors were passed in
# for some of the input parameters. # for some of the input parameters.
if self.paramSetup and event == CInterval.ETInitialize: if self.paramSetup and (event == CInterval.ETInitialize or event == CInterval.ETInstant):
self.setupParam(self.setEndPos, self.endPos) self.setupParam(self.setEndPos, self.endPos)
self.setupParam(self.setStartPos, self.startPos) self.setupParam(self.setStartPos, self.startPos)
self.setupParam(self.setEndQuat, self.endQuat) self.setupParam(self.setEndQuat, self.endQuat)
@ -569,7 +569,7 @@ class LerpPosHprScaleShearInterval(LerpNodePathInterval):
def privDoEvent(self, t, event): def privDoEvent(self, t, event):
# This function is only used if Python functors were passed in # This function is only used if Python functors were passed in
# for some of the input parameters. # for some of the input parameters.
if self.paramSetup and event == CInterval.ETInitialize: if self.paramSetup and (event == CInterval.ETInitialize or event == CInterval.ETInstant):
self.setupParam(self.setEndPos, self.endPos) self.setupParam(self.setEndPos, self.endPos)
self.setupParam(self.setStartPos, self.startPos) self.setupParam(self.setStartPos, self.startPos)
self.setupParam(self.setEndHpr, self.endHpr) self.setupParam(self.setEndHpr, self.endHpr)
@ -636,7 +636,7 @@ class LerpPosQuatScaleShearInterval(LerpNodePathInterval):
def privDoEvent(self, t, event): def privDoEvent(self, t, event):
# This function is only used if Python functors were passed in # This function is only used if Python functors were passed in
# for some of the input parameters. # for some of the input parameters.
if self.paramSetup and event == CInterval.ETInitialize: if self.paramSetup and (event == CInterval.ETInitialize or event == CInterval.ETInstant):
self.setupParam(self.setEndPos, self.endPos) self.setupParam(self.setEndPos, self.endPos)
self.setupParam(self.setStartPos, self.startPos) self.setupParam(self.setStartPos, self.startPos)
self.setupParam(self.setEndQuat, self.endQuat) self.setupParam(self.setEndQuat, self.endQuat)

View File

@ -12,6 +12,8 @@ Or, you can enable the following variable in your Config.prc::
show-buffers true show-buffers true
""" """
from __future__ import annotations
__all__ = ['BufferViewer'] __all__ = ['BufferViewer']
from panda3d.core import ( from panda3d.core import (
@ -39,14 +41,19 @@ from direct.task.TaskManagerGlobal import taskMgr
from direct.directnotify.DirectNotifyGlobal import directNotify from direct.directnotify.DirectNotifyGlobal import directNotify
from direct.showbase.DirectObject import DirectObject from direct.showbase.DirectObject import DirectObject
import math import math
from typing import Literal, Union
# The following variables are typing constructs used in annotations
# to succinctly express complex type structures.
_Texture = Union[Texture, GraphicsOutput, Literal['all']]
class BufferViewer(DirectObject): class BufferViewer(DirectObject):
notify = directNotify.newCategory('BufferViewer') notify = directNotify.newCategory('BufferViewer')
def __init__(self, win, parent): def __init__(self, win: GraphicsOutput | None, parent: NodePath) -> None:
"""Access: private. Constructor.""" """Access: private. Constructor."""
self.enabled = 0 self.enabled = False
size = ConfigVariableDouble('buffer-viewer-size', '0 0') size = ConfigVariableDouble('buffer-viewer-size', '0 0')
self.sizex = size[0] self.sizex = size[0]
self.sizey = size[1] self.sizey = size[1]
@ -59,23 +66,23 @@ class BufferViewer(DirectObject):
self.win = win self.win = win
self.engine = GraphicsEngine.getGlobalPtr() self.engine = GraphicsEngine.getGlobalPtr()
self.renderParent = parent self.renderParent = parent
self.cards = [] self.cards: list[NodePath] = []
self.cardindex = 0 self.cardindex = 0
self.cardmaker = CardMaker("cubemaker") self.cardmaker = CardMaker("cubemaker")
self.cardmaker.setFrame(-1,1,-1,1) self.cardmaker.setFrame(-1,1,-1,1)
self.task = 0 self.task = 0
self.dirty = 1 self.dirty = True
self.accept("render-texture-targets-changed", self.refreshReadout) self.accept("render-texture-targets-changed", self.refreshReadout)
if ConfigVariableBool("show-buffers", 0): if ConfigVariableBool("show-buffers", 0):
self.enable(1) self.enable(True)
def refreshReadout(self): def refreshReadout(self) -> None:
"""Force the readout to be refreshed. This is usually invoked """Force the readout to be refreshed. This is usually invoked
by GraphicsOutput::add_render_texture (via an event handler). by GraphicsOutput::add_render_texture (via an event handler).
However, it is also possible to invoke it manually. Currently, However, it is also possible to invoke it manually. Currently,
the only time I know of that this is necessary is after a the only time I know of that this is necessary is after a
window resize (and I ought to fix that).""" window resize (and I ought to fix that)."""
self.dirty = 1 self.dirty = True
# Call enabled again, mainly to ensure that the task has been # Call enabled again, mainly to ensure that the task has been
# started. # started.
@ -95,14 +102,14 @@ class BufferViewer(DirectObject):
"""Returns true if the buffer viewer is currently enabled.""" """Returns true if the buffer viewer is currently enabled."""
return self.enabled return self.enabled
def enable(self, x): def enable(self, x: bool) -> None:
"""Turn the buffer viewer on or off. The initial state of the """Turn the buffer viewer on or off. The initial state of the
buffer viewer depends on the Config variable 'show-buffers'.""" buffer viewer depends on the Config variable 'show-buffers'."""
if x != 0 and x != 1: if x != 0 and x != 1:
BufferViewer.notify.error('invalid parameter to BufferViewer.enable') BufferViewer.notify.error('invalid parameter to BufferViewer.enable')
return return
self.enabled = x self.enabled = x
self.dirty = 1 self.dirty = True
if (x and self.task == 0): if (x and self.task == 0):
self.task = taskMgr.add(self.maintainReadout, "buffer-viewer-maintain-readout", self.task = taskMgr.add(self.maintainReadout, "buffer-viewer-maintain-readout",
priority=1) priority=1)
@ -218,7 +225,11 @@ class BufferViewer(DirectObject):
self.renderParent = renderParent self.renderParent = renderParent
self.dirty = 1 self.dirty = 1
def analyzeTextureSet(self, x, set): def analyzeTextureSet(
self,
x: _Texture | GraphicsEngine | list[_Texture | GraphicsEngine],
set: dict[Texture, int],
) -> None:
"""Access: private. Converts a list of GraphicsObject, """Access: private. Converts a list of GraphicsObject,
GraphicsEngine, and Texture into a table of Textures.""" GraphicsEngine, and Texture into a table of Textures."""
@ -240,7 +251,7 @@ class BufferViewer(DirectObject):
else: else:
return return
def makeFrame(self, sizex, sizey): def makeFrame(self, sizex: int, sizey: int) -> NodePath:
"""Access: private. Each texture card is displayed with """Access: private. Each texture card is displayed with
a two-pixel wide frame (a ring of black and a ring of white). a two-pixel wide frame (a ring of black and a ring of white).
This routine builds the frame geometry. It is necessary to This routine builds the frame geometry. It is necessary to
@ -287,7 +298,7 @@ class BufferViewer(DirectObject):
geomnode.addGeom(geom) geomnode.addGeom(geom)
return NodePath(geomnode) return NodePath(geomnode)
def maintainReadout(self, task): def maintainReadout(self, task: object) -> int:
"""Access: private. Whenever necessary, rebuilds the entire """Access: private. Whenever necessary, rebuilds the entire
display from scratch. This is only done when the configuration display from scratch. This is only done when the configuration
parameters have changed.""" parameters have changed."""
@ -295,7 +306,7 @@ class BufferViewer(DirectObject):
# If nothing has changed, don't update. # If nothing has changed, don't update.
if not self.dirty: if not self.dirty:
return Task.cont return Task.cont
self.dirty = 0 self.dirty = False
# Delete the old set of cards. # Delete the old set of cards.
for card in self.cards: for card in self.cards:
@ -308,8 +319,8 @@ class BufferViewer(DirectObject):
return Task.done return Task.done
# Generate the include and exclude sets. # Generate the include and exclude sets.
exclude = {} exclude: dict[Texture, int] = {}
include = {} include: dict[Texture, int] = {}
self.analyzeTextureSet(self.exclude, exclude) self.analyzeTextureSet(self.exclude, exclude)
self.analyzeTextureSet(self.include, include) self.analyzeTextureSet(self.include, include)
@ -407,6 +418,7 @@ class BufferViewer(DirectObject):
bordersize = 4.0 bordersize = 4.0
assert self.win is not None
if float(self.sizex) == 0.0 and float(self.sizey) == 0.0: if float(self.sizex) == 0.0 and float(self.sizey) == 0.0:
sizey = int(0.4266666667 * self.win.getYSize()) sizey = int(0.4266666667 * self.win.getYSize())
sizex = (sizey * aspectx) // aspecty sizex = (sizey * aspectx) // aspecty

View File

@ -1,8 +1,14 @@
"""Defines the DirectObject class, a convenient class to inherit from if the """Defines the DirectObject class, a convenient class to inherit from if the
object needs to be able to respond to events.""" object needs to be able to respond to events."""
from __future__ import annotations
__all__ = ['DirectObject'] __all__ = ['DirectObject']
from typing import Callable
from panda3d.core import AsyncTask
from direct.directnotify.DirectNotifyGlobal import directNotify from direct.directnotify.DirectNotifyGlobal import directNotify
from direct.task.TaskManagerGlobal import taskMgr from direct.task.TaskManagerGlobal import taskMgr
from .MessengerGlobal import messenger from .MessengerGlobal import messenger
@ -12,6 +18,9 @@ class DirectObject:
""" """
This is the class that all Direct/SAL classes should inherit from This is the class that all Direct/SAL classes should inherit from
""" """
_MSGRmessengerId: tuple[str, int]
_taskList: dict[int, AsyncTask]
#def __del__(self): #def __del__(self):
# This next line is useful for debugging leaks # This next line is useful for debugging leaks
#print "Destructing: ", self.__class__.__name__ #print "Destructing: ", self.__class__.__name__
@ -19,16 +28,16 @@ class DirectObject:
# Wrapper functions to have a cleaner, more object oriented approach to # Wrapper functions to have a cleaner, more object oriented approach to
# the messenger functionality. # the messenger functionality.
def accept(self, event, method, extraArgs=[]): def accept(self, event: str, method: Callable, extraArgs: list = []) -> None:
return messenger.accept(event, self, method, extraArgs, 1) return messenger.accept(event, self, method, extraArgs, True)
def acceptOnce(self, event, method, extraArgs=[]): def acceptOnce(self, event, method, extraArgs=[]):
return messenger.accept(event, self, method, extraArgs, 0) return messenger.accept(event, self, method, extraArgs, 0)
def ignore(self, event): def ignore(self, event: str) -> None:
return messenger.ignore(event, self) return messenger.ignore(event, self)
def ignoreAll(self): def ignoreAll(self) -> None:
return messenger.ignoreAll(self) return messenger.ignoreAll(self)
def isAccepting(self, event): def isAccepting(self, event):
@ -41,7 +50,7 @@ class DirectObject:
return messenger.isIgnoring(event, self) return messenger.isIgnoring(event, self)
#This function must be used if you want a managed task #This function must be used if you want a managed task
def addTask(self, *args, **kwargs): def addTask(self, *args, **kwargs) -> AsyncTask:
if not hasattr(self, "_taskList"): if not hasattr(self, "_taskList"):
self._taskList = {} self._taskList = {}
kwargs['owner'] = self kwargs['owner'] = self

View File

@ -1,21 +1,33 @@
"""Contains the EventManager class. See :mod:`.EventManagerGlobal` for the """Contains the EventManager class. See :mod:`.EventManagerGlobal` for the
global eventMgr instance.""" global eventMgr instance."""
from __future__ import annotations
__all__ = ['EventManager'] __all__ = ['EventManager']
from typing import Any
from direct.directnotify.DirectNotifyGlobal import directNotify from direct.directnotify.DirectNotifyGlobal import directNotify
from direct.directnotify.Notifier import Notifier
from direct.task.TaskManagerGlobal import taskMgr from direct.task.TaskManagerGlobal import taskMgr
from direct.showbase.MessengerGlobal import messenger from direct.showbase.MessengerGlobal import messenger
from panda3d.core import PStatCollector, EventQueue, EventHandler from panda3d.core import (
from panda3d.core import ConfigVariableBool ConfigVariableBool,
Event,
EventHandler,
EventParameter,
EventQueue,
PStatCollector,
PythonTask,
)
class EventManager: class EventManager:
notify = None notify: Notifier | None = None
def __init__(self, eventQueue = None): def __init__(self, eventQueue: EventQueue | None = None) -> None:
""" """
Create a C++ event queue and handler Create a C++ event queue and handler
""" """
@ -24,11 +36,11 @@ class EventManager:
EventManager.notify = directNotify.newCategory("EventManager") EventManager.notify = directNotify.newCategory("EventManager")
self.eventQueue = eventQueue self.eventQueue = eventQueue
self.eventHandler = None self.eventHandler: EventHandler | None = None
self._wantPstats = ConfigVariableBool('pstats-eventmanager', False) self._wantPstats = ConfigVariableBool('pstats-eventmanager', False)
def doEvents(self): def doEvents(self) -> None:
""" """
Process all the events on the C++ event queue Process all the events on the C++ event queue
""" """
@ -38,12 +50,13 @@ class EventManager:
processFunc = self.processEventPstats processFunc = self.processEventPstats
else: else:
processFunc = self.processEvent processFunc = self.processEvent
assert self.eventQueue is not None
isEmptyFunc = self.eventQueue.isQueueEmpty isEmptyFunc = self.eventQueue.isQueueEmpty
dequeueFunc = self.eventQueue.dequeueEvent dequeueFunc = self.eventQueue.dequeueEvent
while not isEmptyFunc(): while not isEmptyFunc():
processFunc(dequeueFunc()) processFunc(dequeueFunc())
def eventLoopTask(self, task): def eventLoopTask(self, task: PythonTask) -> int:
""" """
Process all the events on the C++ event queue Process all the events on the C++ event queue
""" """
@ -51,7 +64,7 @@ class EventManager:
messenger.send("event-loop-done") messenger.send("event-loop-done")
return task.cont return task.cont
def parseEventParameter(self, eventParameter): def parseEventParameter(self, eventParameter: EventParameter) -> Any:
""" """
Extract the actual data from the eventParameter Extract the actual data from the eventParameter
""" """
@ -63,6 +76,8 @@ class EventManager:
return eventParameter.getStringValue() return eventParameter.getStringValue()
elif eventParameter.isWstring(): elif eventParameter.isWstring():
return eventParameter.getWstringValue() return eventParameter.getWstringValue()
elif hasattr(eventParameter, 'isBytes') and eventParameter.isBytes():
return eventParameter.getBytesValue()
elif eventParameter.isTypedRefCount(): elif eventParameter.isTypedRefCount():
return eventParameter.getTypedRefCountValue() return eventParameter.getTypedRefCountValue()
elif eventParameter.isEmpty(): elif eventParameter.isEmpty():
@ -72,7 +87,7 @@ class EventManager:
# which will be downcast to that type. # which will be downcast to that type.
return eventParameter.getPtr() return eventParameter.getPtr()
def processEvent(self, event): def processEvent(self, event: Event) -> None:
""" """
Process a C++ event Process a C++ event
Duplicate any changes in processEventPstats Duplicate any changes in processEventPstats
@ -89,6 +104,7 @@ class EventManager:
paramList.append(eventParameterData) paramList.append(eventParameterData)
# Do not print the new frame debug, it is too noisy! # Do not print the new frame debug, it is too noisy!
assert EventManager.notify is not None
if EventManager.notify.getDebug() and eventName != 'NewFrame': if EventManager.notify.getDebug() and eventName != 'NewFrame':
EventManager.notify.debug('received C++ event named: ' + eventName + EventManager.notify.debug('received C++ event named: ' + eventName +
' parameters: ' + repr(paramList)) ' parameters: ' + repr(paramList))
@ -106,9 +122,10 @@ class EventManager:
else: else:
# An unnamed event from C++ is probably a bad thing # An unnamed event from C++ is probably a bad thing
assert EventManager.notify is not None
EventManager.notify.warning('unnamed event in processEvent') EventManager.notify.warning('unnamed event in processEvent')
def processEventPstats(self, event): def processEventPstats(self, event: Event) -> None:
""" """
Process a C++ event with pstats tracking Process a C++ event with pstats tracking
Duplicate any changes in processEvent Duplicate any changes in processEvent
@ -125,6 +142,7 @@ class EventManager:
paramList.append(eventParameterData) paramList.append(eventParameterData)
# Do not print the new frame debug, it is too noisy! # Do not print the new frame debug, it is too noisy!
assert EventManager.notify is not None
if EventManager.notify.getDebug() and eventName != 'NewFrame': if EventManager.notify.getDebug() and eventName != 'NewFrame':
EventManager.notify.debug('received C++ event named: ' + eventName + EventManager.notify.debug('received C++ event named: ' + eventName +
' parameters: ' + repr(paramList)) ' parameters: ' + repr(paramList))
@ -156,9 +174,10 @@ class EventManager:
else: else:
# An unnamed event from C++ is probably a bad thing # An unnamed event from C++ is probably a bad thing
assert EventManager.notify is not None
EventManager.notify.warning('unnamed event in processEvent') EventManager.notify.warning('unnamed event in processEvent')
def restart(self): def restart(self) -> None:
if self.eventQueue is None: if self.eventQueue is None:
self.eventQueue = EventQueue.getGlobalEventQueue() self.eventQueue = EventQueue.getGlobalEventQueue()
@ -173,7 +192,7 @@ class EventManager:
taskMgr.add(self.eventLoopTask, 'eventManager') taskMgr.add(self.eventLoopTask, 'eventManager')
def shutdown(self): def shutdown(self) -> None:
taskMgr.remove('eventManager') taskMgr.remove('eventManager')
# Flush the event queue. We do this after removing the task # Flush the event queue. We do this after removing the task

View File

@ -35,7 +35,7 @@ def _varDump__init__(self, *args, **kArgs):
sReentry = 0 sReentry = 0
def _varDump__print(exc): def _varDump__print(exc) -> None:
global sReentry global sReentry
global notify global notify
if sReentry > 0: if sReentry > 0:
@ -176,7 +176,7 @@ def _excepthookDumpVars(eType, eValue, tb):
oldExcepthook(eType, eValue, origTb) oldExcepthook(eType, eValue, origTb)
def install(log, upload): def install(log: bool, upload: bool) -> None:
"""Installs the exception hook.""" """Installs the exception hook."""
global oldExcepthook global oldExcepthook
global wantStackDumpLog global wantStackDumpLog
@ -192,8 +192,8 @@ def install(log, upload):
# thrown by the interpreter don't get created until the # thrown by the interpreter don't get created until the
# stack has been unwound and an except block has been reached # stack has been unwound and an except block has been reached
if not hasattr(Exception, '_moved__init__'): if not hasattr(Exception, '_moved__init__'):
Exception._moved__init__ = Exception.__init__ Exception._moved__init__ = Exception.__init__ # type: ignore[attr-defined]
Exception.__init__ = _varDump__init__ Exception.__init__ = _varDump__init__ # type: ignore[method-assign]
else: else:
if sys.excepthook is not _excepthookDumpVars: if sys.excepthook is not _excepthookDumpVars:
oldExcepthook = sys.excepthook oldExcepthook = sys.excepthook

View File

@ -1,5 +1,7 @@
"""Contains utility classes for debugging memory leaks.""" """Contains utility classes for debugging memory leaks."""
from __future__ import annotations
__all__ = ['FakeObject', '_createGarbage', 'GarbageReport', 'GarbageLogger'] __all__ = ['FakeObject', '_createGarbage', 'GarbageReport', 'GarbageLogger']
from direct.directnotify.DirectNotifyGlobal import directNotify from direct.directnotify.DirectNotifyGlobal import directNotify
@ -10,6 +12,7 @@ from direct.showbase.JobManagerGlobal import jobMgr
from direct.showbase.MessengerGlobal import messenger from direct.showbase.MessengerGlobal import messenger
from panda3d.core import ConfigVariableBool from panda3d.core import ConfigVariableBool
import gc import gc
from collections.abc import Callable
GarbageCycleCountAnnounceEvent = 'announceGarbageCycleDesc2num' GarbageCycleCountAnnounceEvent = 'announceGarbageCycleDesc2num'
@ -41,9 +44,21 @@ class GarbageReport(Job):
If you just want to dump the report to the log, use GarbageLogger.""" If you just want to dump the report to the log, use GarbageLogger."""
notify = directNotify.newCategory("GarbageReport") notify = directNotify.newCategory("GarbageReport")
def __init__(self, name, log=True, verbose=False, fullReport=False, findCycles=True, def __init__(
threaded=False, doneCallback=None, autoDestroy=False, priority=None, self,
safeMode=False, delOnly=False, collect=True): name: str,
log: bool = True,
verbose: bool = False,
fullReport: bool = False,
findCycles: bool = True,
threaded: bool = False,
doneCallback: Callable[[GarbageReport], object] | None = None,
autoDestroy: bool = False,
priority: int | None = None,
safeMode: bool = False,
delOnly: bool = False,
collect: bool = True
) -> None:
# if autoDestroy is True, GarbageReport will self-destroy after logging # if autoDestroy is True, GarbageReport will self-destroy after logging
# if false, caller is responsible for calling destroy() # if false, caller is responsible for calling destroy()
# if threaded is True, processing will be performed over multiple frames # if threaded is True, processing will be performed over multiple frames
@ -399,7 +414,7 @@ class GarbageReport(Job):
if self._args.autoDestroy: if self._args.autoDestroy:
self.destroy() self.destroy()
def destroy(self): def destroy(self) -> None:
#print 'GarbageReport.destroy' #print 'GarbageReport.destroy'
del self._args del self._args
del self.garbage del self.garbage
@ -417,13 +432,13 @@ class GarbageReport(Job):
del self._reportStr del self._reportStr
Job.destroy(self) Job.destroy(self)
def getNumCycles(self): def getNumCycles(self) -> int:
# if the job hasn't run yet, we don't have a numCycles yet # if the job hasn't run yet, we don't have a numCycles yet
return self.numCycles return self.numCycles
def getDesc2numDict(self): def getDesc2numDict(self) -> dict[str, int]:
# dict of python-syntax leak -> number of that type of leak # dict of python-syntax leak -> number of that type of leak
desc2num = {} desc2num: dict[str, int] = {}
for cycleBySyntax in self.cyclesBySyntax: for cycleBySyntax in self.cyclesBySyntax:
desc2num.setdefault(cycleBySyntax, 0) desc2num.setdefault(cycleBySyntax, 0)
desc2num[cycleBySyntax] += 1 desc2num[cycleBySyntax] += 1
@ -563,7 +578,7 @@ class _CFGLGlobals:
LastNumCycles = 0 LastNumCycles = 0
def checkForGarbageLeaks(): def checkForGarbageLeaks() -> int:
gc.collect() gc.collect()
numGarbage = len(gc.garbage) numGarbage = len(gc.garbage)
if numGarbage > 0 and ConfigVariableBool('auto-garbage-logging', False): if numGarbage > 0 and ConfigVariableBool('auto-garbage-logging', False):
@ -576,6 +591,7 @@ def checkForGarbageLeaks():
messenger.send(GarbageCycleCountAnnounceEvent, [gr.getDesc2numDict()]) messenger.send(GarbageCycleCountAnnounceEvent, [gr.getDesc2numDict()])
gr.destroy() gr.destroy()
notify = directNotify.newCategory("GarbageDetect") notify = directNotify.newCategory("GarbageDetect")
func: Callable[[str], object]
if ConfigVariableBool('allow-garbage-cycles', True): if ConfigVariableBool('allow-garbage-cycles', True):
func = notify.warning func = notify.warning
else: else:
@ -584,7 +600,8 @@ def checkForGarbageLeaks():
return numGarbage return numGarbage
def b_checkForGarbageLeaks(wantReply=False): def b_checkForGarbageLeaks(wantReply: bool = False) -> int:
from direct.showbase.ShowBaseGlobal import base, __dev__
if not __dev__: if not __dev__:
return 0 return 0
# does a garbage collect on the client and the AI # does a garbage collect on the client and the AI
@ -592,10 +609,10 @@ def b_checkForGarbageLeaks(wantReply=False):
# logs leak info and terminates (if configured to do so) # logs leak info and terminates (if configured to do so)
try: try:
# if this is the client, tell the AI to check for leaks too # if this is the client, tell the AI to check for leaks too
base.cr.timeManager base.cr.timeManager # type: ignore[attr-defined]
except Exception: except Exception:
pass pass
else: else:
if base.cr.timeManager: if base.cr.timeManager: # type: ignore[attr-defined]
base.cr.timeManager.d_checkForGarbageLeaks(wantReply=wantReply) base.cr.timeManager.d_checkForGarbageLeaks(wantReply=wantReply) # type: ignore[attr-defined]
return checkForGarbageLeaks() return checkForGarbageLeaks()

View File

@ -1,3 +1,7 @@
from __future__ import annotations
from collections.abc import Iterator
from panda3d.core import ConfigVariableBool, ConfigVariableDouble, ClockObject from panda3d.core import ConfigVariableBool, ConfigVariableDouble, ClockObject
from direct.directnotify.DirectNotifyGlobal import directNotify from direct.directnotify.DirectNotifyGlobal import directNotify
from direct.task.TaskManagerGlobal import taskMgr from direct.task.TaskManagerGlobal import taskMgr
@ -17,28 +21,28 @@ class JobManager:
# there's one task for the JobManager, all jobs run in this task # there's one task for the JobManager, all jobs run in this task
TaskName = 'jobManager' TaskName = 'jobManager'
def __init__(self, timeslice=None): def __init__(self, timeslice: float | None = None) -> None:
# how long do we run per frame # how long do we run per frame
self._timeslice = timeslice self._timeslice = timeslice
# store the jobs in these structures to allow fast lookup by various keys # store the jobs in these structures to allow fast lookup by various keys
# priority -> jobId -> job # priority -> jobId -> job
self._pri2jobId2job = {} self._pri2jobId2job: dict[int, dict[int, Job]] = {}
# priority -> chronological list of jobIds # priority -> chronological list of jobIds
self._pri2jobIds = {} self._pri2jobIds: dict[int, list[int]] = {}
# jobId -> priority # jobId -> priority
self._jobId2pri = {} self._jobId2pri: dict[int, int] = {}
# how many timeslices to give each job; this is used to efficiently implement # how many timeslices to give each job; this is used to efficiently implement
# the relative job priorities # the relative job priorities
self._jobId2timeslices = {} self._jobId2timeslices: dict[int, int] = {}
# how much time did the job use beyond the allotted timeslice, used to balance # how much time did the job use beyond the allotted timeslice, used to balance
# out CPU usage # out CPU usage
self._jobId2overflowTime = {} self._jobId2overflowTime: dict[int, float] = {}
self._useOverflowTime = None self._useOverflowTime: bool | None = None
# this is a generator that we use to give high-priority jobs more timeslices, # this is a generator that we use to give high-priority jobs more timeslices,
# it yields jobIds in a sequence that includes high-priority jobIds more often # it yields jobIds in a sequence that includes high-priority jobIds more often
# than low-priority # than low-priority
self._jobIdGenerator = None self._jobIdGenerator: Iterator[int] | None = None
self._highestPriority = Job.Priorities.Normal self._highestPriority: int = Job.Priorities.Normal # type: ignore[attr-defined]
def destroy(self): def destroy(self):
taskMgr.remove(JobManager.TaskName) taskMgr.remove(JobManager.TaskName)

View File

@ -2,9 +2,13 @@
sound, music, shaders and fonts from disk. sound, music, shaders and fonts from disk.
""" """
from __future__ import annotations
__all__ = ['Loader'] __all__ = ['Loader']
from panda3d.core import ( from panda3d.core import (
AsyncTask,
AudioManager,
ConfigVariableBool, ConfigVariableBool,
Filename, Filename,
FontPool, FontPool,
@ -13,6 +17,7 @@ from panda3d.core import (
ModelFlattenRequest, ModelFlattenRequest,
ModelNode, ModelNode,
ModelPool, ModelPool,
MovieAudio,
NodePath, NodePath,
PandaNode, PandaNode,
SamplerState, SamplerState,
@ -24,13 +29,20 @@ from panda3d.core import (
from panda3d.core import Loader as PandaLoader from panda3d.core import Loader as PandaLoader
from direct.directnotify.DirectNotifyGlobal import directNotify from direct.directnotify.DirectNotifyGlobal import directNotify
from direct.showbase.DirectObject import DirectObject from direct.showbase.DirectObject import DirectObject
from . import ShowBase
import warnings import warnings
import sys import sys
import os
from typing import Any, Callable, Iterable, Union
# Type aliases for loadModel, loadSound, etc. annotations
_ModelPath = Union[str, Filename, os.PathLike]
_SoundPath = Union[str, Filename, os.PathLike, MovieAudio]
# You can specify a phaseChecker callback to check # You can specify a phaseChecker callback to check
# a modelPath to see if it is being loaded in the correct # a modelPath to see if it is being loaded in the correct
# phase # phase
phaseChecker = None phaseChecker: Callable[[_ModelPath, LoaderOptions], object] | None = None
class Loader(DirectObject): class Loader(DirectObject):
@ -49,16 +61,23 @@ class Loader(DirectObject):
# This indicates that this class behaves like a Future. # This indicates that this class behaves like a Future.
_asyncio_future_blocking = False _asyncio_future_blocking = False
def __init__(self, loader, numObjects, gotList, callback, extraArgs): def __init__(
self,
loader: Loader | None,
numObjects: int,
gotList: bool,
callback: Callable[..., object] | None,
extraArgs: list,
) -> None:
self._loader = loader self._loader = loader
self.objects = [None] * numObjects self.objects: list[Any | None] = [None] * numObjects
self.gotList = gotList self.gotList = gotList
self.callback = callback self.callback = callback
self.extraArgs = extraArgs self.extraArgs = extraArgs
self.requests = set() self.requests: set[AsyncTask] = set()
self.requestList = [] self.requestList: list[AsyncTask] = []
def gotObject(self, index, object): def gotObject(self, index: int, object) -> None:
self.objects[index] = object self.objects[index] = object
if not self.requests: if not self.requests:
@ -79,7 +98,7 @@ class Loader(DirectObject):
self.requests = None self.requests = None
self.requestList = None self.requestList = None
def cancelled(self): def cancelled(self) -> bool:
"Returns true if the request was cancelled." "Returns true if the request was cancelled."
return self.requestList is None return self.requestList is None
@ -87,7 +106,7 @@ class Loader(DirectObject):
"Returns true if all the requests were finished or cancelled." "Returns true if all the requests were finished or cancelled."
return not self.requests return not self.requests
def result(self): def result(self) -> Any:
"Returns the results, suspending the thread to wait if necessary." "Returns the results, suspending the thread to wait if necessary."
for r in list(self.requests): for r in list(self.requests):
r.wait() r.wait()
@ -126,26 +145,26 @@ class Loader(DirectObject):
yield await req yield await req
# special methods # special methods
def __init__(self, base): def __init__(self, base: ShowBase.ShowBase | None = None) -> None:
self.base = base self.base = base
self.loader = PandaLoader.getGlobalPtr() self.loader = PandaLoader.getGlobalPtr()
self._requests = {} self._requests: dict[AsyncTask, tuple[Loader._Callback, int]] = {}
self.hook = "async_loader_%s" % (Loader.loaderIndex) self.hook = "async_loader_%s" % (Loader.loaderIndex)
Loader.loaderIndex += 1 Loader.loaderIndex += 1
self.accept(self.hook, self.__gotAsyncObject)
self._loadPythonFileTypes() def destroy(self) -> None:
def destroy(self):
self.ignore(self.hook) self.ignore(self.hook)
self.loader.stopThreads() self.loader.stopThreads()
del self.base del self.base
del self.loader
def _init_base(self, base: ShowBase.ShowBase) -> None:
self.base = base
self.accept(self.hook, self.__gotAsyncObject)
@classmethod @classmethod
def _loadPythonFileTypes(cls): def _loadPythonFileTypes(cls) -> None:
if cls._loadedPythonFileTypes: if cls._loadedPythonFileTypes:
return return
@ -168,10 +187,18 @@ class Loader(DirectObject):
cls._loadedPythonFileTypes = True cls._loadedPythonFileTypes = True
# model loading funcs # model loading funcs
def loadModel(self, modelPath, loaderOptions = None, noCache = None, def loadModel(
allowInstance = False, okMissing = None, self,
callback = None, extraArgs = [], priority = None, modelPath: _ModelPath | list[_ModelPath] | tuple[_ModelPath, ...] | set[_ModelPath],
blocking = None): loaderOptions: LoaderOptions | None = None,
noCache: bool | None = None,
allowInstance: bool = False,
okMissing: bool | None = None,
callback: Callable[..., object] | None = None,
extraArgs: list = [],
priority: int | None = None,
blocking: bool | None = None,
) -> Any:
""" """
Attempts to load a model or models from one or more relative Attempts to load a model or models from one or more relative
pathnames. If the input modelPath is a string (a single model pathnames. If the input modelPath is a string (a single model
@ -229,6 +256,10 @@ class Loader(DirectObject):
""" """
assert Loader.notify.debug("Loading model: %s" % (modelPath,)) assert Loader.notify.debug("Loading model: %s" % (modelPath,))
if not self._loadedPythonFileTypes:
self._loadPythonFileTypes()
if loaderOptions is None: if loaderOptions is None:
loaderOptions = LoaderOptions() loaderOptions = LoaderOptions()
else: else:
@ -251,6 +282,7 @@ class Loader(DirectObject):
if allowInstance: if allowInstance:
loaderOptions.setFlags(loaderOptions.getFlags() | LoaderOptions.LFAllowInstance) loaderOptions.setFlags(loaderOptions.getFlags() | LoaderOptions.LFAllowInstance)
modelList: Iterable[_ModelPath]
if not isinstance(modelPath, (tuple, list, set)): if not isinstance(modelPath, (tuple, list, set)):
# We were given a single model pathname. # We were given a single model pathname.
modelList = [modelPath] modelList = [modelPath]
@ -416,6 +448,9 @@ class Loader(DirectObject):
a callback is used, the model is saved asynchronously, and the a callback is used, the model is saved asynchronously, and the
true/false status is passed to the callback function. """ true/false status is passed to the callback function. """
if not self._loadedPythonFileTypes:
self._loadPythonFileTypes()
if loaderOptions is None: if loaderOptions is None:
loaderOptions = LoaderOptions() loaderOptions = LoaderOptions()
else: else:
@ -942,7 +977,13 @@ class Loader(DirectObject):
TexturePool.releaseTexture(texture) TexturePool.releaseTexture(texture)
# sound loading funcs # sound loading funcs
def loadSfx(self, *args, **kw): def loadSfx(
self,
soundPath: _SoundPath | tuple[_SoundPath, ...] | list[_SoundPath] | set[_SoundPath],
positional: bool = False,
callback: Callable[..., object] | None = None,
extraArgs: list = [],
) -> Any:
"""Loads one or more sound files, specifically designated as a """Loads one or more sound files, specifically designated as a
"sound effect" file (that is, uses the sfxManager to load the "sound effect" file (that is, uses the sfxManager to load the
sound). There is no distinction between sound effect files sound). There is no distinction between sound effect files
@ -952,11 +993,18 @@ class Loader(DirectObject):
independently of the other group.""" independently of the other group."""
# showbase-created sfxManager should always be at front of list # showbase-created sfxManager should always be at front of list
assert self.base is not None
if self.base.sfxManagerList: if self.base.sfxManagerList:
return self.loadSound(self.base.sfxManagerList[0], *args, **kw) return self.loadSound(self.base.sfxManagerList[0], soundPath, positional, callback, extraArgs)
return None return None
def loadMusic(self, *args, **kw): def loadMusic(
self,
soundPath: _SoundPath | tuple[_SoundPath, ...] | list[_SoundPath] | set[_SoundPath],
positional: bool = False,
callback: Callable[..., object] | None = None,
extraArgs: list = [],
) -> Any:
"""Loads one or more sound files, specifically designated as a """Loads one or more sound files, specifically designated as a
"music" file (that is, uses the musicManager to load the "music" file (that is, uses the musicManager to load the
sound). There is no distinction between sound effect files sound). There is no distinction between sound effect files
@ -964,13 +1012,20 @@ class Loader(DirectObject):
to load the sound file, but this distinction allows the sound to load the sound file, but this distinction allows the sound
effects and/or the music files to be adjusted as a group, effects and/or the music files to be adjusted as a group,
independently of the other group.""" independently of the other group."""
assert self.base is not None
if self.base.musicManager: if self.base.musicManager:
return self.loadSound(self.base.musicManager, *args, **kw) return self.loadSound(self.base.musicManager, soundPath, positional, callback, extraArgs)
else: else:
return None return None
def loadSound(self, manager, soundPath, positional = False, def loadSound(
callback = None, extraArgs = []): self,
manager: AudioManager,
soundPath: _SoundPath | tuple[_SoundPath, ...] | list[_SoundPath] | set[_SoundPath],
positional: bool = False,
callback: Callable[..., object] | None = None,
extraArgs: list = [],
) -> Any:
"""Loads one or more sound files, specifying the particular """Loads one or more sound files, specifying the particular
AudioManager that should be used to load them. The soundPath AudioManager that should be used to load them. The soundPath
may be either a single filename, or a list of filenames. If a may be either a single filename, or a list of filenames. If a
@ -980,6 +1035,7 @@ class Loader(DirectObject):
from panda3d.core import AudioLoadRequest from panda3d.core import AudioLoadRequest
soundList: Iterable[_SoundPath]
if not isinstance(soundPath, (tuple, list, set)): if not isinstance(soundPath, (tuple, list, set)):
# We were given a single sound pathname or a MovieAudio instance. # We were given a single sound pathname or a MovieAudio instance.
soundList = [soundPath] soundList = [soundPath]
@ -1105,7 +1161,7 @@ class Loader(DirectObject):
else: else:
callback(*(origModelList + extraArgs)) callback(*(origModelList + extraArgs))
def __gotAsyncObject(self, request): def __gotAsyncObject(self, request: AsyncTask) -> None:
"""A model or sound file or some such thing has just been """A model or sound file or some such thing has just been
loaded asynchronously by the sub-thread. Add it to the list loaded asynchronously by the sub-thread. Add it to the list
of loaded objects, and call the appropriate callback when it's of loaded objects, and call the appropriate callback when it's

View File

@ -2,19 +2,40 @@
:ref:`event handling <event-handlers>` that happens on the Python side. :ref:`event handling <event-handlers>` that happens on the Python side.
""" """
from __future__ import annotations
__all__ = ['Messenger'] __all__ = ['Messenger']
import types
from collections.abc import Callable
from typing import Protocol
# These can be replaced with their builtin counterparts once support for Python 3.8 is dropped.
from typing import Dict, Tuple
from panda3d.core import AsyncTask
from direct.stdpy.threading import Lock from direct.stdpy.threading import Lock
from direct.directnotify import DirectNotifyGlobal from direct.directnotify import DirectNotifyGlobal
from .PythonUtil import safeRepr from .PythonUtil import safeRepr
import types
# The following variables are typing constructs used in annotations
# to succinctly express complex type structures.
_ObjMsgrId = Tuple[str, int]
_CallbackInfo = list # [Callable, list, bool]
_ListenerObject = list # [int, DirectObject]
_AcceptorDict = Dict[_ObjMsgrId, _CallbackInfo]
_EventTuple = Tuple[_AcceptorDict, str, list, bool]
class _HasMessengerID(Protocol):
_MSGRmessengerId: _ObjMsgrId
class Messenger: class Messenger:
notify = DirectNotifyGlobal.directNotify.newCategory("Messenger") notify = DirectNotifyGlobal.directNotify.newCategory("Messenger")
def __init__(self): def __init__(self) -> None:
""" """
One is keyed off the event name. It has the following structure:: One is keyed off the event name. It has the following structure::
@ -34,16 +55,16 @@ class Messenger:
{'mouseDown': {avatar: [avatar.jump, [2.0], 1]}} {'mouseDown': {avatar: [avatar.jump, [2.0], 1]}}
""" """
# eventName->objMsgrId->callbackInfo # eventName->objMsgrId->callbackInfo
self.__callbacks = {} self.__callbacks: dict[str, _AcceptorDict] = {}
# objMsgrId->set(eventName) # objMsgrId->set(eventName)
self.__objectEvents = {} self.__objectEvents: dict[_ObjMsgrId, dict[str, None]] = {}
self._messengerIdGen = 0 self._messengerIdGen = 0
# objMsgrId->listenerObject # objMsgrId->listenerObject
self._id2object = {} self._id2object: dict[_ObjMsgrId, _ListenerObject] = {}
# A mapping of taskChain -> eventList, used for sending events # A mapping of taskChain -> eventList, used for sending events
# across task chains (and therefore across threads). # across task chains (and therefore across threads).
self._eventQueuesByTaskChain = {} self._eventQueuesByTaskChain: dict[str, list[_EventTuple]] = {}
# This protects the data structures within this object from # This protects the data structures within this object from
# multithreaded access. # multithreaded access.
@ -51,7 +72,7 @@ class Messenger:
if __debug__: if __debug__:
self.__isWatching=0 self.__isWatching=0
self.__watching={} self.__watching: dict[str, bool] = {}
# I'd like this to be in the __debug__, but I fear that someone will # I'd like this to be in the __debug__, but I fear that someone will
# want this in a release build. If you're sure that that will not be # want this in a release build. If you're sure that that will not be
# then please remove this comment and put the quiet/verbose stuff # then please remove this comment and put the quiet/verbose stuff
@ -62,7 +83,7 @@ class Messenger:
'collisionLoopFinished':1, 'collisionLoopFinished':1,
} # see def quiet() } # see def quiet()
def _getMessengerId(self, object): def _getMessengerId(self, object: _HasMessengerID) -> _ObjMsgrId:
# TODO: allocate this id in DirectObject.__init__ and get derived # TODO: allocate this id in DirectObject.__init__ and get derived
# classes to call down (speed optimization, assuming objects # classes to call down (speed optimization, assuming objects
# accept/ignore more than once over their lifetime) # accept/ignore more than once over their lifetime)
@ -73,7 +94,7 @@ class Messenger:
self._messengerIdGen += 1 self._messengerIdGen += 1
return object._MSGRmessengerId return object._MSGRmessengerId
def _storeObject(self, object): def _storeObject(self, object: _HasMessengerID) -> None:
# store reference-counted reference to object in case we need to # store reference-counted reference to object in case we need to
# retrieve it later. assumes lock is held. # retrieve it later. assumes lock is held.
id = self._getMessengerId(object) id = self._getMessengerId(object)
@ -82,7 +103,7 @@ class Messenger:
else: else:
self._id2object[id][0] += 1 self._id2object[id][0] += 1
def _getObject(self, id): def _getObject(self, id: _ObjMsgrId) -> _HasMessengerID:
return self._id2object[id][1] return self._id2object[id][1]
def _getObjects(self): def _getObjects(self):
@ -101,7 +122,7 @@ class Messenger:
def _getEvents(self): def _getEvents(self):
return list(self.__callbacks.keys()) return list(self.__callbacks.keys())
def _releaseObject(self, object): def _releaseObject(self, object: _HasMessengerID) -> None:
# assumes lock is held. # assumes lock is held.
id = self._getMessengerId(object) id = self._getMessengerId(object)
if id in self._id2object: if id in self._id2object:
@ -117,7 +138,14 @@ class Messenger:
from .EventManagerGlobal import eventMgr from .EventManagerGlobal import eventMgr
return eventMgr.eventHandler.get_future(event) return eventMgr.eventHandler.get_future(event)
def accept(self, event, object, method, extraArgs=[], persistent=1): def accept(
self,
event: str,
object: _HasMessengerID,
method: Callable,
extraArgs: list = [],
persistent: bool = True,
) -> None:
""" accept(self, string, DirectObject, Function, List, Boolean) """ accept(self, string, DirectObject, Function, List, Boolean)
Make this object accept this event. When the event is Make this object accept this event. When the event is
@ -174,7 +202,7 @@ class Messenger:
finally: finally:
self.lock.release() self.lock.release()
def ignore(self, event, object): def ignore(self, event: str, object: _HasMessengerID) -> None:
""" ignore(self, string, DirectObject) """ ignore(self, string, DirectObject)
Make this object no longer respond to this event. Make this object no longer respond to this event.
It is safe to call even if it was not already accepting It is safe to call even if it was not already accepting
@ -208,7 +236,7 @@ class Messenger:
finally: finally:
self.lock.release() self.lock.release()
def ignoreAll(self, object): def ignoreAll(self, object: _HasMessengerID) -> None:
""" """
Make this object no longer respond to any events it was accepting Make this object no longer respond to any events it was accepting
Useful for cleanup Useful for cleanup
@ -283,7 +311,7 @@ class Messenger:
""" """
return not self.isAccepting(event, object) return not self.isAccepting(event, object)
def send(self, event, sentArgs=[], taskChain=None): def send(self, event: str, sentArgs: list = [], taskChain: str | None = None) -> None:
""" """
Send this event, optionally passing in arguments. Send this event, optionally passing in arguments.
@ -305,12 +333,12 @@ class Messenger:
self.lock.acquire() self.lock.acquire()
try: try:
foundWatch = 0 foundWatch = False
if __debug__: if __debug__:
if self.__isWatching: if self.__isWatching:
for i in self.__watching: for i in self.__watching:
if str(event).find(i) >= 0: if str(event).find(i) >= 0:
foundWatch = 1 foundWatch = True
break break
acceptorDict = self.__callbacks.get(event) acceptorDict = self.__callbacks.get(event)
if not acceptorDict: if not acceptorDict:
@ -336,7 +364,7 @@ class Messenger:
finally: finally:
self.lock.release() self.lock.release()
def __taskChainDispatch(self, taskChain, task): def __taskChainDispatch(self, taskChain: str, task: AsyncTask) -> int:
""" This task is spawned each time an event is sent across """ This task is spawned each time an event is sent across
task chains. Its job is to empty the task events on the queue task chains. Its job is to empty the task events on the queue
for this particular task chain. This guarantees that events for this particular task chain. This guarantees that events
@ -365,7 +393,13 @@ class Messenger:
return task.done return task.done
def __dispatch(self, acceptorDict, event, sentArgs, foundWatch): def __dispatch(
self,
acceptorDict: _AcceptorDict,
event: str,
sentArgs: list,
foundWatch: bool,
) -> None:
for id in list(acceptorDict.keys()): for id in list(acceptorDict.keys()):
# We have to make this apparently redundant check, because # We have to make this apparently redundant check, because
# it is possible that one object removes its own hooks # it is possible that one object removes its own hooks
@ -562,7 +596,7 @@ class Messenger:
break break
return matches return matches
def __methodRepr(self, method): def __methodRepr(self, method: object) -> str:
""" """
return string version of class.method or method. return string version of class.method or method.
""" """

View File

@ -1,7 +1,11 @@
"""Contains the OnScreenDebug class.""" """Contains the OnScreenDebug class."""
from __future__ import annotations
__all__ = ['OnScreenDebug'] __all__ = ['OnScreenDebug']
from typing import Any
from panda3d.core import ( from panda3d.core import (
ConfigVariableBool, ConfigVariableBool,
ConfigVariableDouble, ConfigVariableDouble,
@ -18,13 +22,13 @@ class OnScreenDebug:
enabled = ConfigVariableBool("on-screen-debug-enabled", False) enabled = ConfigVariableBool("on-screen-debug-enabled", False)
def __init__(self): def __init__(self) -> None:
self.onScreenText = None self.onScreenText: OnscreenText.OnscreenText | None = None
self.frame = 0 self.frame = 0
self.text = "" self.text = ""
self.data = {} self.data: dict[str, tuple[int, Any]] = {}
def load(self): def load(self) -> None:
if self.onScreenText: if self.onScreenText:
return return
@ -40,6 +44,7 @@ class OnScreenDebug:
fgColor.setW(ConfigVariableDouble("on-screen-debug-fg-alpha", 0.85).value) fgColor.setW(ConfigVariableDouble("on-screen-debug-fg-alpha", 0.85).value)
bgColor.setW(ConfigVariableDouble("on-screen-debug-bg-alpha", 0.85).value) bgColor.setW(ConfigVariableDouble("on-screen-debug-bg-alpha", 0.85).value)
from direct.showbase.ShowBaseGlobal import base
font = base.loader.loadFont(fontPath) font = base.loader.loadFont(fontPath)
if not font.isValid(): if not font.isValid():
print("failed to load OnScreenDebug font %s" % fontPath) print("failed to load OnScreenDebug font %s" % fontPath)
@ -47,15 +52,16 @@ class OnScreenDebug:
self.onScreenText = OnscreenText.OnscreenText( self.onScreenText = OnscreenText.OnscreenText(
parent = base.a2dTopLeft, pos = (0.0, -0.1), parent = base.a2dTopLeft, pos = (0.0, -0.1),
fg=fgColor, bg=bgColor, scale = (fontScale, fontScale, 0.0), fg=fgColor, bg=bgColor, scale = (fontScale, fontScale, 0.0),
align = TextNode.ALeft, mayChange = 1, font = font) align = TextNode.ALeft, mayChange = True, font = font)
# Make sure readout is never lit or drawn in wireframe # Make sure readout is never lit or drawn in wireframe
DirectUtil.useDirectRenderStyle(self.onScreenText) DirectUtil.useDirectRenderStyle(self.onScreenText)
def render(self): def render(self) -> None:
if not self.enabled: if not self.enabled:
return return
if not self.onScreenText: if not self.onScreenText:
self.load() self.load()
assert self.onScreenText is not None
self.onScreenText.clearText() self.onScreenText.clearText()
for k, v in sorted(self.data.items()): for k, v in sorted(self.data.items()):
if v[0] == self.frame: if v[0] == self.frame:
@ -75,7 +81,7 @@ class OnScreenDebug:
self.onScreenText.appendText(self.text) self.onScreenText.appendText(self.text)
self.frame += 1 self.frame += 1
def clear(self): def clear(self) -> None:
self.text = "" self.text = ""
if self.onScreenText: if self.onScreenText:
self.onScreenText.clearText() self.onScreenText.clearText()

View File

@ -41,13 +41,17 @@ import time
import builtins import builtins
import importlib import importlib
import functools import functools
from typing import Callable from collections.abc import Callable, Container, Iterable, Mapping
from typing import Any, Generic, TypeVar
__report_indent = 3 __report_indent = 3
from panda3d.core import ConfigVariableBool, ConfigVariableString, ConfigFlags from panda3d.core import ConfigVariableBool, ConfigVariableString, ConfigFlags
from panda3d.core import ClockObject from panda3d.core import ClockObject
_T = TypeVar('_T')
_KT = TypeVar('_KT')
_VT = TypeVar('_VT')
## with one integer positional arg, this uses about 4/5 of the memory of the Functor class below ## with one integer positional arg, this uses about 4/5 of the memory of the Functor class below
#def Functor(function, *args, **kArgs): #def Functor(function, *args, **kArgs):
@ -100,9 +104,9 @@ class Functor:
return s return s
class Stack: class Stack(Generic[_T]):
def __init__(self): def __init__(self) -> None:
self.__list = [] self.__list: list[_T] = []
def push(self, item): def push(self, item):
self.__list.append(item) self.__list.append(item)
@ -229,7 +233,7 @@ if __debug__:
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
def traceFunctionCall(frame): def traceFunctionCall(frame: types.FrameType) -> str:
""" """
return a string that shows the call frame with calling arguments. return a string that shows the call frame with calling arguments.
e.g. e.g.
@ -270,7 +274,7 @@ if __debug__:
r+="*** undefined ***" r+="*** undefined ***"
return r+')' return r+')'
def traceParentCall(): def traceParentCall() -> str:
return traceFunctionCall(sys._getframe(2)) return traceFunctionCall(sys._getframe(2))
def printThisCall(): def printThisCall():
@ -411,7 +415,7 @@ def list2dict(L, value=None):
return dict([(k, value) for k in L]) return dict([(k, value) for k in L])
def listToIndex2item(L): def listToIndex2item(L: Iterable[_VT]) -> dict[int, _VT]:
"""converts list to dict of list index->list item""" """converts list to dict of list index->list item"""
d = {} d = {}
for i, item in enumerate(L): for i, item in enumerate(L):
@ -422,7 +426,7 @@ def listToIndex2item(L):
assert listToIndex2item(['a','b']) == {0: 'a', 1: 'b',} assert listToIndex2item(['a','b']) == {0: 'a', 1: 'b',}
def listToItem2index(L): def listToItem2index(L: Iterable[_KT]) -> dict[_KT, int]:
"""converts list to dict of list item->list index """converts list to dict of list item->list index
This is lossy if there are duplicate list items""" This is lossy if there are duplicate list items"""
d = {} d = {}
@ -451,7 +455,7 @@ def invertDict(D, lossy=False):
return n return n
def invertDictLossless(D): def invertDictLossless(D: Mapping[_KT, _VT]) -> dict[_VT, list[_KT]]:
"""similar to invertDict, but values of new dict are lists of keys from """similar to invertDict, but values of new dict are lists of keys from
old dict. No information is lost. old dict. No information is lost.
@ -459,7 +463,7 @@ def invertDictLossless(D):
>>> invertDictLossless(old) >>> invertDictLossless(old)
{1: ['key1'], 2: ['key2', 'keyA']} {1: ['key1'], 2: ['key2', 'keyA']}
""" """
n = {} n: dict[_VT, list[_KT]] = {}
for key, value in D.items(): for key, value in D.items():
n.setdefault(value, []) n.setdefault(value, [])
n[value].append(key) n[value].append(key)
@ -706,7 +710,7 @@ if __debug__:
movedDumpFuncs: list[Callable] = [] movedDumpFuncs: list[Callable] = []
movedLoadFuncs: list[Callable] = [] movedLoadFuncs: list[Callable] = []
profileFilenames = set() profileFilenames = set()
profileFilenameList = Stack() profileFilenameList = Stack[str]()
profileFilename2file = {} profileFilename2file = {}
profileFilename2marshalData = {} profileFilename2marshalData = {}
@ -998,7 +1002,7 @@ def lineupPos(i, num, spacing):
return pos - ((float(spacing) * (num-1))/2.) return pos - ((float(spacing) * (num-1))/2.)
def formatElapsedSeconds(seconds): def formatElapsedSeconds(seconds: float) -> str:
""" """
Returns a string of the form "mm:ss" or "hh:mm:ss" or "n days", Returns a string of the form "mm:ss" or "hh:mm:ss" or "n days",
representing the indicated elapsed time in seconds. representing the indicated elapsed time in seconds.
@ -1278,7 +1282,7 @@ def randInt32(rng=random.random):
class SerialNumGen: class SerialNumGen:
"""generates serial numbers""" """generates serial numbers"""
def __init__(self, start=None): def __init__(self, start: int | None = None) -> None:
if start is None: if start is None:
start = 0 start = 0
self.__counter = start-1 self.__counter = start-1
@ -1305,7 +1309,7 @@ class SerialMaskedGen(SerialNumGen):
_serialGen = SerialNumGen() _serialGen = SerialNumGen()
def serialNum(): def serialNum() -> int:
return _serialGen.next() return _serialGen.next()
@ -1404,14 +1408,16 @@ def _getSafeReprNotify():
return safeReprNotify return safeReprNotify
def safeRepr(obj): def safeRepr(obj: object) -> str:
global dtoolSuperBase global dtoolSuperBase
if dtoolSuperBase is None: if dtoolSuperBase is None:
_getDtoolSuperBase() _getDtoolSuperBase()
assert dtoolSuperBase is not None
global safeReprNotify global safeReprNotify
if safeReprNotify is None: if safeReprNotify is None:
_getSafeReprNotify() _getSafeReprNotify()
assert safeReprNotify is not None
if isinstance(obj, dtoolSuperBase): if isinstance(obj, dtoolSuperBase):
# repr of C++ object could crash, particularly if the object has been deleted # repr of C++ object could crash, particularly if the object has been deleted
@ -1443,7 +1449,12 @@ def safeReprTypeOnFail(obj):
return '<** FAILED REPR OF %s instance at %s **>' % (obj.__class__.__name__, hex(id(obj))) return '<** FAILED REPR OF %s instance at %s **>' % (obj.__class__.__name__, hex(id(obj)))
def fastRepr(obj, maxLen=200, strFactor=10, _visitedIds=None): def fastRepr(
obj: object,
maxLen: int = 200,
strFactor: int = 10,
_visitedIds: set[int] | None = None,
) -> str:
""" caps the length of iterable types, so very large objects will print faster. """ caps the length of iterable types, so very large objects will print faster.
also prevents infinite recursion """ also prevents infinite recursion """
try: try:
@ -1452,9 +1463,9 @@ def fastRepr(obj, maxLen=200, strFactor=10, _visitedIds=None):
if id(obj) in _visitedIds: if id(obj) in _visitedIds:
return '<ALREADY-VISITED %s>' % itype(obj) return '<ALREADY-VISITED %s>' % itype(obj)
if type(obj) in (tuple, list): if type(obj) in (tuple, list):
assert isinstance(obj, (tuple, list))
s = '' s = ''
s += {tuple: '(', s += '(' if type(obj) == tuple else '['
list: '[',}[type(obj)]
if maxLen is not None and len(obj) > maxLen: if maxLen is not None and len(obj) > maxLen:
o = obj[:maxLen] o = obj[:maxLen]
ellips = '...' ellips = '...'
@ -1467,8 +1478,7 @@ def fastRepr(obj, maxLen=200, strFactor=10, _visitedIds=None):
s += ', ' s += ', '
_visitedIds.remove(id(obj)) _visitedIds.remove(id(obj))
s += ellips s += ellips
s += {tuple: ')', s += ')' if type(obj) == tuple else ']'
list: ']',}[type(obj)]
return s return s
elif type(obj) is dict: elif type(obj) is dict:
s = '{' s = '{'
@ -1588,7 +1598,7 @@ class ScratchPad:
class Sync: class Sync:
_SeriesGen = SerialNumGen() _SeriesGen = SerialNumGen()
def __init__(self, name, other=None): def __init__(self, name: str, other: Sync | None = None) -> None:
self._name = name self._name = name
if other is None: if other is None:
self._series = self._SeriesGen.next() self._series = self._SeriesGen.next()
@ -1620,7 +1630,7 @@ class Sync:
self._name, self._series, self._value) self._name, self._series, self._value)
def itype(obj): def itype(obj: object) -> type | str:
# version of type that gives more complete information about instance types # version of type that gives more complete information about instance types
global dtoolSuperBase global dtoolSuperBase
t = type(obj) t = type(obj)
@ -1628,6 +1638,7 @@ def itype(obj):
# check if this is a C++ object # check if this is a C++ object
if dtoolSuperBase is None: if dtoolSuperBase is None:
_getDtoolSuperBase() _getDtoolSuperBase()
assert dtoolSuperBase is not None
if isinstance(obj, dtoolSuperBase): if isinstance(obj, dtoolSuperBase):
return "<type 'instance' of %s>" % (obj.__class__) return "<type 'instance' of %s>" % (obj.__class__)
return t return t
@ -1972,7 +1983,13 @@ def pstatcollect(scope, level = None):
__report_indent = 0 __report_indent = 0
def report(types = [], prefix = '', xform = None, notifyFunc = None, dConfigParam = []): def report(
types: Container[str] = [],
prefix: str = '',
xform: Callable[[Any], object] | None = None,
notifyFunc: Callable[[str], object] | None = None,
dConfigParam: str | list[str] | tuple[str, ...] = [],
) -> Callable[[_T], _T]:
""" """
This is a decorator generating function. Use is similar to This is a decorator generating function. Use is similar to
a @decorator, except you must be sure to call it as a function. a @decorator, except you must be sure to call it as a function.
@ -2031,7 +2048,7 @@ def report(types = [], prefix = '', xform = None, notifyFunc = None, dConfigPara
return f return f
try: try:
if not __dev__ and not ConfigVariableBool('force-reports', False): if not __dev__ and not ConfigVariableBool('force-reports', False): # type: ignore[name-defined]
return decorator return decorator
# determine whether we should use the decorator # determine whether we should use the decorator
@ -2041,6 +2058,7 @@ def report(types = [], prefix = '', xform = None, notifyFunc = None, dConfigPara
if not dConfigParam: if not dConfigParam:
doPrint = True doPrint = True
else: else:
dConfigParams: list[str] | tuple[str, ...]
if not isinstance(dConfigParam, (list,tuple)): if not isinstance(dConfigParam, (list,tuple)):
dConfigParams = (dConfigParam,) dConfigParams = (dConfigParam,)
else: else:
@ -2070,7 +2088,7 @@ def report(types = [], prefix = '', xform = None, notifyFunc = None, dConfigPara
globalClockDelta = importlib.import_module("direct.distributed.ClockDelta").globalClockDelta globalClockDelta = importlib.import_module("direct.distributed.ClockDelta").globalClockDelta
def decorator(f): def decorator(f): # type: ignore[no-redef]
def wrap(*args, **kwargs): def wrap(*args, **kwargs):
if args: if args:
rArgs = [args[0].__class__.__name__ + ', '] rArgs = [args[0].__class__.__name__ + ', ']

View File

@ -62,6 +62,7 @@ from panda3d.core import (
DepthTestAttrib, DepthTestAttrib,
DepthWriteAttrib, DepthWriteAttrib,
DriveInterface, DriveInterface,
EventQueue,
ExecutionEnvironment, ExecutionEnvironment,
Filename, Filename,
FisheyeMaker, FisheyeMaker,
@ -114,7 +115,7 @@ from panda3d.core import (
WindowProperties, WindowProperties,
getModelPath, getModelPath,
) )
from panda3d.direct import throw_new_frame, init_app_for_gui from panda3d.direct import init_app_for_gui
from panda3d.direct import storeAccessibilityShortcutKeys, allowAccessibilityShortcutKeys from panda3d.direct import storeAccessibilityShortcutKeys, allowAccessibilityShortcutKeys
from . import DConfig from . import DConfig
@ -177,6 +178,10 @@ class ShowBase(DirectObject.DirectObject):
aspect2d: NodePath aspect2d: NodePath
pixel2d: NodePath pixel2d: NodePath
a2dTopLeft: NodePath
cluster: Any | None
def __init__(self, fStartDirect: bool = True, windowType: str | None = None) -> None: def __init__(self, fStartDirect: bool = True, windowType: str | None = None) -> None:
"""Opens a window, sets up a 3-D and several 2-D scene graphs, and """Opens a window, sets up a 3-D and several 2-D scene graphs, and
everything else needed to render the scene graph to the window. everything else needed to render the scene graph to the window.
@ -380,7 +385,7 @@ class ShowBase(DirectObject.DirectObject):
#: yourself every frame. #: yourself every frame.
self.cTrav: CollisionTraverser | Literal[0] = 0 self.cTrav: CollisionTraverser | Literal[0] = 0
self.shadowTrav: CollisionTraverser | Literal[0] = 0 self.shadowTrav: CollisionTraverser | Literal[0] = 0
self.cTravStack = Stack() self.cTravStack = Stack[CollisionTraverser]()
# Ditto for an AppTraverser. # Ditto for an AppTraverser.
self.appTrav: Any | Literal[0] = 0 self.appTrav: Any | Literal[0] = 0
@ -436,9 +441,9 @@ class ShowBase(DirectObject.DirectObject):
self.useTrackball() self.useTrackball()
#: `.Loader.Loader` object. #: `.Loader.Loader` object.
self.loader = Loader.Loader(self) self.loader: Loader.Loader = ShowBaseGlobal.loader
self.loader._init_base(self)
self.graphicsEngine.setDefaultLoader(self.loader.loader) self.graphicsEngine.setDefaultLoader(self.loader.loader)
ShowBaseGlobal.loader = self.loader
#: The global event manager, as imported from `.EventManagerGlobal`. #: The global event manager, as imported from `.EventManagerGlobal`.
self.eventMgr = eventMgr self.eventMgr = eventMgr
@ -652,7 +657,7 @@ class ShowBase(DirectObject.DirectObject):
def getExitErrorCode(self): def getExitErrorCode(self):
return 0 return 0
def printEnvDebugInfo(self): def printEnvDebugInfo(self) -> None:
"""Print some information about the environment that we are running """Print some information about the environment that we are running
in. Stuff like the model paths and other paths. Feel free to in. Stuff like the model paths and other paths. Feel free to
add stuff to this. add stuff to this.
@ -706,7 +711,8 @@ class ShowBase(DirectObject.DirectObject):
allowAccessibilityShortcutKeys(True) allowAccessibilityShortcutKeys(True)
self.__disabledStickyKeys = False self.__disabledStickyKeys = False
self.__directObject.ignoreAll() if hasattr(self, '__directObject'):
self.__directObject.ignoreAll()
self.ignoreAll() self.ignoreAll()
self.shutdown() self.shutdown()
@ -1208,7 +1214,7 @@ class ShowBase(DirectObject.DirectObject):
self.taskMgr.remove('clientSleep') self.taskMgr.remove('clientSleep')
self.taskMgr.add(self.__sleepCycleTask, 'clientSleep', sort = 55) self.taskMgr.add(self.__sleepCycleTask, 'clientSleep', sort = 55)
def __sleepCycleTask(self, task): def __sleepCycleTask(self, task: object) -> int:
Thread.sleep(self.clientSleep) Thread.sleep(self.clientSleep)
#time.sleep(self.clientSleep) #time.sleep(self.clientSleep)
return Task.cont return Task.cont
@ -1444,7 +1450,7 @@ class ShowBase(DirectObject.DirectObject):
self.__configAspectRatio = aspectRatio self.__configAspectRatio = aspectRatio
self.adjustWindowAspectRatio(self.getAspectRatio()) self.adjustWindowAspectRatio(self.getAspectRatio())
def getAspectRatio(self, win = None): def getAspectRatio(self, win: GraphicsOutput | None = None) -> float:
# Returns the actual aspect ratio of the indicated (or main # Returns the actual aspect ratio of the indicated (or main
# window), or the default aspect ratio if there is not yet a # window), or the default aspect ratio if there is not yet a
# main window. # main window.
@ -1453,7 +1459,7 @@ class ShowBase(DirectObject.DirectObject):
if self.__configAspectRatio: if self.__configAspectRatio:
return self.__configAspectRatio return self.__configAspectRatio
aspectRatio = 1 aspectRatio: float = 1
if win is None: if win is None:
win = self.win win = self.win
@ -1476,7 +1482,7 @@ class ShowBase(DirectObject.DirectObject):
return aspectRatio return aspectRatio
def getSize(self, win = None): def getSize(self, win: GraphicsOutput | None = None) -> tuple[int, int]:
""" """
Returns the actual size of the indicated (or main window), or the Returns the actual size of the indicated (or main window), or the
default size if there is not yet a main window. default size if there is not yet a main window.
@ -2176,7 +2182,7 @@ class ShowBase(DirectObject.DirectObject):
music.setLoop(looping) music.setLoop(looping)
music.play() music.play()
def __resetPrevTransform(self, state): def __resetPrevTransform(self, state: object) -> int:
# Clear out the previous velocity deltas now, after we have # Clear out the previous velocity deltas now, after we have
# rendered (the previous frame). We do this after the render, # rendered (the previous frame). We do this after the render,
# so that we have a chance to draw a representation of spheres # so that we have a chance to draw a representation of spheres
@ -2187,7 +2193,7 @@ class ShowBase(DirectObject.DirectObject):
PandaNode.resetAllPrevTransform() PandaNode.resetAllPrevTransform()
return Task.cont return Task.cont
def __dataLoop(self, state): def __dataLoop(self, state: object) -> int:
# Check if there were newly connected devices. # Check if there were newly connected devices.
self.devices.update() self.devices.update()
@ -2197,7 +2203,7 @@ class ShowBase(DirectObject.DirectObject):
self.dgTrav.traverse(self.dataRootNode) self.dgTrav.traverse(self.dataRootNode)
return Task.cont return Task.cont
def __ivalLoop(self, state): def __ivalLoop(self, state: object) -> int:
# Execute all intervals in the global ivalMgr. # Execute all intervals in the global ivalMgr.
IntervalManager.ivalMgr.step() IntervalManager.ivalMgr.step()
return Task.cont return Task.cont
@ -2215,7 +2221,7 @@ class ShowBase(DirectObject.DirectObject):
self.shadowTrav.traverse(self.render) self.shadowTrav.traverse(self.render)
return Task.cont return Task.cont
def __collisionLoop(self, state): def __collisionLoop(self, state: object) -> int:
# run the collision traversal if we have a # run the collision traversal if we have a
# CollisionTraverser set. # CollisionTraverser set.
if self.cTrav: if self.cTrav:
@ -2227,14 +2233,14 @@ class ShowBase(DirectObject.DirectObject):
messenger.send("collisionLoopFinished") messenger.send("collisionLoopFinished")
return Task.cont return Task.cont
def __audioLoop(self, state): def __audioLoop(self, state: object) -> int:
if self.musicManager is not None: if self.musicManager is not None:
self.musicManager.update() self.musicManager.update()
for x in self.sfxManagerList: for x in self.sfxManagerList:
x.update() x.update()
return Task.cont return Task.cont
def __garbageCollectStates(self, state): def __garbageCollectStates(self, state: object) -> int:
""" This task is started only when we have """ This task is started only when we have
garbage-collect-states set in the Config.prc file, in which garbage-collect-states set in the Config.prc file, in which
case we're responsible for taking out Panda's garbage from case we're responsible for taking out Panda's garbage from
@ -2245,7 +2251,7 @@ class ShowBase(DirectObject.DirectObject):
RenderState.garbageCollect() RenderState.garbageCollect()
return Task.cont return Task.cont
def __igLoop(self, state): def __igLoop(self, state: object) -> int:
if __debug__: if __debug__:
# We render the watch variables for the onScreenDebug as soon # We render the watch variables for the onScreenDebug as soon
# as we reasonably can before the renderFrame(). # as we reasonably can before the renderFrame().
@ -2280,12 +2286,11 @@ class ShowBase(DirectObject.DirectObject):
# now until someone complains. # now until someone complains.
time.sleep(0.1) time.sleep(0.1)
# Lerp stuff needs this event, and it must be generated in # Lerp stuff needs this event, thrown directly on the C++ queue.
# C++, not in Python. EventQueue.getGlobalEventQueue().queueEvent("NewFrame")
throw_new_frame()
return Task.cont return Task.cont
def __igLoopSync(self, state): def __igLoopSync(self, state: object) -> int:
if __debug__: if __debug__:
# We render the watch variables for the onScreenDebug as soon # We render the watch variables for the onScreenDebug as soon
# as we reasonably can before the renderFrame(). # as we reasonably can before the renderFrame().
@ -2294,6 +2299,7 @@ class ShowBase(DirectObject.DirectObject):
if self.recorder: if self.recorder:
self.recorder.recordFrame() self.recorder.recordFrame()
assert self.cluster is not None
self.cluster.collectData() self.cluster.collectData()
# Finally, render the frame. # Finally, render the frame.
@ -2323,12 +2329,12 @@ class ShowBase(DirectObject.DirectObject):
time.sleep(0.1) time.sleep(0.1)
self.graphicsEngine.readyFlip() self.graphicsEngine.readyFlip()
assert self.cluster is not None
self.cluster.waitForFlipCommand() self.cluster.waitForFlipCommand()
self.graphicsEngine.flipFrame() self.graphicsEngine.flipFrame()
# Lerp stuff needs this event, and it must be generated in # Lerp stuff needs this event, thrown directly on the C++ queue.
# C++, not in Python. EventQueue.getGlobalEventQueue().queueEvent("NewFrame")
throw_new_frame()
return Task.cont return Task.cont
def restart(self, clusterSync: bool = False, cluster=None) -> None: def restart(self, clusterSync: bool = False, cluster=None) -> None:
@ -2753,7 +2759,7 @@ class ShowBase(DirectObject.DirectObject):
self.oobeVis.reparentTo(self.camera) self.oobeVis.reparentTo(self.camera)
self.oobeMode = 1 self.oobeMode = 1
def __oobeButton(self, suffix, button): def __oobeButton(self, suffix: str, button: str) -> None:
if button.startswith('mouse'): if button.startswith('mouse'):
# Eat mouse buttons. # Eat mouse buttons.
return return
@ -3073,7 +3079,7 @@ class ShowBase(DirectObject.DirectObject):
else: else:
return Task.cont return Task.cont
def windowEvent(self, win): def windowEvent(self, win: GraphicsOutput) -> None:
if win != self.win: if win != self.win:
# This event isn't about our window. # This event isn't about our window.
return return
@ -3092,9 +3098,9 @@ class ShowBase(DirectObject.DirectObject):
self.userExit() self.userExit()
if properties.getForeground() and not self.mainWinForeground: if properties.getForeground() and not self.mainWinForeground:
self.mainWinForeground = 1 self.mainWinForeground = True
elif not properties.getForeground() and self.mainWinForeground: elif not properties.getForeground() and self.mainWinForeground:
self.mainWinForeground = 0 self.mainWinForeground = False
if __debug__: if __debug__:
if self.__autoGarbageLogging: if self.__autoGarbageLogging:
GarbageReport.b_checkForGarbageLeaks() GarbageReport.b_checkForGarbageLeaks()
@ -3102,12 +3108,12 @@ class ShowBase(DirectObject.DirectObject):
if properties.getMinimized() and not self.mainWinMinimized: if properties.getMinimized() and not self.mainWinMinimized:
# If the main window is minimized, throw an event to # If the main window is minimized, throw an event to
# stop the music. # stop the music.
self.mainWinMinimized = 1 self.mainWinMinimized = True
messenger.send('PandaPaused') messenger.send('PandaPaused')
elif not properties.getMinimized() and self.mainWinMinimized: elif not properties.getMinimized() and self.mainWinMinimized:
# If the main window is restored, throw an event to # If the main window is restored, throw an event to
# restart the music. # restart the music.
self.mainWinMinimized = 0 self.mainWinMinimized = False
messenger.send('PandaRestarted') messenger.send('PandaRestarted')
# If we have not forced the aspect ratio, let's see if it has # If we have not forced the aspect ratio, let's see if it has
@ -3125,7 +3131,7 @@ class ShowBase(DirectObject.DirectObject):
if self.wantRender2dp: if self.wantRender2dp:
self.pixel2dp.setScale(2.0 / xsize, 1.0, 2.0 / ysize) self.pixel2dp.setScale(2.0 / xsize, 1.0, 2.0 / ysize)
def adjustWindowAspectRatio(self, aspectRatio): def adjustWindowAspectRatio(self, aspectRatio: float) -> None:
""" This function is normally called internally by """ This function is normally called internally by
`windowEvent()`, but it may also be called to explicitly adjust `windowEvent()`, but it may also be called to explicitly adjust
the aspect ratio of the render/render2d DisplayRegion, by a the aspect ratio of the render/render2d DisplayRegion, by a
@ -3512,6 +3518,7 @@ class ShowBase(DirectObject.DirectObject):
remove_camera_frustum = removeCameraFrustum remove_camera_frustum = removeCameraFrustum
save_cube_map = saveCubeMap save_cube_map = saveCubeMap
save_sphere_map = saveSphereMap save_sphere_map = saveSphereMap
user_exit = userExit
start_wx = startWx start_wx = startWx
start_tk = startTk start_tk = startTk
start_direct = startDirect start_direct = startDirect

View File

@ -62,7 +62,8 @@ aspect2d = render2d.attachNewNode(PGTop("aspect2d"))
#: A dummy scene graph that is not being rendered by anything. #: A dummy scene graph that is not being rendered by anything.
hidden = NodePath("hidden") hidden = NodePath("hidden")
loader: Loader #: The global Loader instance for models, textures, etc.
loader = Loader()
# Set direct notify categories now that we have config # Set direct notify categories now that we have config
directNotify.setDconfigLevels() directNotify.setDconfigLevels()

View File

@ -5,477 +5,267 @@ Calling the :func:`register()` function to register the import hooks should be
sufficient to enable this functionality. sufficient to enable this functionality.
""" """
__all__ = ['register', 'sharedPackages', from __future__ import annotations
'reloadSharedPackage', 'reloadSharedPackages']
from panda3d.core import Filename, VirtualFileSystem, VirtualFileMountSystem, OFileStream, copyStream __all__ = ['register']
from direct.stdpy.file import open
from panda3d.core import Filename, VirtualFile, VirtualFileSystem, VirtualFileMountSystem
from panda3d.core import OFileStream, copy_stream
import sys import sys
import marshal import marshal
import imp import _imp
import types import atexit
from importlib.abc import Loader, SourceLoader
from importlib.util import MAGIC_NUMBER, decode_source
from importlib.machinery import ModuleSpec, EXTENSION_SUFFIXES, BYTECODE_SUFFIXES
from types import ModuleType
from typing import Any
#: The sharedPackages dictionary lists all of the "shared packages", vfs = VirtualFileSystem.get_global_ptr()
#: special Python packages that automatically span multiple directories
#: via magic in the VFSImporter. You can make a package "shared"
#: simply by adding its name into this dictionary (and then calling
#: reloadSharedPackages() if it's already been imported).
#:
#: When a package name is in this dictionary at import time, *all*
#: instances of the package are located along sys.path, and merged into
#: a single Python module with a __path__ setting that represents the
#: union. Thus, you can have a direct.showbase.foo in your own
#: application, and loading it won't shadow the system
#: direct.showbase.ShowBase which is in a different directory on disk.
sharedPackages = {}
vfs = VirtualFileSystem.getGlobalPtr()
compiledExtensions = ['pyc', 'pyo']
if not __debug__:
# In optimized mode, we prefer loading .pyo files over .pyc files.
# We implement that by reversing the extension names.
compiledExtensions = ['pyo', 'pyc']
class VFSImporter: def _make_spec(fullname: str, loader: VFSLoader, *, is_package: bool) -> ModuleSpec:
filename = loader._vfile.get_filename()
spec = ModuleSpec(fullname, loader, origin=filename.to_os_specific(), is_package=is_package)
if is_package:
assert spec.submodule_search_locations is not None
spec.submodule_search_locations.append(Filename(filename.get_dirname()).to_os_specific())
spec.has_location = True
return spec
class VFSFinder:
""" This class serves as a Python importer to support loading """ This class serves as a Python importer to support loading
Python .py and .pyc/.pyo files from Panda's Virtual File System, Python .py and .pyc/.pyo files from Panda's Virtual File System,
which allows loading Python source files from mounted .mf files which allows loading Python source files from mounted .mf files
(among other places). """ (among other places). """
def __init__(self, path): def __init__(self, path: str) -> None:
if isinstance(path, Filename): self.path = path
self.dir_path = Filename(path)
else:
self.dir_path = Filename.fromOsSpecific(path)
def find_module(self, fullname, path = None): def find_spec(self, fullname: str, target: ModuleType | None = None) -> ModuleSpec | None:
if path is None: #print(f"find_spec({fullname}), dir_path = {dir_path}", file=sys.stderr)
dir_path = self.dir_path
else:
dir_path = path
#print >>sys.stderr, "find_module(%s), dir_path = %s" % (fullname, dir_path)
basename = fullname.split('.')[-1] basename = fullname.split('.')[-1]
path = Filename(dir_path, basename) filename = Filename(Filename.from_os_specific(self.path), basename)
loader: VFSLoader
# First, look for Python files. # First, look for Python files.
filename = Filename(path) vfile = vfs.get_file(filename + '.py', True)
filename.setExtension('py')
vfile = vfs.getFile(filename, True)
if vfile: if vfile:
return VFSLoader(dir_path, vfile, filename, loader = VFSSourceLoader(fullname, vfile)
desc=('.py', 'r', imp.PY_SOURCE)) return _make_spec(fullname, loader, is_package=False)
# If there's no .py file, but there's a .pyc file, load that # If there's no .py file, but there's a .pyc file, load that
# anyway. # anyway.
for ext in compiledExtensions: for suffix in BYTECODE_SUFFIXES:
filename = Filename(path) vfile = vfs.get_file(filename + suffix, True)
filename.setExtension(ext)
vfile = vfs.getFile(filename, True)
if vfile: if vfile:
return VFSLoader(dir_path, vfile, filename, loader = VFSCompiledLoader(fullname, vfile)
desc=('.'+ext, 'rb', imp.PY_COMPILED)) return _make_spec(fullname, loader, is_package=False)
# Look for a C/C++ extension module. # Look for a C/C++ extension module.
for desc in imp.get_suffixes(): for suffix in EXTENSION_SUFFIXES:
if desc[2] != imp.C_EXTENSION: vfile = vfs.get_file(filename + suffix, True)
continue
filename = Filename(path + desc[0])
vfile = vfs.getFile(filename, True)
if vfile: if vfile:
return VFSLoader(dir_path, vfile, filename, desc=desc) loader = VFSExtensionLoader(fullname, vfile)
return _make_spec(fullname, loader, is_package=False)
# Finally, consider a package, i.e. a directory containing # Consider a package, i.e. a directory containing __init__.py.
# __init__.py. init_filename = Filename(filename, '__init__.py')
filename = Filename(path, '__init__.py') vfile = vfs.get_file(init_filename, True)
vfile = vfs.getFile(filename, True)
if vfile: if vfile:
return VFSLoader(dir_path, vfile, filename, packagePath=path, loader = VFSSourceLoader(fullname, vfile)
desc=('.py', 'r', imp.PY_SOURCE)) return _make_spec(fullname, loader, is_package=True)
for ext in compiledExtensions:
filename = Filename(path, '__init__.' + ext)
vfile = vfs.getFile(filename, True)
if vfile:
return VFSLoader(dir_path, vfile, filename, packagePath=path,
desc=('.'+ext, 'rb', imp.PY_COMPILED))
#print >>sys.stderr, "not found." for suffix in BYTECODE_SUFFIXES:
init_filename = Filename(filename, '__init__' + suffix)
vfile = vfs.get_file(init_filename, True)
if vfile:
loader = VFSCompiledLoader(fullname, vfile)
return _make_spec(fullname, loader, is_package=True)
# Consider a namespace package.
if vfs.is_directory(filename):
spec = ModuleSpec(fullname, VFSNamespaceLoader(), is_package=True)
assert spec.submodule_search_locations is not None
spec.submodule_search_locations.append(filename.to_os_specific())
return spec
#print("not found.", file=sys.stderr)
return None return None
class VFSLoader: class VFSLoader(Loader):
""" The second part of VFSImporter, this is created for a def __init__(self, fullname: str, vfile: VirtualFile) -> None:
particular .py file or directory. """ self.name = fullname
self._vfile = vfile
def __init__(self, dir_path, vfile, filename, desc, packagePath=None):
self.dir_path = dir_path
self.timestamp = None
if vfile:
self.timestamp = vfile.getTimestamp()
self.filename = filename
self.desc = desc
self.packagePath = packagePath
def load_module(self, fullname, loadingShared = False):
#print >>sys.stderr, "load_module(%s), dir_path = %s, filename = %s" % (fullname, self.dir_path, self.filename)
if self.desc[2] == imp.PY_FROZEN:
return self._import_frozen_module(fullname)
if self.desc[2] == imp.C_EXTENSION:
return self._import_extension_module(fullname)
# Check if this is a child of a shared package.
if not loadingShared and self.packagePath and '.' in fullname:
parentname = fullname.rsplit('.', 1)[0]
if parentname in sharedPackages:
# It is. That means it's a shared package too.
parent = sys.modules[parentname]
path = getattr(parent, '__path__', None)
importer = VFSSharedImporter()
sharedPackages[fullname] = True
loader = importer.find_module(fullname, path = path)
assert loader
return loader.load_module(fullname)
code = self._read_code()
if not code:
raise ImportError('No Python code in %s' % (fullname))
mod = sys.modules.setdefault(fullname, imp.new_module(fullname))
mod.__file__ = self.filename.toOsSpecific()
mod.__loader__ = self
if self.packagePath:
mod.__path__ = [self.packagePath.toOsSpecific()]
#print >> sys.stderr, "loaded %s, path = %s" % (fullname, mod.__path__)
exec(code, mod.__dict__)
return sys.modules[fullname]
def getdata(self, path):
path = Filename(self.dir_path, Filename.fromOsSpecific(path))
vfile = vfs.getFile(path)
if not vfile:
raise IOError("Could not find '%s'" % (path))
return vfile.readFile(True)
def is_package(self, fullname): def is_package(self, fullname):
return bool(self.packagePath) if fullname is not None and self.name != fullname:
raise ImportError
filename = self._vfile.get_filename().get_basename()
filename_base = filename.rsplit('.', 1)[0]
tail_name = fullname.rpartition('.')[2]
return filename_base == '__init__' and tail_name != '__init__'
def create_module(self, spec: ModuleSpec) -> ModuleType | None:
"""Use default semantics for module creation."""
def exec_module(self, module: ModuleType) -> None:
"""Execute the module."""
code = self.get_code(module.__name__) # type: ignore[attr-defined]
exec(code, module.__dict__)
def get_filename(self, fullname: str) -> str:
if fullname is not None and self.name != fullname:
raise ImportError
return self._vfile.get_filename().to_os_specific()
@staticmethod
def get_data(path: str) -> bytes:
vfile = vfs.get_file(Filename.from_os_specific(path))
if vfile:
return vfile.read_file(True)
else:
raise OSError
@staticmethod
def path_stats(path: str) -> dict[str, Any]:
vfile = vfs.get_file(Filename.from_os_specific(path))
if vfile:
return {'mtime': vfile.get_timestamp(), 'size': vfile.get_file_size()}
else:
raise OSError
@staticmethod
def path_mtime(path):
vfile = vfs.get_file(Filename.from_os_specific(path))
if vfile:
return vfile.get_timestamp()
else:
raise OSError
class VFSSourceLoader(VFSLoader, SourceLoader): # type: ignore[misc]
def get_source(self, fullname):
if fullname is not None and self.name != fullname:
raise ImportError
return decode_source(self._vfile.read_file(True))
class VFSCompiledLoader(VFSLoader):
def get_code(self, fullname): def get_code(self, fullname):
return self._read_code() if fullname is not None and self.name != fullname:
raise ImportError
vfile = self._vfile
data = vfile.read_file(True)
if data[:4] != MAGIC_NUMBER:
raise ImportError("Bad magic number in %s" % (vfile))
return marshal.loads(data[16:])
def get_source(self, fullname): def get_source(self, fullname):
return self._read_source() return None
def get_filename(self, fullname):
return self.filename.toOsSpecific()
def _read_source(self): class VFSExtensionLoader(VFSLoader):
""" Returns the Python source for this file, if it is def create_module(self, spec):
available, or None if it is not. May raise IOError. """ vfile = self._vfile
filename = vfile.get_filename()
if self.desc[2] == imp.PY_COMPILED or \
self.desc[2] == imp.C_EXTENSION:
return None
filename = Filename(self.filename)
filename.setExtension('py')
filename.setText()
# Use the tokenize module to detect the encoding.
import tokenize
fh = open(self.filename, 'rb')
encoding, lines = tokenize.detect_encoding(fh.readline)
return (b''.join(lines) + fh.read()).decode(encoding)
def _import_extension_module(self, fullname):
""" Loads the binary shared object as a Python module, and
returns it. """
vfile = vfs.getFile(self.filename, False)
# We can only import an extension module if it already exists on # We can only import an extension module if it already exists on
# disk. This means if it's a truly virtual file that has no # disk. This means if it's a truly virtual file that has no
# on-disk equivalent, we have to write it to a temporary file # on-disk equivalent, we have to write it to a temporary file
# first. # first.
if hasattr(vfile, 'getMount') and \ if isinstance(vfile.get_mount(), VirtualFileMountSystem):
isinstance(vfile.getMount(), VirtualFileMountSystem):
# It's a real file. # It's a real file.
filename = self.filename pass
elif self.filename.exists(): elif filename.exists():
# It's a virtual file, but it's shadowing a real file in # It's a virtual file, but it's shadowing a real file in
# the same directory. Assume they're the same, and load # the same directory. Assume they're the same, and load
# the real one. # the real one.
filename = self.filename
else:
# It's a virtual file with no real-world existence. Dump
# it to disk. TODO: clean up this filename.
filename = Filename.temporary('', self.filename.getBasenameWoExtension(),
'.' + self.filename.getExtension(),
type = Filename.TDso)
filename.setExtension(self.filename.getExtension())
filename.setBinary()
sin = vfile.openReadFile(True)
sout = OFileStream()
if not filename.openWrite(sout):
raise IOError
if not copyStream(sin, sout):
raise IOError
vfile.closeReadFile(sin)
del sout
module = imp.load_module(fullname, None, filename.toOsSpecific(),
self.desc)
module.__file__ = self.filename.toOsSpecific()
return module
def _import_frozen_module(self, fullname):
""" Imports the frozen module without messing around with
searching any more. """
#print >>sys.stderr, "importing frozen %s" % (fullname)
module = imp.load_module(fullname, None, fullname,
('', '', imp.PY_FROZEN))
module.__path__ = []
return module
def _read_code(self):
""" Returns the Python compiled code object for this file, if
it is available, or None if it is not. May raise IOError,
ValueError, SyntaxError, or a number of other errors generated
by the low-level system. """
if self.desc[2] == imp.PY_COMPILED:
# It's a pyc file; just read it directly.
pycVfile = vfs.getFile(self.filename, False)
if pycVfile:
return self._loadPyc(pycVfile, None)
raise IOError('Could not read %s' % (self.filename))
elif self.desc[2] == imp.C_EXTENSION:
return None
# It's a .py file (or an __init__.py file; same thing). Read
# the .pyc file if it is available and current; otherwise read
# the .py file and compile it.
t_pyc = None
for ext in compiledExtensions:
pycFilename = Filename(self.filename)
pycFilename.setExtension(ext)
pycVfile = vfs.getFile(pycFilename, False)
if pycVfile:
t_pyc = pycVfile.getTimestamp()
break
code = None
if t_pyc and t_pyc >= self.timestamp:
try:
code = self._loadPyc(pycVfile, self.timestamp)
except ValueError:
code = None
if not code:
source = self._read_source()
filename = Filename(self.filename)
filename.setExtension('py')
code = self._compile(filename, source)
return code
def _loadPyc(self, vfile, timestamp):
""" Reads and returns the marshal data from a .pyc file.
Raises ValueError if there is a problem. """
code = None
data = vfile.readFile(True)
if data[:4] != imp.get_magic():
raise ValueError("Bad magic number in %s" % (vfile))
t = int.from_bytes(data[4:8], 'little')
data = data[12:]
if not timestamp or t == timestamp:
return marshal.loads(data)
else:
raise ValueError("Timestamp wrong on %s" % (vfile))
def _compile(self, filename, source):
""" Compiles the Python source code to a code object and
attempts to write it to an appropriate .pyc file. May raise
SyntaxError or other errors generated by the compiler. """
if source and source[-1] != '\n':
source = source + '\n'
code = compile(source, filename.toOsSpecific(), 'exec')
# try to cache the compiled code
pycFilename = Filename(filename)
pycFilename.setExtension(compiledExtensions[0])
try:
f = open(pycFilename.toOsSpecific(), 'wb')
except IOError:
pass pass
else: else:
f.write(imp.get_magic()) # It's a virtual file with no real-world existence. Dump
f.write((self.timestamp & 0xffffffff).to_bytes(4, 'little')) # it to disk.
f.write(b'\0\0\0\0') ext = filename.get_extension()
f.write(marshal.dumps(code)) tmp_filename = Filename.temporary('', filename.get_basename_wo_extension(),
f.close() '.' + ext,
type = Filename.T_dso)
return code tmp_filename.set_extension(ext)
tmp_filename.set_binary()
sin = vfile.open_read_file(True)
class VFSSharedImporter:
""" This is a special importer that is added onto the meta_path
list, so that it is called before sys.path is traversed. It uses
special logic to load one of the "shared" packages, by searching
the entire sys.path for all instances of this shared package, and
merging them. """
def __init__(self):
pass
def find_module(self, fullname, path = None, reload = False):
#print >>sys.stderr, "shared find_module(%s), path = %s" % (fullname, path)
if fullname not in sharedPackages:
# Not a shared package; fall back to normal import.
return None
if path is None:
path = sys.path
excludePaths = []
if reload:
# If reload is true, we are simply reloading the module,
# looking for new paths to add.
mod = sys.modules[fullname]
excludePaths = getattr(mod, '_vfs_shared_path', None)
if excludePaths is None:
# If there isn't a _vfs_shared_path symbol already,
# the module must have been loaded through
# conventional means. Try to guess which path it was
# found on.
d = self.getLoadedDirname(mod)
excludePaths = [d]
loaders = []
for dir in path:
if dir in excludePaths:
continue
importer = sys.path_importer_cache.get(dir, None)
if importer is None:
try:
importer = VFSImporter(dir)
except ImportError:
continue
sys.path_importer_cache[dir] = importer
try: try:
loader = importer.find_module(fullname) sout = OFileStream()
if not loader: if not tmp_filename.open_write(sout):
continue raise IOError
except ImportError: if not copy_stream(sin, sout):
continue raise IOError
finally:
vfile.close_read_file(sin)
del sout
loaders.append(loader) # Delete when the process ends.
atexit.register(tmp_filename.unlink)
if not loaders: # Make a dummy spec to pass to create_dynamic with the path to
return None # our temporary file.
return VFSSharedLoader(loaders, reload = reload) spec = ModuleSpec(spec.name, spec.loader,
origin=tmp_filename.to_os_specific(),
is_package=False)
def getLoadedDirname(self, mod): module = _imp.create_dynamic(spec)
""" Returns the directory name that the indicated module.__file__ = filename.to_os_specific()
conventionally-loaded module must have been loaded from. """ return module
if not getattr(mod, '__file__', None): def exec_module(self, module):
return None _imp.exec_dynamic(module)
fullname = mod.__name__ def is_package(self, fullname):
dirname = Filename.fromOsSpecific(mod.__file__).getDirname() return False
parentname = None def get_code(self, fullname):
basename = fullname return None
if '.' in fullname:
parentname, basename = fullname.rsplit('.', 1)
path = None def get_source(self, fullname):
if parentname:
parent = sys.modules[parentname]
path = parent.__path__
if path is None:
path = sys.path
for dir in path:
pdir = str(Filename.fromOsSpecific(dir))
if pdir + '/' + basename == dirname:
# We found it!
return dir
# Couldn't figure it out.
return None return None
class VFSSharedLoader: class VFSNamespaceLoader(Loader):
""" The second part of VFSSharedImporter, this imports a list of def create_module(self, spec: ModuleSpec) -> ModuleType | None:
packages and combines them. """ """Use default semantics for module creation."""
def __init__(self, loaders, reload): def exec_module(self, module: ModuleType) -> None:
self.loaders = loaders pass
self.reload = reload
def load_module(self, fullname): def is_package(self, fullname):
#print >>sys.stderr, "shared load_module(%s), loaders = %s" % (fullname, map(lambda l: l.dir_path, self.loaders)) return True
mod = None def get_source(self, fullname):
message = None return ''
path = []
vfs_shared_path = []
if self.reload:
mod = sys.modules[fullname]
path = mod.__path__ or []
if path == fullname:
# Work around Python bug setting __path__ of frozen modules.
path = []
vfs_shared_path = getattr(mod, '_vfs_shared_path', [])
for loader in self.loaders: def get_code(self, fullname):
try: return compile('', '<string>', 'exec', dont_inherit=True)
mod = loader.load_module(fullname, loadingShared = True)
except ImportError:
etype, evalue, etraceback = sys.exc_info()
print("%s on %s: %s" % (etype.__name__, fullname, evalue))
if not message:
message = '%s: %s' % (fullname, evalue)
continue
for dir in getattr(mod, '__path__', []):
if dir not in path:
path.append(dir)
if mod is None:
# If all of them failed to load, raise ImportError.
raise ImportError(message)
# If at least one of them loaded successfully, return the def _path_hook(entry: str) -> VFSFinder:
# union of loaded modules. # If this is a directory in the VFS, create a VFSFinder for this entry.
mod.__path__ = path vfile = vfs.get_file(Filename.from_os_specific(entry), False)
mod.__package__ = fullname if vfile and vfile.is_directory() and not isinstance(vfile.get_mount(), VirtualFileMountSystem):
return VFSFinder(entry)
# Also set this special symbol, which records that this is a else:
# shared package, and also lists the paths we have already raise ImportError
# loaded.
mod._vfs_shared_path = vfs_shared_path + [l.dir_path for l in self.loaders]
return mod
_registered = False _registered = False
def register() -> None:
def register(): """ Register the VFSFinder on the path_hooks, if it has not
""" Register the VFSImporter on the path_hooks, if it has not
already been registered, so that future Python import statements already been registered, so that future Python import statements
will vector through here (and therefore will take advantage of will vector through here (and therefore will take advantage of
Panda's virtual file system). """ Panda's virtual file system). """
@ -483,55 +273,9 @@ def register():
global _registered global _registered
if not _registered: if not _registered:
_registered = True _registered = True
sys.path_hooks.insert(0, VFSImporter) sys.path_hooks.insert(0, _path_hook)
sys.meta_path.insert(0, VFSSharedImporter())
# Blow away the importer cache, so we'll come back through the # Blow away the importer cache, so we'll come back through the
# VFSImporter for every folder in the future, even those # VFSFinder for every folder in the future, even those
# folders that previously were loaded directly. # folders that previously were loaded directly.
sys.path_importer_cache = {} sys.path_importer_cache = {}
def reloadSharedPackage(mod):
""" Reloads the specific module as a shared package, adding any
new directories that might have appeared on the search path. """
fullname = mod.__name__
path = None
if '.' in fullname:
parentname = fullname.rsplit('.', 1)[0]
parent = sys.modules[parentname]
path = parent.__path__
importer = VFSSharedImporter()
loader = importer.find_module(fullname, path = path, reload = True)
if loader:
loader.load_module(fullname)
# Also force any child packages to become shared packages, if
# they aren't already.
for basename, child in list(mod.__dict__.items()):
if isinstance(child, types.ModuleType):
childname = child.__name__
if childname == fullname + '.' + basename and \
hasattr(child, '__path__') and \
childname not in sharedPackages:
sharedPackages[childname] = True
reloadSharedPackage(child)
def reloadSharedPackages():
""" Walks through the sharedPackages list, and forces a reload of
any modules on that list that have already been loaded. This
allows new directories to be added to the search path. """
#print >> sys.stderr, "reloadSharedPackages, path = %s, sharedPackages = %s" % (sys.path, sharedPackages.keys())
# Sort the list, just to make sure parent packages are reloaded
# before child packages are.
for fullname in sorted(sharedPackages.keys()):
mod = sys.modules.get(fullname, None)
if not mod:
continue
reloadSharedPackage(mod)

View File

@ -45,15 +45,6 @@ ConfigureDef(config_showbase);
ConfigureFn(config_showbase) { ConfigureFn(config_showbase) {
} }
ConfigVariableSearchPath particle_path
("particle-path",
PRC_DESC("The directories to search for particle files to be loaded."));
ConfigVariableSearchPath &
get_particle_path() {
return particle_path;
}
// Throw the "NewFrame" event in the C++ world. Some of the lerp code depends // Throw the "NewFrame" event in the C++ world. Some of the lerp code depends
// on receiving this. // on receiving this.
void void

View File

@ -22,8 +22,6 @@
#include "animControl.h" #include "animControl.h"
#include "pointerTo.h" #include "pointerTo.h"
#include "dconfig.h" #include "dconfig.h"
#include "dSearchPath.h"
#include "configVariableSearchPath.h"
#include "nodePath.h" #include "nodePath.h"
ConfigureDecl(config_showbase, EXPCL_DIRECT_SHOWBASE, EXPTP_DIRECT_SHOWBASE); ConfigureDecl(config_showbase, EXPCL_DIRECT_SHOWBASE, EXPTP_DIRECT_SHOWBASE);
@ -34,8 +32,6 @@ class GraphicsEngine;
BEGIN_PUBLISH BEGIN_PUBLISH
EXPCL_DIRECT_SHOWBASE ConfigVariableSearchPath &get_particle_path();
EXPCL_DIRECT_SHOWBASE void throw_new_frame(); EXPCL_DIRECT_SHOWBASE void throw_new_frame();
EXPCL_DIRECT_SHOWBASE void init_app_for_gui(); EXPCL_DIRECT_SHOWBASE void init_app_for_gui();

View File

@ -5,6 +5,8 @@ in some compilation models, Panda's threading constructs are
incompatible with the OS-provided threads used by Python's thread incompatible with the OS-provided threads used by Python's thread
module. """ module. """
from __future__ import annotations
__all__ = [ __all__ = [
'error', 'LockType', 'error', 'LockType',
'start_new_thread', 'start_new_thread',
@ -19,6 +21,9 @@ __all__ = [
from panda3d import core from panda3d import core
import sys import sys
from collections.abc import Callable, Iterable, Mapping
from typing import Any
if sys.platform == "win32": if sys.platform == "win32":
TIMEOUT_MAX = float(0xffffffff // 1000) TIMEOUT_MAX = float(0xffffffff // 1000)
else: else:
@ -41,12 +46,12 @@ class LockType:
allows a different thread to release the lock than the one that allows a different thread to release the lock than the one that
acquired it. """ acquired it. """
def __init__(self): def __init__(self) -> None:
self.__lock = core.Mutex('PythonLock') self.__lock = core.Mutex('PythonLock')
self.__cvar = core.ConditionVar(self.__lock) self.__cvar = core.ConditionVar(self.__lock)
self.__locked = False self.__locked = False
def acquire(self, waitflag = 1, timeout = -1): def acquire(self, waitflag: bool = True, timeout: float = -1) -> bool:
self.__lock.acquire() self.__lock.acquire()
try: try:
if self.__locked and not waitflag: if self.__locked and not waitflag:
@ -65,7 +70,7 @@ class LockType:
finally: finally:
self.__lock.release() self.__lock.release()
def release(self): def release(self) -> None:
self.__lock.acquire() self.__lock.acquire()
try: try:
if not self.__locked: if not self.__locked:
@ -90,18 +95,23 @@ class LockType:
_counter = 0 _counter = 0
def _newname(template="Thread-%d"): def _newname(template: str = "Thread-%d") -> str:
global _counter global _counter
_counter = _counter + 1 _counter = _counter + 1
return template % _counter return template % _counter
_threads = {} _threads: dict[int, tuple[core.Thread, dict[int, dict[str, Any]], Any | None]] = {}
_nextThreadId = 0 _nextThreadId = 0
_threadsLock = core.Mutex('thread._threadsLock') _threadsLock = core.Mutex('thread._threadsLock')
def start_new_thread(function, args, kwargs = {}, name = None): def start_new_thread(
function: Callable[..., object],
args: Iterable[Any],
kwargs: Mapping[str, Any] = {},
name: str | None = None,
) -> int:
def threadFunc(threadId, function = function, args = args, kwargs = kwargs): def threadFunc(threadId, function = function, args = args, kwargs = kwargs):
try: try:
try: try:
@ -132,7 +142,7 @@ def start_new_thread(function, args, kwargs = {}, name = None):
_threadsLock.release() _threadsLock.release()
def _add_thread(thread, wrapper): def _add_thread(thread: core.Thread, wrapper: Any) -> int:
""" Adds the indicated core.Thread object, with the indicated Python """ Adds the indicated core.Thread object, with the indicated Python
wrapper, to the thread list. Returns the new thread ID. """ wrapper, to the thread list. Returns the new thread ID. """
@ -150,7 +160,7 @@ def _add_thread(thread, wrapper):
_threadsLock.release() _threadsLock.release()
def _get_thread_wrapper(thread, wrapperClass): def _get_thread_wrapper(thread: core.Thread, wrapperClass: Callable[[core.Thread, int], Any]) -> Any:
""" Returns the thread wrapper for the indicated thread. If there """ Returns the thread wrapper for the indicated thread. If there
is not one, creates an instance of the indicated wrapperClass is not one, creates an instance of the indicated wrapperClass
instead. """ instead. """
@ -222,7 +232,7 @@ def _get_thread_locals(thread, i):
_threadsLock.release() _threadsLock.release()
def _remove_thread_id(threadId): def _remove_thread_id(threadId: int) -> None:
""" Removes the thread with the indicated ID from the thread list. """ """ Removes the thread with the indicated ID from the thread list. """
# On interpreter shutdown, Python may set module globals to None. # On interpreter shutdown, Python may set module globals to None.
@ -250,11 +260,11 @@ def exit():
raise SystemExit raise SystemExit
def allocate_lock(): def allocate_lock() -> LockType:
return LockType() return LockType()
def get_ident(): def get_ident() -> int:
return core.Thread.getCurrentThread().this return core.Thread.getCurrentThread().this

View File

@ -21,12 +21,17 @@ easier to use and understand.
It is permissible to mix-and-match both threading and threading2 It is permissible to mix-and-match both threading and threading2
within the same application. """ within the same application. """
from __future__ import annotations
from panda3d import core from panda3d import core
from direct.stdpy import thread as _thread from direct.stdpy import thread as _thread
import sys as _sys import sys as _sys
import weakref import weakref
from collections.abc import Callable, Iterable, Mapping
from typing import Any, NoReturn
__all__ = [ __all__ = [
'Thread', 'Thread',
'Lock', 'RLock', 'Lock', 'RLock',
@ -54,10 +59,14 @@ class ThreadBase:
""" A base class for both Thread and ExternalThread in this """ A base class for both Thread and ExternalThread in this
module. """ module. """
def __init__(self): name: str
ident: int
daemon: bool
def __init__(self) -> None:
pass pass
def getName(self): def getName(self) -> str:
return self.name return self.name
def isDaemon(self): def isDaemon(self):
@ -92,7 +101,15 @@ class Thread(ThreadBase):
object. The wrapper is designed to emulate Python's own object. The wrapper is designed to emulate Python's own
threading.Thread object. """ threading.Thread object. """
def __init__(self, group=None, target=None, name=None, args=(), kwargs={}, daemon=None): def __init__(
self,
group: None = None,
target: Callable[..., object] | None = None,
name: str | None = None,
args: Iterable[Any] = (),
kwargs: Mapping[str, Any] = {},
daemon: bool | None = None,
) -> None:
ThreadBase.__init__(self) ThreadBase.__init__(self)
assert group is None assert group is None
@ -131,7 +148,7 @@ class Thread(ThreadBase):
isAlive = is_alive isAlive = is_alive
def start(self): def start(self) -> None:
thread = self.__thread thread = self.__thread
if thread is None or thread.is_started(): if thread is None or thread.is_started():
raise RuntimeError raise RuntimeError
@ -147,7 +164,7 @@ class Thread(ThreadBase):
self.__target(*self.__args, **self.__kwargs) self.__target(*self.__args, **self.__kwargs)
def join(self, timeout = None): def join(self, timeout: float | None = None) -> None:
# We don't support a timed join here, sorry. # We don't support a timed join here, sorry.
assert timeout is None assert timeout is None
thread = self.__thread thread = self.__thread
@ -157,7 +174,7 @@ class Thread(ThreadBase):
self.__thread = None self.__thread = None
_thread._remove_thread_id(self.ident) _thread._remove_thread_id(self.ident)
def setName(self, name): def setName(self, name: str) -> None:
self.__dict__['name'] = name self.__dict__['name'] = name
self.__thread.setName(name) self.__thread.setName(name)
@ -166,7 +183,7 @@ class ExternalThread(ThreadBase):
""" Returned for a Thread object that wasn't created by this """ Returned for a Thread object that wasn't created by this
interface. """ interface. """
def __init__(self, extThread, threadId): def __init__(self, extThread: core.Thread, threadId: int) -> None:
ThreadBase.__init__(self) ThreadBase.__init__(self)
self.__thread = extThread self.__thread = extThread
@ -196,7 +213,7 @@ class ExternalThread(ThreadBase):
class MainThread(ExternalThread): class MainThread(ExternalThread):
""" Returned for the MainThread object. """ """ Returned for the MainThread object. """
def __init__(self, extThread, threadId): def __init__(self, extThread: core.Thread, threadId: int) -> None:
ExternalThread.__init__(self, extThread, threadId) ExternalThread.__init__(self, extThread, threadId)
self.__dict__['daemon'] = False self.__dict__['daemon'] = False
@ -206,7 +223,7 @@ class Lock(core.Mutex):
The wrapper is designed to emulate Python's own threading.Lock The wrapper is designed to emulate Python's own threading.Lock
object. """ object. """
def __init__(self, name = "PythonLock"): def __init__(self, name: str = "PythonLock") -> None:
core.Mutex.__init__(self, name) core.Mutex.__init__(self, name)
@ -224,7 +241,7 @@ class Condition(core.ConditionVar):
object. The wrapper is designed to emulate Python's own object. The wrapper is designed to emulate Python's own
threading.Condition object. """ threading.Condition object. """
def __init__(self, lock = None): def __init__(self, lock: Lock | RLock | None = None) -> None:
if not lock: if not lock:
lock = Lock() lock = Lock()
@ -241,7 +258,7 @@ class Condition(core.ConditionVar):
def release(self): def release(self):
self.__lock.release() self.__lock.release()
def wait(self, timeout = None): def wait(self, timeout: float | None = None) -> None:
if timeout is None: if timeout is None:
core.ConditionVar.wait(self) core.ConditionVar.wait(self)
else: else:
@ -373,8 +390,9 @@ class Timer(Thread):
self.finished.set() self.finished.set()
def _create_thread_wrapper(t, threadId): def _create_thread_wrapper(t: core.Thread, threadId: int) -> ExternalThread:
""" Creates a thread wrapper for the indicated external thread. """ """ Creates a thread wrapper for the indicated external thread. """
pyt: ExternalThread
if isinstance(t, core.MainThread): if isinstance(t, core.MainThread):
pyt = MainThread(t, threadId) pyt = MainThread(t, threadId)
else: else:
@ -383,7 +401,7 @@ def _create_thread_wrapper(t, threadId):
return pyt return pyt
def current_thread(): def current_thread() -> ThreadBase:
t = core.Thread.getCurrentThread() t = core.Thread.getCurrentThread()
return _thread._get_thread_wrapper(t, _create_thread_wrapper) return _thread._get_thread_wrapper(t, _create_thread_wrapper)
@ -430,5 +448,5 @@ def setprofile(func):
_setprofile_func = func _setprofile_func = func
def stack_size(size = None): def stack_size(size: object = None) -> NoReturn:
raise ThreadError raise ThreadError

View File

@ -13,6 +13,8 @@ to import Panda's thread reimplementation instead of the system thread
module, and so it is therefore layered on top of Panda's thread module, and so it is therefore layered on top of Panda's thread
implementation. """ implementation. """
from __future__ import annotations
import sys as _sys import sys as _sys
import atexit as _atexit import atexit as _atexit
@ -21,8 +23,10 @@ from direct.stdpy.thread import stack_size, _newname, _local as local
from panda3d import core from panda3d import core
_sleep = core.Thread.sleep _sleep = core.Thread.sleep
from collections.abc import Callable, Iterable, Mapping
from time import time as _time from time import time as _time
from traceback import format_exc as _format_exc from traceback import format_exc as _format_exc
from typing import Any
__all__ = ['get_ident', 'active_count', 'Condition', 'current_thread', __all__ = ['get_ident', 'active_count', 'Condition', 'current_thread',
'enumerate', 'main_thread', 'TIMEOUT_MAX', 'enumerate', 'main_thread', 'TIMEOUT_MAX',
@ -51,12 +55,12 @@ if __debug__:
class _Verbose(object): class _Verbose(object):
def __init__(self, verbose=None): def __init__(self, verbose: bool | None = None) -> None:
if verbose is None: if verbose is None:
verbose = _VERBOSE verbose = _VERBOSE
self.__verbose = verbose self.__verbose = verbose
def _note(self, format, *args): def _note(self, format: str, *args: Any) -> None:
if self.__verbose: if self.__verbose:
format = format % args format = format % args
format = "%s: %s\n" % ( format = "%s: %s\n" % (
@ -66,9 +70,9 @@ if __debug__:
else: else:
# Disable this when using "python -O" # Disable this when using "python -O"
class _Verbose(object): # type: ignore[no-redef] class _Verbose(object): # type: ignore[no-redef]
def __init__(self, verbose=None): def __init__(self, verbose: bool | None = None) -> None:
pass pass
def _note(self, *args): def _note(self, *args) -> None:
pass pass
# Support for profile and trace hooks # Support for profile and trace hooks
@ -88,15 +92,15 @@ def settrace(func):
Lock = _allocate_lock Lock = _allocate_lock
def RLock(*args, **kwargs): def RLock(verbose: bool | None = None) -> _RLock:
return _RLock(*args, **kwargs) return _RLock(verbose)
class _RLock(_Verbose): class _RLock(_Verbose):
def __init__(self, verbose=None): def __init__(self, verbose: bool | None = None) -> None:
_Verbose.__init__(self, verbose) _Verbose.__init__(self, verbose)
self.__block = _allocate_lock() self.__block = _allocate_lock()
self.__owner = None self.__owner: Thread | None = None
self.__count = 0 self.__count = 0
def __repr__(self): def __repr__(self):
@ -105,13 +109,13 @@ class _RLock(_Verbose):
self.__owner and self.__owner.getName(), self.__owner and self.__owner.getName(),
self.__count) self.__count)
def acquire(self, blocking=1): def acquire(self, blocking: bool = True) -> bool:
me = currentThread() me = currentThread()
if self.__owner is me: if self.__owner is me:
self.__count = self.__count + 1 self.__count = self.__count + 1
if __debug__: if __debug__:
self._note("%s.acquire(%s): recursive success", self, blocking) self._note("%s.acquire(%s): recursive success", self, blocking)
return 1 return True
rc = self.__block.acquire(blocking) rc = self.__block.acquire(blocking)
if rc: if rc:
self.__owner = me self.__owner = me
@ -125,7 +129,7 @@ class _RLock(_Verbose):
__enter__ = acquire __enter__ = acquire
def release(self): def release(self) -> None:
me = currentThread() me = currentThread()
assert self.__owner is me, "release() of un-acquire()d lock" assert self.__owner is me, "release() of un-acquire()d lock"
self.__count = count = self.__count - 1 self.__count = count = self.__count - 1
@ -163,12 +167,12 @@ class _RLock(_Verbose):
return self.__owner is currentThread() return self.__owner is currentThread()
def Condition(*args, **kwargs): def Condition(lock: _thread.LockType | _RLock | None = None, verbose: bool | None = None) -> _Condition:
return _Condition(*args, **kwargs) return _Condition(lock, verbose)
class _Condition(_Verbose): class _Condition(_Verbose):
def __init__(self, lock=None, verbose=None): def __init__(self, lock: _thread.LockType | _RLock | None = None, verbose: bool | None = None) -> None:
_Verbose.__init__(self, verbose) _Verbose.__init__(self, verbose)
if lock is None: if lock is None:
lock = RLock() lock = RLock()
@ -180,18 +184,18 @@ class _Condition(_Verbose):
# these override the default implementations (which just call # these override the default implementations (which just call
# release() and acquire() on the lock). Ditto for _is_owned(). # release() and acquire() on the lock). Ditto for _is_owned().
try: try:
self._release_save = lock._release_save self._release_save = lock._release_save # type: ignore[method-assign, union-attr]
except AttributeError: except AttributeError:
pass pass
try: try:
self._acquire_restore = lock._acquire_restore self._acquire_restore = lock._acquire_restore # type: ignore[method-assign, union-attr]
except AttributeError: except AttributeError:
pass pass
try: try:
self._is_owned = lock._is_owned self._is_owned = lock._is_owned # type: ignore[method-assign, union-attr]
except AttributeError: except AttributeError:
pass pass
self.__waiters = [] self.__waiters: list[_thread.LockType] = []
def __enter__(self): def __enter__(self):
return self.__lock.__enter__() return self.__lock.__enter__()
@ -202,22 +206,22 @@ class _Condition(_Verbose):
def __repr__(self): def __repr__(self):
return "<Condition(%s, %d)>" % (self.__lock, len(self.__waiters)) return "<Condition(%s, %d)>" % (self.__lock, len(self.__waiters))
def _release_save(self): # pylint: disable=method-hidden def _release_save(self) -> Any: # pylint: disable=method-hidden
self.__lock.release() # No state to save self.__lock.release() # No state to save
def _acquire_restore(self, x): # pylint: disable=method-hidden def _acquire_restore(self, x) -> None: # pylint: disable=method-hidden
self.__lock.acquire() # Ignore saved state self.__lock.acquire() # Ignore saved state
def _is_owned(self): # pylint: disable=method-hidden def _is_owned(self) -> bool: # pylint: disable=method-hidden
# Return True if lock is owned by currentThread. # Return True if lock is owned by currentThread.
# This method is called only if __lock doesn't have _is_owned(). # This method is called only if __lock doesn't have _is_owned().
if self.__lock.acquire(0): if self.__lock.acquire(False):
self.__lock.release() self.__lock.release()
return False return False
else: else:
return True return True
def wait(self, timeout=None): def wait(self, timeout: float | None = None) -> None:
assert self._is_owned(), "wait() of un-acquire()d lock" assert self._is_owned(), "wait() of un-acquire()d lock"
waiter = _allocate_lock() waiter = _allocate_lock()
waiter.acquire() waiter.acquire()
@ -237,7 +241,7 @@ class _Condition(_Verbose):
endtime = _time() + timeout endtime = _time() + timeout
delay = 0.0005 # 500 us -> initial delay of 1 ms delay = 0.0005 # 500 us -> initial delay of 1 ms
while True: while True:
gotit = waiter.acquire(0) gotit = waiter.acquire(False)
if gotit: if gotit:
break break
remaining = endtime - _time() remaining = endtime - _time()
@ -258,7 +262,7 @@ class _Condition(_Verbose):
finally: finally:
self._acquire_restore(saved_state) self._acquire_restore(saved_state)
def notify(self, n=1): def notify(self, n: int = 1) -> None:
assert self._is_owned(), "notify() of un-acquire()d lock" assert self._is_owned(), "notify() of un-acquire()d lock"
__waiters = self.__waiters __waiters = self.__waiters
waiters = __waiters[:n] waiters = __waiters[:n]
@ -275,7 +279,7 @@ class _Condition(_Verbose):
except ValueError: except ValueError:
pass pass
def notifyAll(self): def notifyAll(self) -> None:
self.notify(len(self.__waiters)) self.notify(len(self.__waiters))
@ -381,7 +385,7 @@ class _Event(_Verbose):
# Active thread administration # Active thread administration
_active_limbo_lock = _allocate_lock() _active_limbo_lock = _allocate_lock()
_active = {} # maps thread id to Thread object _active: dict[int, Thread] = {} # maps thread id to Thread object
_limbo = {} _limbo = {}
@ -400,8 +404,16 @@ class Thread(_Verbose):
# Protected by _active_limbo_lock. # Protected by _active_limbo_lock.
__registered_atexit = False __registered_atexit = False
def __init__(self, group=None, target=None, name=None, def __init__(
args=(), kwargs=None, verbose=None, daemon=None): self,
group: None = None,
target: Callable[..., object] | None = None,
name: object = None,
args: Iterable[Any] = (),
kwargs: Mapping[str, Any] | None = None,
verbose: bool | None = None,
daemon: bool | None = None,
) -> None:
assert group is None, "group argument must be None for now" assert group is None, "group argument must be None for now"
_Verbose.__init__(self, verbose) _Verbose.__init__(self, verbose)
if kwargs is None: if kwargs is None:
@ -422,7 +434,7 @@ class Thread(_Verbose):
# sys.exc_info since it can be changed between instances # sys.exc_info since it can be changed between instances
self.__stderr = _sys.stderr self.__stderr = _sys.stderr
def _set_daemon(self): def _set_daemon(self) -> bool:
# Overridden in _MainThread and _DummyThread # Overridden in _MainThread and _DummyThread
return currentThread().isDaemon() return currentThread().isDaemon()
@ -437,7 +449,7 @@ class Thread(_Verbose):
status = status + " daemon" status = status + " daemon"
return "<%s(%s, %s)>" % (self.__class__.__name__, self.__name, status) return "<%s(%s, %s)>" % (self.__class__.__name__, self.__name, status)
def start(self): def start(self) -> None:
assert self.__initialized, "Thread.__init__() not called" assert self.__initialized, "Thread.__init__() not called"
assert not self.__started, "thread already started" assert not self.__started, "thread already started"
if __debug__: if __debug__:
@ -457,11 +469,11 @@ class Thread(_Verbose):
self.__started = True self.__started = True
_sleep(0.000001) # 1 usec, to let the thread run (Solaris hack) _sleep(0.000001) # 1 usec, to let the thread run (Solaris hack)
def run(self): def run(self) -> None:
if self.__target: if self.__target:
self.__target(*self.__args, **self.__kwargs) self.__target(*self.__args, **self.__kwargs)
def __bootstrap(self): def __bootstrap(self) -> None:
try: try:
self.__started = True self.__started = True
_active_limbo_lock.acquire() _active_limbo_lock.acquire()
@ -497,7 +509,7 @@ class Thread(_Verbose):
# Do the best job possible w/o a huge amt. of code to # Do the best job possible w/o a huge amt. of code to
# approximate a traceback (code ideas from # approximate a traceback (code ideas from
# Lib/traceback.py) # Lib/traceback.py)
exc_type, exc_value, exc_tb = self.__exc_info() exc_type, exc_value, exc_tb = self.__exc_info() # type: ignore[misc]
try: try:
self.__stderr.write("Exception in thread " + self.getName() + self.__stderr.write("Exception in thread " + self.getName() +
" (most likely raised during interpreter shutdown):\n") " (most likely raised during interpreter shutdown):\n")
@ -523,13 +535,13 @@ class Thread(_Verbose):
except: except:
pass pass
def __stop(self): def __stop(self) -> None:
self.__block.acquire() self.__block.acquire()
self.__stopped = True self.__stopped = True
self.__block.notifyAll() self.__block.notifyAll()
self.__block.release() self.__block.release()
def __delete(self): def __delete(self) -> None:
"Remove current thread from the dict of currently running threads." "Remove current thread from the dict of currently running threads."
# Notes about running with dummy_thread: # Notes about running with dummy_thread:
@ -563,7 +575,7 @@ class Thread(_Verbose):
finally: finally:
_active_limbo_lock.release() _active_limbo_lock.release()
def join(self, timeout=None): def join(self, timeout: float | None = None) -> None:
assert self.__initialized, "Thread.__init__() not called" assert self.__initialized, "Thread.__init__() not called"
assert self.__started, "cannot join thread before it is started" assert self.__started, "cannot join thread before it is started"
assert self is not currentThread(), "cannot join current thread" assert self is not currentThread(), "cannot join current thread"
@ -592,11 +604,11 @@ class Thread(_Verbose):
finally: finally:
self.__block.release() self.__block.release()
def getName(self): def getName(self) -> str:
assert self.__initialized, "Thread.__init__() not called" assert self.__initialized, "Thread.__init__() not called"
return self.__name return self.__name
def setName(self, name): def setName(self, name: object) -> None:
assert self.__initialized, "Thread.__init__() not called" assert self.__initialized, "Thread.__init__() not called"
self.__name = str(name) self.__name = str(name)
@ -606,7 +618,7 @@ class Thread(_Verbose):
isAlive = is_alive isAlive = is_alive
def isDaemon(self): def isDaemon(self) -> bool:
assert self.__initialized, "Thread.__init__() not called" assert self.__initialized, "Thread.__init__() not called"
return self.__daemonic return self.__daemonic
@ -654,7 +666,7 @@ class _Timer(Thread):
class _MainThread(Thread): class _MainThread(Thread):
def __init__(self): def __init__(self) -> None:
Thread.__init__(self, name="MainThread") Thread.__init__(self, name="MainThread")
self._Thread__started = True self._Thread__started = True
_active_limbo_lock.acquire() _active_limbo_lock.acquire()
@ -688,13 +700,13 @@ class _MainThread(Thread):
class _DummyThread(Thread): class _DummyThread(Thread):
def __init__(self): def __init__(self) -> None:
Thread.__init__(self, name=_newname("Dummy-%d"), daemon=True) Thread.__init__(self, name=_newname("Dummy-%d"), daemon=True)
# Thread.__block consumes an OS-level locking primitive, which # Thread.__block consumes an OS-level locking primitive, which
# can never be used by a _DummyThread. Since a _DummyThread # can never be used by a _DummyThread. Since a _DummyThread
# instance is immortal, that's bad, so release this resource. # instance is immortal, that's bad, so release this resource.
del self._Thread__block del self._Thread__block # type: ignore[attr-defined]
self._Thread__started = True self._Thread__started = True
_active_limbo_lock.acquire() _active_limbo_lock.acquire()
@ -710,7 +722,7 @@ class _DummyThread(Thread):
# Global API functions # Global API functions
def current_thread(): def current_thread() -> Thread:
try: try:
return _active[get_ident()] return _active[get_ident()]
except KeyError: except KeyError:

View File

@ -28,7 +28,7 @@ if hasattr(sys, 'getandroidapilevel'):
signal = None signal = None
else: else:
try: try:
import _signal as signal # type: ignore[import, no-redef] import _signal as signal # type: ignore[import-not-found, no-redef]
except ImportError: except ImportError:
signal = None signal = None

View File

@ -48,7 +48,7 @@ def inspectorFor(anObject):
### initializing ### initializing
def initializeInspectorMap(): def initializeInspectorMap() -> None:
global _InspectorMap global _InspectorMap
notFinishedTypes = ['BufferType', 'EllipsisType', 'FrameType', 'TracebackType', 'XRangeType'] notFinishedTypes = ['BufferType', 'EllipsisType', 'FrameType', 'TracebackType', 'XRangeType']

View File

@ -3,7 +3,7 @@
__all__ = ['ParticlePanel'] __all__ = ['ParticlePanel']
# Import Tkinter, Pmw, and the floater code from this directory tree. # Import Tkinter, Pmw, and the floater code from this directory tree.
from panda3d.core import ColorBlendAttrib, Filename, Point2, Point3, Vec3, Vec4, getModelPath from panda3d.core import ColorBlendAttrib, ConfigVariableSearchPath, Filename, Point2, Point3, Vec3, Vec4, getModelPath
from panda3d.physics import ( from panda3d.physics import (
BaseParticleEmitter, BaseParticleEmitter,
BaseParticleRenderer, BaseParticleRenderer,
@ -36,7 +36,6 @@ from panda3d.physics import (
SpriteParticleRenderer, SpriteParticleRenderer,
TangentRingEmitter, TangentRingEmitter,
) )
from panda3d.direct import getParticlePath
from direct.tkwidgets.AppShell import AppShell from direct.tkwidgets.AppShell import AppShell
from direct.tkwidgets import Dial from direct.tkwidgets import Dial
from direct.tkwidgets import Floater from direct.tkwidgets import Floater
@ -53,6 +52,10 @@ import os
import tkinter as tk import tkinter as tk
particlePath = ConfigVariableSearchPath("particle-path",
"The directories to search for particle files to be loaded.")
class ParticlePanel(AppShell): class ParticlePanel(AppShell):
# Override class variables # Override class variables
appname = 'Particle Panel' appname = 'Particle Panel'
@ -1275,7 +1278,7 @@ class ParticlePanel(AppShell):
def loadParticleEffectFromFile(self): def loadParticleEffectFromFile(self):
# Find path to particle directory # Find path to particle directory
pPath = getParticlePath() pPath = particlePath
if pPath.getNumDirectories() > 0: if pPath.getNumDirectories() > 0:
if repr(pPath.getDirectory(0)) == '.': if repr(pPath.getDirectory(0)) == '.':
path = '.' path = '.'
@ -1303,7 +1306,7 @@ class ParticlePanel(AppShell):
def saveParticleEffectToFile(self): def saveParticleEffectToFile(self):
# Find path to particle directory # Find path to particle directory
pPath = getParticlePath() pPath = particlePath
if pPath.getNumDirectories() > 0: if pPath.getNumDirectories() > 0:
if repr(pPath.getDirectory(0)) == '.': if repr(pPath.getDirectory(0)) == '.':
path = '.' path = '.'
@ -2872,6 +2875,12 @@ class ParticlePanel(AppShell):
if type == 'FT_ONE_OVER_R_CUBED': if type == 'FT_ONE_OVER_R_CUBED':
#f.setFalloffType(LinearDistanceForce.FTONEOVERRCUBED) #f.setFalloffType(LinearDistanceForce.FTONEOVERRCUBED)
f.setFalloffType(2) f.setFalloffType(2)
if type == 'FT_ONE_OVER_R_OVER_DISTANCE':
f.setFalloffType(3)
if type == 'FT_ONE_OVER_R_OVER_DISTANCE_SQUARED':
f.setFalloffType(4)
if type == 'FT_ONE_OVER_R_OVER_DISTANCE_CUBED':
f.setFalloffType(5)
def setForceCenter(vec, f = force): def setForceCenter(vec, f = force):
f.setForceCenter(Point3(vec[0], vec[1], vec[2])) f.setForceCenter(Point3(vec[0], vec[1], vec[2]))
@ -2886,7 +2895,10 @@ class ParticlePanel(AppShell):
'Set force falloff type', 'Set force falloff type',
('FT_ONE_OVER_R', ('FT_ONE_OVER_R',
'FT_ONE_OVER_R_SQUARED', 'FT_ONE_OVER_R_SQUARED',
'FT_ONE_OVER_R_CUBED'), 'FT_ONE_OVER_R_CUBED',
'FT_ONE_OVER_R_OVER_DISTANCE',
'FT_ONE_OVER_R_OVER_DISTANCE_SQUARED',
'FT_ONE_OVER_R_OVER_DISTANCE_CUBED'),
command = setFalloffType) command = setFalloffType)
self.getWidget(pageName, forceName + ' Falloff').configure( self.getWidget(pageName, forceName + ' Falloff').configure(
label_width = 16) label_width = 16)
@ -2897,6 +2909,12 @@ class ParticlePanel(AppShell):
var.set('FT_ONE_OVER_R_SQUARED') var.set('FT_ONE_OVER_R_SQUARED')
elif falloff == LinearDistanceForce.FTONEOVERRCUBED: elif falloff == LinearDistanceForce.FTONEOVERRCUBED:
var.set('FT_ONE_OVER_R_CUBED') var.set('FT_ONE_OVER_R_CUBED')
elif falloff == LinearDistanceForce.FTONEOVERROVERDISTANCE:
var.set('FT_ONE_OVER_R_OVER_DISTANCE')
elif falloff == LinearDistanceForce.FTONEOVERROVERDISTANCESQUARED:
var.set('FT_ONE_OVER_R_OVER_DISTANCE_SQUARED')
elif falloff == LinearDistanceForce.FTONEOVERROVERDISTANCECUBED:
var.set('FT_ONE_OVER_R_OVER_DISTANCE_CUBED')
vec = force.getForceCenter() vec = force.getForceCenter()
self.createVector3Entry(frame, pageName, forceName + ' Center', self.createVector3Entry(frame, pageName, forceName + ' Center',
'Set center of force', 'Set center of force',

View File

@ -1,3 +1,32 @@
----------------------- RELEASE 1.10.16 -----------------------
This maintenance release fixes some minor defects and stability issues.
* OpenAL: Support non-default coordinate systems when playing 3D audio
* Tasks: Coroutine detect now also handles coroutine subclass to support Nuitka
* Tasks: Now properly handles generators without send()
* Windows: Fixes a hang when adjusting Z-order in some situations
* Fix SparseArray methods get_lowest_off_bit() and get_lowest_on_bit()
* Fix linecache error when distributing for newer Python versions
* Fix `await AsyncFuture.gather()` returning first item instead of tuple (#1738)
* Fix use-after-free in collision system with transform cache disabled (#1733)
* Egg: Add limited forward compatibility for metallic-roughness textures
* OpenGL: fix error blitting depth texture on macOS (#1719)
* OpenGL: fix SSBO not being rebound if deleted and recreated immediately
* OpenGL: fix SSBO support not being detected in certain situations
* GLSL: Add p3d_MetallicRoughnessTexture input mapped to M_metallic_roughness
* X11: Add config variable to enable detection of autorepeat key events (#1735)
* GUI: Fix bug with PGSliderBar dragging (#1722)
* Minor thread safety things for free-threaded Python builds
* Add forward compatibility for bam version 6.46
* Fixes a harmless buffer overflow in pdtoa
* Fix compilation issues with SDL version of tinydisplay (#1708)
* bam2egg: Fix issue when having more than two tags (#1725)
* Fix "Detected leak for ... which interrogate cannot delete." error (#1743)
* Fix PythonCallbackObject crash upon destruction in some cases
* PStats: Fix crash when receiving frames out of order
* PandaFramework::close_framework() now clears task manager of tasks
----------------------- RELEASE 1.10.15 ----------------------- ----------------------- RELEASE 1.10.15 -----------------------
This release adds support for Python 3.13, and fixes some significant bugs. This release adds support for Python 3.13, and fixes some significant bugs.

View File

@ -51,6 +51,16 @@ elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GCC")
endif() endif()
# AddressSanitizer
if(ENABLE_ASAN)
if(CMAKE_CXX_COMPILER_ID MATCHES "(AppleClang|Clang|GNU)")
add_compile_options(-fsanitize=address -fno-omit-frame-pointer)
add_link_options(-fsanitize=address)
else()
message(FATAL_ERROR "ENABLE_ASAN requires GCC or Clang")
endif()
endif()
# Panda3D is now a C++14 project. # Panda3D is now a C++14 project.
set(CMAKE_CXX_STANDARD 14) set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_STANDARD_REQUIRED ON)

View File

@ -265,7 +265,7 @@ if(BUILD_INTERROGATE)
panda3d-interrogate panda3d-interrogate
GIT_REPOSITORY https://github.com/panda3d/interrogate.git GIT_REPOSITORY https://github.com/panda3d/interrogate.git
GIT_TAG 03418d6d7ddda7fb99abf27230aa42d1d8bd607e GIT_TAG 7cf2550d2c8d95b8c268aa4bb0b5602e85a086dc
PREFIX ${_interrogate_dir} PREFIX ${_interrogate_dir}
CMAKE_ARGS CMAKE_ARGS
@ -425,6 +425,14 @@ endif()
unset(_prefer_mimalloc) unset(_prefer_mimalloc)
#
# Sanitizers
#
option(ENABLE_ASAN
"Enable AddressSanitizer for detecting memory errors such as
buffer overflows, use-after-free, etc. Requires GCC or Clang." OFF)
# #
# This section relates to mobile-device/phone support and options # This section relates to mobile-device/phone support and options
# #

View File

@ -133,6 +133,7 @@ check_include_file_cxx(dirent.h PHAVE_DIRENT_H)
check_include_file_cxx(ucontext.h PHAVE_UCONTEXT_H) #TODO doesn't work on OSX, use sys/ucontext.h check_include_file_cxx(ucontext.h PHAVE_UCONTEXT_H) #TODO doesn't work on OSX, use sys/ucontext.h
check_include_file_cxx(linux/input.h PHAVE_LINUX_INPUT_H) check_include_file_cxx(linux/input.h PHAVE_LINUX_INPUT_H)
check_include_file_cxx(stdint.h PHAVE_STDINT_H) check_include_file_cxx(stdint.h PHAVE_STDINT_H)
check_include_file_cxx(execinfo.h PHAVE_EXECINFO_H)
# Do we have Posix threads? # Do we have Posix threads?
#set(HAVE_POSIX_THREADS ${CMAKE_USE_PTHREADS_INIT}) #set(HAVE_POSIX_THREADS ${CMAKE_USE_PTHREADS_INIT})

View File

@ -30,7 +30,7 @@ struct AtomicAdjust {
#include "atomicAdjustDummyImpl.h" #include "atomicAdjustDummyImpl.h"
typedef AtomicAdjustDummyImpl AtomicAdjust; typedef AtomicAdjustDummyImpl AtomicAdjust;
#elif (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7))) || (defined(__clang__) && (__clang_major__ >= 3)) #elif (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7))) || (defined(__clang__) && (__clang_major__ >= 3) && !defined(WIN32_VC) )
// GCC 4.7 and above has built-in __atomic functions for atomic operations. // GCC 4.7 and above has built-in __atomic functions for atomic operations.
// Clang 3.0 and above also supports them. // Clang 3.0 and above also supports them.

View File

@ -88,7 +88,14 @@ allocate(size_t size, TypeHandle type_handle) {
return ptr; return ptr;
#else // USE_DELETED_CHAIN #else // USE_DELETED_CHAIN
return PANDA_MALLOC_SINGLE(_buffer_size); void *ptr = PANDA_MALLOC_SINGLE(_buffer_size);
#ifdef DO_MEMORY_USAGE
type_handle.inc_memory_usage(TypeHandle::MC_singleton, _buffer_size);
#endif // DO_MEMORY_USAGE
return ptr;
#endif // USE_DELETED_CHAIN #endif // USE_DELETED_CHAIN
} }
@ -133,6 +140,11 @@ deallocate(void *ptr, TypeHandle type_handle) {
_lock.unlock(); _lock.unlock();
#else // USE_DELETED_CHAIN #else // USE_DELETED_CHAIN
#ifdef DO_MEMORY_USAGE
type_handle.dec_memory_usage(TypeHandle::MC_singleton, _buffer_size);
#endif // DO_MEMORY_USAGE
PANDA_FREE_SINGLE(ptr); PANDA_FREE_SINGLE(ptr);
#endif // USE_DELETED_CHAIN #endif // USE_DELETED_CHAIN
} }
@ -155,8 +167,9 @@ get_deleted_chain(size_t buffer_size) {
static MutexImpl lock; static MutexImpl lock;
lock.lock(); lock.lock();
static std::set<DeletedBufferChain> deleted_chains; alignas(std::set<DeletedBufferChain>) static char storage[sizeof(std::set<DeletedBufferChain>)];
DeletedBufferChain *result = (DeletedBufferChain *)&*deleted_chains.insert(DeletedBufferChain(buffer_size)).first; static auto *deleted_chains = new (storage) std::set<DeletedBufferChain>;
DeletedBufferChain *result = (DeletedBufferChain *)&*deleted_chains->insert(DeletedBufferChain(buffer_size)).first;
lock.unlock(); lock.unlock();
return result; return result;
} }

View File

@ -122,6 +122,50 @@ public:
#define ALLOC_DELETED_CHAIN_DEF(Type) \ #define ALLOC_DELETED_CHAIN_DEF(Type) \
DeletedChain< Type > Type::_deleted_chain; DeletedChain< Type > Type::_deleted_chain;
#elif defined(DO_MEMORY_USAGE)
#define ALLOC_DELETED_CHAIN(Type) \
inline void *operator new(size_t size) RETURNS_ALIGNED(MEMORY_HOOK_ALIGNMENT) { \
void *ptr = PANDA_MALLOC_SINGLE(size); \
get_type_handle(Type).inc_memory_usage(TypeHandle::MC_singleton, sizeof(Type)); \
return ptr; \
} \
inline void *operator new(size_t size, void *ptr) { \
(void) size; \
return ptr; \
} \
inline void operator delete(void *ptr) { \
if (ptr != nullptr) { \
get_type_handle(Type).dec_memory_usage(TypeHandle::MC_singleton, sizeof(Type)); \
PANDA_FREE_SINGLE(ptr); \
} \
} \
inline void operator delete(void *, void *) { \
} \
inline void *operator new[](size_t size) RETURNS_ALIGNED(MEMORY_HOOK_ALIGNMENT) { \
void *ptr = PANDA_MALLOC_SINGLE(size); \
get_type_handle(Type).inc_memory_usage(TypeHandle::MC_array, sizeof(Type)); \
return ptr; \
} \
inline void *operator new[](size_t size, void *ptr) { \
(void) size; \
return ptr; \
} \
inline void operator delete[](void *ptr) { \
if (ptr != nullptr) { \
get_type_handle(Type).dec_memory_usage(TypeHandle::MC_array, sizeof(Type)); \
PANDA_FREE_SINGLE(ptr); \
} \
} \
inline void operator delete[](void *, void *) { \
} \
inline static bool validate_ptr(const void *ptr) { \
return (ptr != nullptr); \
}
#define ALLOC_DELETED_CHAIN_DECL(Type) ALLOC_DELETED_CHAIN(Type)
#define ALLOC_DELETED_CHAIN_DEF(Type)
#else // USE_DELETED_CHAIN #else // USE_DELETED_CHAIN
#define ALLOC_DELETED_CHAIN(Type) \ #define ALLOC_DELETED_CHAIN(Type) \

View File

@ -96,6 +96,9 @@
#elif defined(__arm__) #elif defined(__arm__)
#define DTOOL_PLATFORM "linux_arm" #define DTOOL_PLATFORM "linux_arm"
#elif defined(__riscv)
#define DTOOL_PLATFORM "linux_riscv"
#elif defined(__ppc__) #elif defined(__ppc__)
#define DTOOL_PLATFORM "linux_ppc" #define DTOOL_PLATFORM "linux_ppc"

View File

@ -618,6 +618,9 @@ determine_page_size() const {
_page_size = (size_t)sysinfo.dwPageSize; _page_size = (size_t)sysinfo.dwPageSize;
#elif defined(ANDROID)
_page_size = getpagesize();
#else #else
// Posix case. // Posix case.
_page_size = sysconf(_SC_PAGESIZE); _page_size = sysconf(_SC_PAGESIZE);

View File

@ -454,6 +454,12 @@ patomic_notify_one(volatile uint32_t *value) {
#elif defined(_WIN32) #elif defined(_WIN32)
_patomic_wake_one_func((void *)value); _patomic_wake_one_func((void *)value);
#elif defined(__APPLE__) #elif defined(__APPLE__)
#ifndef __arm64__
if (UNLIKELY(__ulock_wake == nullptr || __ulock_wait == nullptr)) {
_patomic_notify_all(value);
return;
}
#endif
__ulock_wake(UL_COMPARE_AND_WAIT, (void *)value, 0); __ulock_wake(UL_COMPARE_AND_WAIT, (void *)value, 0);
#elif defined(HAVE_POSIX_THREADS) #elif defined(HAVE_POSIX_THREADS)
_patomic_notify_all(value); _patomic_notify_all(value);
@ -472,6 +478,12 @@ patomic_notify_all(volatile uint32_t *value) {
#elif defined(_WIN32) #elif defined(_WIN32)
_patomic_wake_all_func((void *)value); _patomic_wake_all_func((void *)value);
#elif defined(__APPLE__) #elif defined(__APPLE__)
#ifndef __arm64__
if (UNLIKELY(__ulock_wake == nullptr || __ulock_wait == nullptr)) {
_patomic_notify_all(value);
return;
}
#endif
__ulock_wake(UL_COMPARE_AND_WAIT | ULF_WAKE_ALL, (void *)value, 0); __ulock_wake(UL_COMPARE_AND_WAIT | ULF_WAKE_ALL, (void *)value, 0);
#elif defined(HAVE_POSIX_THREADS) #elif defined(HAVE_POSIX_THREADS)
_patomic_notify_all(value); _patomic_notify_all(value);

View File

@ -125,7 +125,7 @@ initialize_wait(volatile VOID *addr, PVOID cmp, SIZE_T size, DWORD timeout) {
return emulated_wait(addr, cmp, size, timeout); return emulated_wait(addr, cmp, size, timeout);
} }
#elif !defined(CPPPARSER) && !defined(__linux__) && !defined(__APPLE__) && defined(HAVE_POSIX_THREADS) #elif !defined(CPPPARSER) && !defined(__linux__) && (!defined(__APPLE__) || !defined(__arm64__)) && defined(HAVE_POSIX_THREADS)
// Same as above, but using pthreads. // Same as above, but using pthreads.
struct alignas(64) WaitTableEntry { struct alignas(64) WaitTableEntry {

View File

@ -36,9 +36,6 @@
// Undocumented API, see https://outerproduct.net/futex-dictionary.html // Undocumented API, see https://outerproduct.net/futex-dictionary.html
#define UL_COMPARE_AND_WAIT 1 #define UL_COMPARE_AND_WAIT 1
#define ULF_WAKE_ALL 0x00000100 #define ULF_WAKE_ALL 0x00000100
extern "C" int __ulock_wait(uint32_t op, void *addr, uint64_t value, uint32_t timeout);
extern "C" int __ulock_wake(uint32_t op, void *addr, uint64_t wake_value);
#endif #endif
#if defined(THREAD_DUMMY_IMPL) || defined(THREAD_SIMPLE_IMPL) #if defined(THREAD_DUMMY_IMPL) || defined(THREAD_SIMPLE_IMPL)
@ -173,9 +170,17 @@ ALWAYS_INLINE void patomic_notify_all(volatile uint32_t *value);
EXPCL_DTOOL_DTOOLBASE extern BOOL (__stdcall *_patomic_wait_func)(volatile VOID *, PVOID, SIZE_T, DWORD); EXPCL_DTOOL_DTOOLBASE extern BOOL (__stdcall *_patomic_wait_func)(volatile VOID *, PVOID, SIZE_T, DWORD);
EXPCL_DTOOL_DTOOLBASE extern void (__stdcall *_patomic_wake_one_func)(PVOID); EXPCL_DTOOL_DTOOLBASE extern void (__stdcall *_patomic_wake_one_func)(PVOID);
EXPCL_DTOOL_DTOOLBASE extern void (__stdcall *_patomic_wake_all_func)(PVOID); EXPCL_DTOOL_DTOOLBASE extern void (__stdcall *_patomic_wake_all_func)(PVOID);
#elif !defined(__linux__) && !defined(__APPLE__) && defined(HAVE_POSIX_THREADS) #elif defined(__APPLE__) && defined(__arm64__)
extern "C" int __ulock_wait(uint32_t op, void *addr, uint64_t value, uint32_t timeout);
extern "C" int __ulock_wake(uint32_t op, void *addr, uint64_t wake_value);
#elif !defined(__linux__) && defined(HAVE_POSIX_THREADS)
EXPCL_DTOOL_DTOOLBASE void _patomic_wait(const volatile uint32_t *value, uint32_t old); EXPCL_DTOOL_DTOOLBASE void _patomic_wait(const volatile uint32_t *value, uint32_t old);
EXPCL_DTOOL_DTOOLBASE void _patomic_notify_all(volatile uint32_t *value); EXPCL_DTOOL_DTOOLBASE void _patomic_notify_all(volatile uint32_t *value);
#ifdef __APPLE__
// Use conditionally since we can't count on support before 10.12.
extern "C" int __ulock_wait(uint32_t op, void *addr, uint64_t value, uint32_t timeout) __attribute__((weak_import));
extern "C" int __ulock_wake(uint32_t op, void *addr, uint64_t wake_value) __attribute__((weak_import));
#endif
#endif #endif
#include "patomic.I" #include "patomic.I"

View File

@ -252,7 +252,7 @@ inline static unsigned CountDecimalDigit32(uint32_t n) {
} }
inline static void DigitGen(const DiyFp& W, const DiyFp& Mp, uint64_t delta, char* buffer, int* len, int* K) { inline static void DigitGen(const DiyFp& W, const DiyFp& Mp, uint64_t delta, char* buffer, int* len, int* K) {
static const uint32_t kPow10[] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000, 0, 0, 0, 0, 0 }; static const uint32_t kPow10[] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000, 0, 0, 0, 0, 0, 0 };
const DiyFp one(uint64_t(1) << -Mp.e, Mp.e); const DiyFp one(uint64_t(1) << -Mp.e, Mp.e);
const DiyFp wp_w = Mp - W; const DiyFp wp_w = Mp - W;
uint32_t p1 = static_cast<uint32_t>(Mp.f >> -one.e); uint32_t p1 = static_cast<uint32_t>(Mp.f >> -one.e);

View File

@ -46,13 +46,16 @@ public:
typedef std::vector<Type, allocator> base_class; typedef std::vector<Type, allocator> base_class;
typedef typename base_class::size_type size_type; typedef typename base_class::size_type size_type;
explicit pvector(TypeHandle type_handle = pvector_type_handle) : base_class(allocator(type_handle)) { } pvector() : base_class(allocator(get_type_handle(pvector<Type>))) { }
explicit pvector(TypeHandle type_handle) : base_class(allocator(type_handle)) { }
pvector(const pvector<Type> &copy) : base_class(copy) { } pvector(const pvector<Type> &copy) : base_class(copy) { }
pvector(pvector<Type> &&from) noexcept : base_class(std::move(from)) {}; pvector(pvector<Type> &&from) noexcept : base_class(std::move(from)) {};
explicit pvector(size_type n, TypeHandle type_handle = pvector_type_handle) : base_class(n, Type(), allocator(type_handle)) { } explicit pvector(size_type n, TypeHandle type_handle = get_type_handle(pvector<Type>)) : base_class(n, Type(), allocator(type_handle)) { }
explicit pvector(size_type n, const Type &value, TypeHandle type_handle = pvector_type_handle) : base_class(n, value, allocator(type_handle)) { } explicit pvector(size_type n, const Type &value, TypeHandle type_handle = get_type_handle(pvector<Type>)) : base_class(n, value, allocator(type_handle)) { }
pvector(const Type *begin, const Type *end, TypeHandle type_handle = pvector_type_handle) : base_class(begin, end, allocator(type_handle)) { } pvector(const Type *begin, const Type *end, TypeHandle type_handle = get_type_handle(pvector<Type>)) : base_class(allocator(type_handle)) {
pvector(std::initializer_list<Type> init, TypeHandle type_handle = pvector_type_handle) : base_class(std::move(init), allocator(type_handle)) { } this->insert(this->end(), begin, end);
}
pvector(std::initializer_list<Type> init, TypeHandle type_handle = get_type_handle(pvector<Type>)) : base_class(std::move(init), allocator(type_handle)) { }
pvector<Type> &operator =(const pvector<Type> &copy) { pvector<Type> &operator =(const pvector<Type> &copy) {
base_class::operator =(copy); base_class::operator =(copy);

View File

@ -26,6 +26,7 @@ TypeHandle double_type_handle;
TypeHandle float_type_handle; TypeHandle float_type_handle;
TypeHandle string_type_handle; TypeHandle string_type_handle;
TypeHandle wstring_type_handle; TypeHandle wstring_type_handle;
TypeHandle vector_uchar_type_handle;
TypeHandle long_p_type_handle; TypeHandle long_p_type_handle;
TypeHandle int_p_type_handle; TypeHandle int_p_type_handle;
@ -59,6 +60,7 @@ void init_system_type_handles() {
register_type(float_type_handle, "float"); register_type(float_type_handle, "float");
register_type(string_type_handle, "string"); register_type(string_type_handle, "string");
register_type(wstring_type_handle, "wstring"); register_type(wstring_type_handle, "wstring");
register_type(vector_uchar_type_handle, "vector_uchar");
register_type(int_p_type_handle, "int*"); register_type(int_p_type_handle, "int*");
register_type(short_p_type_handle, "short*"); register_type(short_p_type_handle, "short*");

View File

@ -19,6 +19,9 @@
#include "typeHandle.h" #include "typeHandle.h"
#include "typeRegistry.h" #include "typeRegistry.h"
template<class T>
class pvector;
/** /**
* This inline function is just a convenient way to call * This inline function is just a convenient way to call
* TypeRegistry::register_type(), along with zero to four * TypeRegistry::register_type(), along with zero to four
@ -88,6 +91,7 @@ extern TypeHandle EXPCL_DTOOL_DTOOLBASE double_type_handle;
extern TypeHandle EXPCL_DTOOL_DTOOLBASE float_type_handle; extern TypeHandle EXPCL_DTOOL_DTOOLBASE float_type_handle;
extern TypeHandle EXPCL_DTOOL_DTOOLBASE string_type_handle; extern TypeHandle EXPCL_DTOOL_DTOOLBASE string_type_handle;
extern TypeHandle EXPCL_DTOOL_DTOOLBASE wstring_type_handle; extern TypeHandle EXPCL_DTOOL_DTOOLBASE wstring_type_handle;
extern TypeHandle EXPCL_DTOOL_DTOOLBASE vector_uchar_type_handle;
extern TypeHandle long_p_type_handle; extern TypeHandle long_p_type_handle;
extern TypeHandle int_p_type_handle; extern TypeHandle int_p_type_handle;
@ -175,6 +179,16 @@ INLINE TypeHandle _get_type_handle(const std::wstring *) {
return wstring_type_handle; return wstring_type_handle;
} }
template<class T>
INLINE TypeHandle _get_type_handle(const pvector<T> *) {
return pvector_type_handle;
}
template<>
INLINE TypeHandle _get_type_handle(const pvector<unsigned char> *) {
return vector_uchar_type_handle;
}
template<> template<>
INLINE TypeHandle _get_type_handle(const long * const *) { INLINE TypeHandle _get_type_handle(const long * const *) {
return long_p_type_handle; return long_p_type_handle;

View File

@ -69,6 +69,8 @@ set(P3DTOOLUTIL_IGATEEXT
globPattern_ext.h globPattern_ext.h
iostream_ext.cxx iostream_ext.cxx
iostream_ext.h iostream_ext.h
pyenv_init.cxx
pyenv_init.h
textEncoder_ext.cxx textEncoder_ext.cxx
textEncoder_ext.h textEncoder_ext.h
) )

View File

@ -0,0 +1,50 @@
/**
* PANDA 3D SOFTWARE
* Copyright (c) Carnegie Mellon University. All rights reserved.
*
* All use of this software is subject to the terms of the revised BSD
* license. You should have received a copy of this license along
* with this source code in a file named "LICENSE."
*
* @file console_preamble.js
* @author rdb
* @date 2025-02-03
*/
if (ENVIRONMENT_IS_NODE) {
Module["preInit"] = Module["preInit"] || [];
Module["preInit"].push(function() {
if (typeof process === "object" && typeof process.env === "object") {
// These are made up by emscripten if we don't set them to undefined
ENV['USER'] = undefined;
ENV['LOGNAME'] = undefined;
ENV['PATH'] = undefined;
ENV['PWD'] = undefined;
ENV['HOME'] = undefined;
ENV['LANG'] = undefined;
ENV['_'] = undefined;
for (var variable in process.env) {
ENV[variable] = process.env[variable];
}
}
addOnPreMain(function preloadNodeEnv() {
var sp = stackSave();
var set_binary_name = wasmExports["_set_binary_name"];
if (set_binary_name && typeof __filename === "string") {
set_binary_name(stringToUTF8OnStack(__filename));
}
var set_env_var = wasmExports["_set_env_var"];
if (set_env_var) {
for (var variable in ENV) {
var value = ENV[variable];
if (value !== undefined) {
set_env_var(stringToUTF8OnStack(variable), stringToUTF8OnStack(value));
}
}
}
stackRestore(sp);
});
});
}

View File

@ -125,12 +125,18 @@ static const char *const libp3dtool_filenames[] = {
#if defined(__EMSCRIPTEN__) && !defined(CPPPARSER) #if defined(__EMSCRIPTEN__) && !defined(CPPPARSER)
extern "C" void EMSCRIPTEN_KEEPALIVE extern "C" void EMSCRIPTEN_KEEPALIVE
_set_env_var(ExecutionEnvironment *ptr, const char *var, const char *value) { _set_env_var(const char *var, const char *value) {
ExecutionEnvironment *ptr = ExecutionEnvironment::get_ptr();
ptr->_variables[std::string(var)] = std::string(value); ptr->_variables[std::string(var)] = std::string(value);
} }
extern "C" void EMSCRIPTEN_KEEPALIVE
_set_binary_name(const char *path) {
ExecutionEnvironment::set_binary_name(std::string(path));
}
#endif #endif
// Linux with GNU libc does have global argvargc variables, but we can't // Linux with GNU libc does have global argv/argc variables, but we can't
// safely access them at stat init time--at least, not in libc5. (It does seem // safely access them at stat init time--at least, not in libc5. (It does seem
// to work with glibc2, however.) // to work with glibc2, however.)
@ -584,17 +590,10 @@ read_environment_variables() {
} }
} }
#elif defined(__EMSCRIPTEN__) #elif defined(__EMSCRIPTEN__)
// We only have environment variables if we're running in node.js. // The environment variables get loaded in by the .js file before main()
#ifndef CPPPARSER // using the _set_env_var exported function, defined above. Trying to load
EM_ASM({ // env vars at static init time otherwise makes some optimizations more
if (typeof process === 'object' && typeof process.env === 'object') { // difficult, notably wasm-ctor-eval/wizer.
for (var variable in process.env) {
__set_env_var($0, stringToUTF8OnStack(variable),
stringToUTF8OnStack(process.env[variable]));
}
}
}, this);
#endif
#elif defined(HAVE_PROC_SELF_ENVIRON) #elif defined(HAVE_PROC_SELF_ENVIRON)
// In some cases, we may have a file called procselfenviron that may be read // In some cases, we may have a file called procselfenviron that may be read

View File

@ -22,10 +22,10 @@
#include <map> #include <map>
#if defined(__EMSCRIPTEN__) && !defined(CPPPARSER) #if defined(__EMSCRIPTEN__) && !defined(CPPPARSER)
class ExecutionEnvironment;
extern "C" void EMSCRIPTEN_KEEPALIVE extern "C" void EMSCRIPTEN_KEEPALIVE
_set_env_var(ExecutionEnvironment *ptr, const char *var, const char *value); _set_env_var(const char *var, const char *value);
extern "C" void EMSCRIPTEN_KEEPALIVE
_set_binary_name(const char *path);
#endif #endif
/** /**
@ -98,7 +98,7 @@ private:
static ExecutionEnvironment *_global_ptr; static ExecutionEnvironment *_global_ptr;
#ifdef __EMSCRIPTEN__ #ifdef __EMSCRIPTEN__
friend void ::_set_env_var(ExecutionEnvironment *ptr, const char *var, const char *value); friend void ::_set_env_var(const char *var, const char *value);
#endif #endif
}; };

View File

@ -67,7 +67,7 @@ __init__(PyObject *path) {
return; return;
} }
PyObject *path_str = PyObject_CallFunctionObjArgs(fspath, path, nullptr); PyObject *path_str = PyObject_CallOneArg(fspath, path);
Py_DECREF(fspath); Py_DECREF(fspath);
if (path_str == nullptr) { if (path_str == nullptr) {
return; return;

View File

@ -134,8 +134,9 @@ readinto(PyObject *b) {
Py_buffer view; Py_buffer view;
if (PyObject_GetBuffer(b, &view, PyBUF_CONTIG) == -1) { if (PyObject_GetBuffer(b, &view, PyBUF_CONTIG) == -1) {
PyErr_SetString(PyExc_TypeError, PyErr_Format(PyExc_TypeError,
"write() requires a contiguous, read-write bytes-like object"); "readinto() requires a contiguous, read-write bytes-like object, not '%.100s'",
Py_TYPE(b)->tp_name);
return 0; return 0;
} }
@ -260,7 +261,9 @@ write(PyObject *b) {
Py_buffer view; Py_buffer view;
if (PyObject_GetBuffer(b, &view, PyBUF_CONTIG_RO) == -1) { if (PyObject_GetBuffer(b, &view, PyBUF_CONTIG_RO) == -1) {
PyErr_SetString(PyExc_TypeError, "write() requires a contiguous buffer"); PyErr_Format(PyExc_TypeError,
"write() requires a bytes-like object, not '%.100s'",
Py_TYPE(b)->tp_name);
return; return;
} }

View File

@ -22,6 +22,9 @@
#include <iostream> #include <iostream>
#include "py_panda.h" #include "py_panda.h"
using std::ostream;
using std::istream;
/** /**
* These classes define the extension methods for istream and ostream, which * These classes define the extension methods for istream and ostream, which
* are called instead of any C++ methods with the same prototype. * are called instead of any C++ methods with the same prototype.

View File

@ -19,11 +19,15 @@ using std::string;
static Filename resolve_dso(const DSearchPath &path, const Filename &filename) { static Filename resolve_dso(const DSearchPath &path, const Filename &filename) {
if (filename.is_local()) { if (filename.is_local()) {
if ((path.get_num_directories()==1)&&(path.get_directory(0)=="<auto>")) { if ((path.get_num_directories()==1)&&(path.get_directory(0)=="<auto>")) {
#ifdef ANDROID
return filename;
#else
// This is a special case, meaning to search in the same directory in // This is a special case, meaning to search in the same directory in
// which libp3dtool.dll, or the exe, was started from. // which libp3dtool.dll, or the exe, was started from.
Filename dtoolpath = ExecutionEnvironment::get_dtool_name(); Filename dtoolpath = ExecutionEnvironment::get_dtool_name();
DSearchPath spath(dtoolpath.get_dirname()); DSearchPath spath(dtoolpath.get_dirname());
return spath.find_file(filename); return spath.find_file(filename);
#endif
} else { } else {
return path.find_file(filename); return path.find_file(filename);
} }
@ -119,7 +123,13 @@ get_dso_symbol(void *handle, const string &name) {
void * void *
load_dso(const DSearchPath &path, const Filename &filename) { load_dso(const DSearchPath &path, const Filename &filename) {
Filename abspath = resolve_dso(path, filename); Filename abspath = resolve_dso(path, filename);
#ifdef ANDROID
// We just try to load it on Android, because we can't verify right now
// whether it might just be an unextracted library.
if (abspath.empty()) {
#else
if (!abspath.is_regular_file()) { if (!abspath.is_regular_file()) {
#endif
// Make sure the error flag is cleared, to prevent a subsequent call to // Make sure the error flag is cleared, to prevent a subsequent call to
// load_dso_error() from returning a previously stored error. // load_dso_error() from returning a previously stored error.
dlerror(); dlerror();

View File

@ -1,4 +1,5 @@
#include "filename_ext.cxx" #include "filename_ext.cxx"
#include "globPattern_ext.cxx" #include "globPattern_ext.cxx"
#include "iostream_ext.cxx" #include "iostream_ext.cxx"
#include "pyenv_init.cxx"
#include "textEncoder_ext.cxx" #include "textEncoder_ext.cxx"

View File

@ -0,0 +1,80 @@
/**
* PANDA 3D SOFTWARE
* Copyright (c) Carnegie Mellon University. All rights reserved.
*
* All use of this software is subject to the terms of the revised BSD
* license. You should have received a copy of this license along
* with this source code in a file named "LICENSE."
*
* @file pyenv_init.cxx
* @author rdb
* @date 2025-02-02
*/
#include "pyenv_init.h"
#include "py_panda.h"
#include "executionEnvironment.h"
/**
* Called when panda3d.core is initialized, does some initialization specific
* to the Python environment.
*/
void
pyenv_init() {
// MAIN_DIR needs to be set very early; this seems like a convenient place
// to do that. Perhaps we'll find a better place for this in the future.
static bool initialized_main_dir = false;
if (!initialized_main_dir) {
/*if (interrogatedb_cat.is_debug()) {
// Good opportunity to print this out once, at startup.
interrogatedb_cat.debug()
<< "Python " << version << "\n";
}*/
if (!ExecutionEnvironment::has_environment_variable("MAIN_DIR")) {
// Grab the __main__ module and extract its __file__ attribute.
Filename main_dir;
PyObject *main_module = PyImport_ImportModule("__main__");
PyObject *file_attr = nullptr;
if (main_module != nullptr) {
file_attr = PyObject_GetAttrString(main_module, "__file__");
} else {
std::cerr << "Warning: unable to import __main__\n";
}
if (file_attr == nullptr) {
// Must be running in the interactive interpreter. Use the CWD.
main_dir = ExecutionEnvironment::get_cwd();
} else {
#if PY_MAJOR_VERSION >= 3
Py_ssize_t length;
wchar_t *buffer = PyUnicode_AsWideCharString(file_attr, &length);
if (buffer != nullptr) {
main_dir = Filename::from_os_specific_w(std::wstring(buffer, length));
main_dir.make_absolute();
main_dir = main_dir.get_dirname();
PyMem_Free(buffer);
}
#else
char *buffer;
Py_ssize_t length;
if (PyString_AsStringAndSize(file_attr, &buffer, &length) != -1) {
main_dir = Filename::from_os_specific(std::string(buffer, length));
main_dir.make_absolute();
main_dir = main_dir.get_dirname();
}
#endif
else {
std::cerr << "Invalid string for __main__.__file__\n";
}
}
ExecutionEnvironment::shadow_environment_variable("MAIN_DIR", main_dir.to_os_specific());
PyErr_Clear();
}
initialized_main_dir = true;
}
// Also, while we are at it, initialize the thread swap hook.
#if defined(HAVE_THREADS) && defined(SIMPLE_THREADS)
global_thread_state_swap = PyThreadState_Swap;
#endif
}

View File

@ -0,0 +1,14 @@
/**
* PANDA 3D SOFTWARE
* Copyright (c) Carnegie Mellon University. All rights reserved.
*
* All use of this software is subject to the terms of the revised BSD
* license. You should have received a copy of this license along
* with this source code in a file named "LICENSE."
*
* @file pyenv_init.h
* @author rdb
* @date 2025-02-02
*/
extern "C" void pyenv_init();

View File

@ -94,7 +94,7 @@ encode_wchar(char32_t ch, TextEncoder::Encoding encoding) {
* given encoding. * given encoding.
*/ */
PyObject *Extension<TextEncoder>:: PyObject *Extension<TextEncoder>::
encode_wtext(const wstring &wtext, TextEncoder::Encoding encoding) { encode_wtext(const std::wstring &wtext, TextEncoder::Encoding encoding) {
std::string value = TextEncoder::encode_wtext(wtext, encoding); std::string value = TextEncoder::encode_wtext(wtext, encoding);
return PyBytes_FromStringAndSize((char *)value.data(), (Py_ssize_t)value.size()); return PyBytes_FromStringAndSize((char *)value.data(), (Py_ssize_t)value.size());
} }

View File

@ -19,3 +19,25 @@
#define NAME vector_uchar #define NAME vector_uchar
#include "vector_src.cxx" #include "vector_src.cxx"
/**
* Writes a hex representation of the bytes to the output stream.
*/
std::ostream &
operator << (std::ostream &out, const vector_uchar &data) {
static const char nibble_to_hex[] = "0123456789abcdef";
if (data.empty()) {
return out;
}
out << nibble_to_hex[(data[0] & 0xf0) >> 4];
out << nibble_to_hex[data[0] & 0xf];
for (size_t i = 1; i < data.size(); ++i) {
out << ' ';
out << nibble_to_hex[(data[i] & 0xf0) >> 4];
out << nibble_to_hex[data[i] & 0xf];
}
return out;
}

View File

@ -30,4 +30,7 @@
#include "vector_src.h" #include "vector_src.h"
EXPCL_DTOOL_DTOOLUTIL std::ostream &
operator << (std::ostream &out, const vector_uchar &data);
#endif #endif

View File

@ -2,7 +2,6 @@ set(P3IGATERUNTIME_HEADERS
interrogate_request.h interrogate_request.h
py_compat.h py_compat.h
py_panda.h py_panda.I py_panda.h py_panda.I
py_wrappers.h
) )
install(FILES ${P3IGATERUNTIME_HEADERS} COMPONENT CoreDevel DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/panda3d) install(FILES ${P3IGATERUNTIME_HEADERS} COMPONENT CoreDevel DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/panda3d)

View File

@ -44,12 +44,12 @@ EXPCL_INTERROGATEDB void interrogate_request_database(const char *database_filen
/* The more sophisticated interface uses these structures. */ /* The more sophisticated interface uses these structures. */
typedef struct { typedef struct InterrogateUniqueNameDef {
const char *name; const char *name;
int index_offset; int index_offset;
} InterrogateUniqueNameDef; } InterrogateUniqueNameDef;
typedef struct { typedef struct InterrogateModuleDef {
int file_identifier; int file_identifier;
const char *library_name; const char *library_name;

View File

@ -219,6 +219,14 @@ INLINE PyObject *_PyLong_Lshift(PyObject *a, size_t shiftby) {
} }
#endif #endif
#ifndef PY_VECTORCALL_ARGUMENTS_OFFSET
# define PY_VECTORCALL_ARGUMENTS_OFFSET (((size_t)1) << (8 * sizeof(size_t) - 1))
#endif
#if PY_VERSION_HEX < 0x030800B1
# define PyVectorcall_NARGS(n) ((n) & ~PY_VECTORCALL_ARGUMENTS_OFFSET)
#endif
/* Python 3.9 */ /* Python 3.9 */
#if PY_VERSION_HEX < 0x030900A4 && !defined(Py_IS_TYPE) #if PY_VERSION_HEX < 0x030900A4 && !defined(Py_IS_TYPE)
@ -229,6 +237,10 @@ INLINE PyObject *_PyLong_Lshift(PyObject *a, size_t shiftby) {
# define PyCFunction_CheckExact(op) (Py_IS_TYPE(op, &PyCFunction_Type)) # define PyCFunction_CheckExact(op) (Py_IS_TYPE(op, &PyCFunction_Type))
#endif #endif
#if PY_VERSION_HEX < 0x030900A4
# define PyObject_Vectorcall(callable, args, nargsf, kwnames) (_PyObject_Vectorcall((callable), (args), (nargsf), (kwnames)))
#endif
#if PY_VERSION_HEX < 0x03090000 #if PY_VERSION_HEX < 0x03090000
INLINE PyObject *PyObject_CallNoArgs(PyObject *func) { INLINE PyObject *PyObject_CallNoArgs(PyObject *func) {
#if PY_VERSION_HEX >= 0x03080000 #if PY_VERSION_HEX >= 0x03080000
@ -368,6 +380,12 @@ PyDict_GetItemStringRef(PyObject *mp, const char *key, PyObject **result) {
# define Py_END_CRITICAL_SECTION2() } # define Py_END_CRITICAL_SECTION2() }
#endif #endif
/* Python 3.14 */
#if PY_VERSION_HEX < 0x030E00A8
# define PyUnstable_Object_IsUniquelyReferenced(op) (Py_REFCNT((op)) == 1)
#endif
/* Other Python implementations */ /* Other Python implementations */
#endif // HAVE_PYTHON #endif // HAVE_PYTHON

View File

@ -59,63 +59,6 @@ DtoolInstance_GetPointer(PyObject *self, T *&into, Dtool_PyTypedObject &target_c
return false; return false;
} }
/**
* Function to create a hash from a wrapped Python object.
*/
INLINE Py_hash_t DtoolInstance_HashPointer(PyObject *self) {
if (self != nullptr && DtoolInstance_Check(self)) {
return (Py_hash_t)(intptr_t)DtoolInstance_VOID_PTR(self);
}
return -1;
}
/**
* Python 2-style comparison function that compares objects by pointer.
*/
INLINE int DtoolInstance_ComparePointers(PyObject *v1, PyObject *v2) {
void *v1_this = DtoolInstance_Check(v1) ? DtoolInstance_VOID_PTR(v1) : nullptr;
void *v2_this = DtoolInstance_Check(v2) ? DtoolInstance_VOID_PTR(v2) : nullptr;
if (v1_this != nullptr && v2_this != nullptr) {
return (v1_this > v2_this) - (v1_this < v2_this);
} else {
return (v1 > v2) - (v1 < v2);
}
}
/**
* Rich comparison function that compares objects by pointer.
*/
INLINE PyObject *DtoolInstance_RichComparePointers(PyObject *v1, PyObject *v2, int op) {
int cmpval = DtoolInstance_ComparePointers(v1, v2);
Py_RETURN_RICHCOMPARE(cmpval, 0, op);
}
/**
* Utility function for assigning a PyObject pointer while managing refcounts.
*/
ALWAYS_INLINE void
Dtool_Assign_PyObject(PyObject *&ptr, PyObject *value) {
PyObject *prev_value = ptr;
if (prev_value != value) {
ptr = Py_XNewRef(value);
Py_XDECREF(prev_value);
}
}
/**
* Converts the enum value to a C long.
*/
INLINE long Dtool_EnumValue_AsLong(PyObject *value) {
PyObject *val = PyObject_GetAttrString(value, "value");
if (val != nullptr) {
long as_long = PyLongOrInt_AS_LONG(val);
Py_DECREF(val);
return as_long;
} else {
return -1;
}
}
/** /**
* These functions wrap a pointer for a class that defines get_type_handle(). * These functions wrap a pointer for a class that defines get_type_handle().
*/ */
@ -167,23 +110,6 @@ DTool_PyInit_Finalize(PyObject *self, void *local_this, Dtool_PyTypedObject *typ
return 0; return 0;
} }
/**
* Checks that the tuple is empty.
*/
ALWAYS_INLINE bool
Dtool_CheckNoArgs(PyObject *args) {
return PyTuple_GET_SIZE(args) == 0;
}
/**
* Checks that the tuple is empty, and that the dict is empty or NULL.
*/
ALWAYS_INLINE bool
Dtool_CheckNoArgs(PyObject *args, PyObject *kwds) {
return PyTuple_GET_SIZE(args) == 0 &&
(kwds == nullptr || PyDict_GET_SIZE(kwds) == 0);
}
/** /**
* The following functions wrap an arbitrary C++ value into a PyObject. * The following functions wrap an arbitrary C++ value into a PyObject.
*/ */
@ -324,7 +250,8 @@ ALWAYS_INLINE PyObject *Dtool_WrapValue(PyObject *value) {
return value; return value;
} }
ALWAYS_INLINE PyObject *Dtool_WrapValue(const vector_uchar &value) { template<class Allocator>
ALWAYS_INLINE PyObject *Dtool_WrapValue(const std::vector<unsigned char, Allocator> &value) {
#if PY_MAJOR_VERSION >= 3 #if PY_MAJOR_VERSION >= 3
return PyBytes_FromStringAndSize((char *)value.data(), (Py_ssize_t)value.size()); return PyBytes_FromStringAndSize((char *)value.data(), (Py_ssize_t)value.size());
#else #else

View File

@ -8,15 +8,14 @@
#include <set> #include <set>
#include <map> #include <map>
#include <string> #include <string>
#include <vector>
#ifdef USE_DEBUG_PYTHON #ifdef USE_DEBUG_PYTHON
#define Py_DEBUG #define Py_DEBUG
#endif #endif
#include "pnotify.h" #include "pnotify.h"
#include "vector_uchar.h"
#include "register_type.h" #include "register_type.h"
#include "interrogate_request.h"
#if defined(HAVE_PYTHON) && !defined(CPPPARSER) #if defined(HAVE_PYTHON) && !defined(CPPPARSER)
@ -24,8 +23,6 @@
#include "py_compat.h" #include "py_compat.h"
#include <structmember.h> #include <structmember.h>
using namespace std;
// this is tempory .. untill this is glued better into the panda build system // this is tempory .. untill this is glued better into the panda build system
#if defined(_WIN32) && !defined(LINK_ALL_STATIC) #if defined(_WIN32) && !defined(LINK_ALL_STATIC)
@ -86,83 +83,6 @@ struct Dtool_PyTypedObject {
CoerceFunction _Dtool_Coerce; CoerceFunction _Dtool_Coerce;
}; };
// This is now simply a forward declaration. The actual definition is created
// by the code generator.
#define Define_Dtool_Class(MODULE_NAME, CLASS_NAME, PUBLIC_NAME) \
extern Dtool_PyTypedObject Dtool_##CLASS_NAME;
// More Macro(s) to Implement class functions.. Usually used if C++ needs type
// information
#define Define_Dtool_new(CLASS_NAME,CNAME)\
static PyObject *Dtool_new_##CLASS_NAME(PyTypeObject *type, PyObject *args, PyObject *kwds) {\
(void) args; (void) kwds;\
PyObject *self = type->tp_alloc(type, 0);\
((Dtool_PyInstDef *)self)->_signature = PY_PANDA_SIGNATURE;\
((Dtool_PyInstDef *)self)->_My_Type = &Dtool_##CLASS_NAME;\
return self;\
}
// The following used to be in the above macro, but it doesn't seem to be
// necessary as tp_alloc memsets the object to 0.
// ((Dtool_PyInstDef *)self)->_ptr_to_object = NULL;
// ((Dtool_PyInstDef *)self)->_memory_rules = false;
// ((Dtool_PyInstDef *)self)->_is_const = false;
// Delete functions..
#ifdef NDEBUG
#define Define_Dtool_FreeInstance_Private(CLASS_NAME,CNAME)\
static void Dtool_FreeInstance_##CLASS_NAME(PyObject *self) {\
Py_TYPE(self)->tp_free(self);\
}
#else // NDEBUG
#define Define_Dtool_FreeInstance_Private(CLASS_NAME,CNAME)\
static void Dtool_FreeInstance_##CLASS_NAME(PyObject *self) {\
if (DtoolInstance_VOID_PTR(self) != nullptr) {\
if (((Dtool_PyInstDef *)self)->_memory_rules) {\
std::cerr << "Detected leak for " << #CLASS_NAME \
<< " which interrogate cannot delete.\n"; \
}\
}\
Py_TYPE(self)->tp_free(self);\
}
#endif // NDEBUG
#define Define_Dtool_FreeInstance(CLASS_NAME,CNAME)\
static void Dtool_FreeInstance_##CLASS_NAME(PyObject *self) {\
if (DtoolInstance_VOID_PTR(self) != nullptr) {\
if (((Dtool_PyInstDef *)self)->_memory_rules) {\
delete (CNAME *)DtoolInstance_VOID_PTR(self);\
}\
}\
Py_TYPE(self)->tp_free(self);\
}
#define Define_Dtool_FreeInstanceRef(CLASS_NAME,CNAME)\
static void Dtool_FreeInstance_##CLASS_NAME(PyObject *self) {\
if (DtoolInstance_VOID_PTR(self) != nullptr) {\
if (((Dtool_PyInstDef *)self)->_memory_rules) {\
unref_delete((CNAME *)DtoolInstance_VOID_PTR(self));\
}\
}\
Py_TYPE(self)->tp_free(self);\
}
#define Define_Dtool_FreeInstanceRef_Private(CLASS_NAME,CNAME)\
static void Dtool_FreeInstance_##CLASS_NAME(PyObject *self) {\
if (DtoolInstance_VOID_PTR(self) != nullptr) {\
if (((Dtool_PyInstDef *)self)->_memory_rules) {\
unref_delete((ReferenceCount *)(CNAME *)DtoolInstance_VOID_PTR(self));\
}\
}\
Py_TYPE(self)->tp_free(self);\
}
#define Define_Dtool_Simple_FreeInstance(CLASS_NAME, CNAME)\
static void Dtool_FreeInstance_##CLASS_NAME(PyObject *self) {\
((Dtool_InstDef_##CLASS_NAME *)self)->_value.~##CLASS_NAME();\
Py_TYPE(self)->tp_free(self);\
}
// Extract the PyTypeObject pointer corresponding to a Dtool_PyTypedObject. // Extract the PyTypeObject pointer corresponding to a Dtool_PyTypedObject.
#define Dtool_GetPyTypeObject(type) (&(type)->_PyType) #define Dtool_GetPyTypeObject(type) (&(type)->_PyType)
@ -178,31 +98,12 @@ static void Dtool_FreeInstance_##CLASS_NAME(PyObject *self) {\
#define DtoolInstance_INIT_PTR(obj, ptr) { ((Dtool_PyInstDef *)obj)->_ptr_to_object = (void*)(ptr); } #define DtoolInstance_INIT_PTR(obj, ptr) { ((Dtool_PyInstDef *)obj)->_ptr_to_object = (void*)(ptr); }
#define DtoolInstance_UPCAST(obj, type) (((Dtool_PyInstDef *)(obj))->_My_Type->_Dtool_UpcastInterface((obj), &(type))) #define DtoolInstance_UPCAST(obj, type) (((Dtool_PyInstDef *)(obj))->_My_Type->_Dtool_UpcastInterface((obj), &(type)))
// ** HACK ** allert.. Need to keep a runtime type dictionary ... that is
// forward declared of typed object. We rely on the fact that typed objects
// are uniquly defined by an integer.
#if PY_VERSION_HEX >= 0x030d0000
class Dtool_TypeMap : public std::map<std::string, Dtool_PyTypedObject *> {
public:
PyMutex _lock { 0 };
};
#else
typedef std::map<std::string, Dtool_PyTypedObject *> Dtool_TypeMap;
#endif
EXPCL_PYPANDA Dtool_TypeMap *Dtool_GetGlobalTypeMap();
class DtoolProxy { class DtoolProxy {
public: public:
mutable PyObject *_self; mutable PyObject *_self;
TypeHandle _type; TypeHandle _type;
}; };
EXPCL_PYPANDA void DtoolProxy_Init(DtoolProxy *proxy, PyObject *self,
Dtool_PyTypedObject &classdef,
TypeRegistry::PythonWrapFunc *wrap_func);
/** /**
*/ */
@ -218,23 +119,11 @@ EXPCL_PYPANDA bool Dtool_Call_ExtractThisPointer_NonConst(PyObject *self, Dtool_
template<class T> INLINE bool DtoolInstance_GetPointer(PyObject *self, T *&into); template<class T> INLINE bool DtoolInstance_GetPointer(PyObject *self, T *&into);
template<class T> INLINE bool DtoolInstance_GetPointer(PyObject *self, T *&into, Dtool_PyTypedObject &classdef); template<class T> INLINE bool DtoolInstance_GetPointer(PyObject *self, T *&into, Dtool_PyTypedObject &classdef);
INLINE Py_hash_t DtoolInstance_HashPointer(PyObject *self);
INLINE int DtoolInstance_ComparePointers(PyObject *v1, PyObject *v2);
INLINE PyObject *DtoolInstance_RichComparePointers(PyObject *v1, PyObject *v2, int op);
// Functions related to error reporting.
EXPCL_PYPANDA bool _Dtool_CheckErrorOccurred();
#ifdef NDEBUG
#define Dtool_CheckErrorOccurred() (UNLIKELY(PyErr_Occurred() != nullptr))
#else
#define Dtool_CheckErrorOccurred() (UNLIKELY(_Dtool_CheckErrorOccurred()))
#endif
EXPCL_PYPANDA PyObject *Dtool_Raise_AssertionError(); EXPCL_PYPANDA PyObject *Dtool_Raise_AssertionError();
EXPCL_PYPANDA PyObject *Dtool_Raise_TypeError(const char *message); EXPCL_PYPANDA PyObject *Dtool_Raise_TypeError(const char *message);
EXPCL_PYPANDA PyObject *Dtool_Raise_ArgTypeError(PyObject *obj, int param, const char *function_name, const char *type_name); EXPCL_PYPANDA PyObject *Dtool_Raise_ArgTypeError(PyObject *obj, int param, const char *function_name, const char *type_name);
EXPCL_PYPANDA PyObject *Dtool_Raise_AttributeError(PyObject *obj, const char *attribute); EXPCL_PYPANDA PyObject *Dtool_Raise_AttributeError(PyObject *obj, const char *attribute);
EXPCL_PYPANDA int Dtool_Raise_CantDeleteAttributeError(const char *attribute);
EXPCL_PYPANDA PyObject *_Dtool_Raise_BadArgumentsError(); EXPCL_PYPANDA PyObject *_Dtool_Raise_BadArgumentsError();
EXPCL_PYPANDA PyObject *_Dtool_Raise_BadArgumentsError(const char *message); EXPCL_PYPANDA PyObject *_Dtool_Raise_BadArgumentsError(const char *message);
@ -250,31 +139,6 @@ EXPCL_PYPANDA int _Dtool_Raise_BadArgumentsError_Int(const char *message);
#define Dtool_Raise_BadArgumentsError_Int(x) _Dtool_Raise_BadArgumentsError_Int(x) #define Dtool_Raise_BadArgumentsError_Int(x) _Dtool_Raise_BadArgumentsError_Int(x)
#endif #endif
// These functions are similar to Dtool_WrapValue, except that they also
// contain code for checking assertions and exceptions when compiling with
// NDEBUG mode on.
EXPCL_PYPANDA PyObject *_Dtool_Return_None();
EXPCL_PYPANDA PyObject *Dtool_Return_Bool(bool value);
EXPCL_PYPANDA PyObject *_Dtool_Return(PyObject *value);
#ifdef NDEBUG
#define Dtool_Return_None() (LIKELY(PyErr_Occurred() == nullptr) ? (Py_NewRef(Py_None)) : nullptr)
#define Dtool_Return(value) (LIKELY(PyErr_Occurred() == nullptr) ? value : nullptr)
#else
#define Dtool_Return_None() _Dtool_Return_None()
#define Dtool_Return(value) _Dtool_Return(value)
#endif
ALWAYS_INLINE void Dtool_Assign_PyObject(PyObject *&ptr, PyObject *value);
/**
* Wrapper around Python 3.4's enum library, which does not have a C API.
*/
EXPCL_PYPANDA PyTypeObject *Dtool_EnumType_Create(const char *name, PyObject *names,
const char *module = nullptr);
INLINE long Dtool_EnumValue_AsLong(PyObject *value);
/** /**
*/ */
@ -291,102 +155,9 @@ template<class T> INLINE PyObject *DTool_CreatePyInstance(T *obj, bool memory_ru
template<class T> INLINE PyObject *DTool_CreatePyInstanceTyped(const T *obj, bool memory_rules); template<class T> INLINE PyObject *DTool_CreatePyInstanceTyped(const T *obj, bool memory_rules);
template<class T> INLINE PyObject *DTool_CreatePyInstanceTyped(T *obj, bool memory_rules); template<class T> INLINE PyObject *DTool_CreatePyInstanceTyped(T *obj, bool memory_rules);
// Macro(s) class definition .. Used to allocate storage and init some values
// for a Dtool Py Type object.
// struct Dtool_PyTypedObject Dtool_##CLASS_NAME;
#define Define_Module_Class_Internal(MODULE_NAME,CLASS_NAME,CNAME)\
extern struct Dtool_PyTypedObject Dtool_##CLASS_NAME;\
static int Dtool_Init_##CLASS_NAME(PyObject *self, PyObject *args, PyObject *kwds);\
static PyObject *Dtool_new_##CLASS_NAME(PyTypeObject *type, PyObject *args, PyObject *kwds);
#define Define_Module_Class(MODULE_NAME,CLASS_NAME,CNAME,PUBLIC_NAME)\
Define_Module_Class_Internal(MODULE_NAME,CLASS_NAME,CNAME)\
Define_Dtool_new(CLASS_NAME,CNAME)\
Define_Dtool_FreeInstance(CLASS_NAME,CNAME)\
Define_Dtool_Class(MODULE_NAME,CLASS_NAME,PUBLIC_NAME)
#define Define_Module_Class_Private(MODULE_NAME,CLASS_NAME,CNAME,PUBLIC_NAME)\
Define_Module_Class_Internal(MODULE_NAME,CLASS_NAME,CNAME)\
Define_Dtool_new(CLASS_NAME,CNAME)\
Define_Dtool_FreeInstance_Private(CLASS_NAME,CNAME)\
Define_Dtool_Class(MODULE_NAME,CLASS_NAME,PUBLIC_NAME)
#define Define_Module_ClassRef_Private(MODULE_NAME,CLASS_NAME,CNAME,PUBLIC_NAME)\
Define_Module_Class_Internal(MODULE_NAME,CLASS_NAME,CNAME)\
Define_Dtool_new(CLASS_NAME,CNAME)\
Define_Dtool_FreeInstanceRef_Private(CLASS_NAME,CNAME)\
Define_Dtool_Class(MODULE_NAME,CLASS_NAME,PUBLIC_NAME)
#define Define_Module_ClassRef(MODULE_NAME,CLASS_NAME,CNAME,PUBLIC_NAME)\
Define_Module_Class_Internal(MODULE_NAME,CLASS_NAME,CNAME)\
Define_Dtool_new(CLASS_NAME,CNAME)\
Define_Dtool_FreeInstanceRef(CLASS_NAME,CNAME)\
Define_Dtool_Class(MODULE_NAME,CLASS_NAME,PUBLIC_NAME)
// The finalizer for simple instances. // The finalizer for simple instances.
INLINE int DTool_PyInit_Finalize(PyObject *self, void *This, Dtool_PyTypedObject *type, bool memory_rules, bool is_const); INLINE int DTool_PyInit_Finalize(PyObject *self, void *This, Dtool_PyTypedObject *type, bool memory_rules, bool is_const);
// A heler function to glu methed definition together .. that can not be done
// at code generation time becouse of multiple generation passes in
// interigate..
typedef std::map<std::string, PyMethodDef *> MethodDefmap;
// We need a way to runtime merge compile units into a python "Module" .. this
// is done with the fallowing structors and code.. along with the support of
// interigate_module
struct Dtool_TypeDef {
const char *const name;
Dtool_PyTypedObject *type;
};
struct LibraryDef {
PyMethodDef *const _methods;
const Dtool_TypeDef *const _types;
Dtool_TypeDef *const _external_types;
const InterrogateModuleDef *const _module_def;
};
#if PY_MAJOR_VERSION >= 3
EXPCL_PYPANDA PyObject *Dtool_PyModuleInitHelper(const LibraryDef *defs[], PyModuleDef *module_def);
#else
EXPCL_PYPANDA PyObject *Dtool_PyModuleInitHelper(const LibraryDef *defs[], const char *modulename);
#endif
// HACK.... Be carefull Dtool_BorrowThisReference This function can be used to
// grab the "THIS" pointer from an object and use it Required to support fom
// historical inharatence in the for of "is this instance of"..
EXPCL_PYPANDA PyObject *Dtool_BorrowThisReference(PyObject *self, PyObject *args);
#define DTOOL_PyObject_HashPointer DtoolInstance_HashPointer
#define DTOOL_PyObject_ComparePointers DtoolInstance_ComparePointers
EXPCL_PYPANDA PyObject *
copy_from_make_copy(PyObject *self, PyObject *noargs);
EXPCL_PYPANDA PyObject *
copy_from_copy_constructor(PyObject *self, PyObject *noargs);
EXPCL_PYPANDA PyObject *
map_deepcopy_to_copy(PyObject *self, PyObject *args);
/**
* These functions check whether the arguments passed to a function conform to
* certain expectations.
*/
ALWAYS_INLINE bool Dtool_CheckNoArgs(PyObject *args);
ALWAYS_INLINE bool Dtool_CheckNoArgs(PyObject *args, PyObject *kwds);
EXPCL_PYPANDA bool Dtool_ExtractArg(PyObject **result, PyObject *args,
PyObject *kwds, const char *keyword);
EXPCL_PYPANDA bool Dtool_ExtractArg(PyObject **result, PyObject *args,
PyObject *kwds);
EXPCL_PYPANDA bool Dtool_ExtractOptionalArg(PyObject **result, PyObject *args,
PyObject *kwds, const char *keyword);
EXPCL_PYPANDA bool Dtool_ExtractOptionalArg(PyObject **result, PyObject *args,
PyObject *kwds);
/** /**
* These functions convert a C++ value into the corresponding Python object. * These functions convert a C++ value into the corresponding Python object.
* This used to be generated by the code generator, but it seems more reliable * This used to be generated by the code generator, but it seems more reliable
@ -411,7 +182,8 @@ ALWAYS_INLINE PyObject *Dtool_WrapValue(char value);
ALWAYS_INLINE PyObject *Dtool_WrapValue(wchar_t value); ALWAYS_INLINE PyObject *Dtool_WrapValue(wchar_t value);
ALWAYS_INLINE PyObject *Dtool_WrapValue(std::nullptr_t); ALWAYS_INLINE PyObject *Dtool_WrapValue(std::nullptr_t);
ALWAYS_INLINE PyObject *Dtool_WrapValue(PyObject *value); ALWAYS_INLINE PyObject *Dtool_WrapValue(PyObject *value);
ALWAYS_INLINE PyObject *Dtool_WrapValue(const vector_uchar &value); template<class Allocator>
ALWAYS_INLINE PyObject *Dtool_WrapValue(const std::vector<unsigned char, Allocator> &value);
#if PY_MAJOR_VERSION >= 0x02060000 #if PY_MAJOR_VERSION >= 0x02060000
ALWAYS_INLINE PyObject *Dtool_WrapValue(Py_buffer *value); ALWAYS_INLINE PyObject *Dtool_WrapValue(Py_buffer *value);
@ -422,9 +194,12 @@ ALWAYS_INLINE PyObject *Dtool_WrapValue(const std::pair<T1, T2> &value);
EXPCL_PYPANDA Dtool_PyTypedObject *Dtool_GetSuperBase(); EXPCL_PYPANDA Dtool_PyTypedObject *Dtool_GetSuperBase();
#include "py_panda.I" /**
* Creates a Python generator object from a next() function.
*/
EXPCL_PYPANDA PyObject *Dtool_NewGenerator(PyObject *self, iternextfunc func);
#include "py_wrappers.h" #include "py_panda.I"
#endif // HAVE_PYTHON && !CPPPARSER #endif // HAVE_PYTHON && !CPPPARSER

View File

@ -1,61 +0,0 @@
/**
* @file py_wrappers.h
* @author rdb
* @date 2017-11-26
*/
#ifndef PY_WRAPPERS_H
#define PY_WRAPPERS_H
#include "py_panda.h"
#ifdef HAVE_PYTHON
/**
* These classes are returned from properties that require a subscript
* interface, ie. something.children[i] = 3.
*/
struct Dtool_WrapperBase {
PyObject_HEAD;
PyObject *_self;
const char *_name;
};
struct Dtool_SequenceWrapper {
Dtool_WrapperBase _base;
lenfunc _len_func;
ssizeargfunc _getitem_func;
};
struct Dtool_MutableSequenceWrapper {
Dtool_WrapperBase _base;
lenfunc _len_func;
ssizeargfunc _getitem_func;
ssizeobjargproc _setitem_func;
PyObject *(*_insert_func)(PyObject *, size_t, PyObject *);
};
struct Dtool_MappingWrapper {
union {
Dtool_WrapperBase _base;
Dtool_SequenceWrapper _keys;
};
binaryfunc _getitem_func;
objobjargproc _setitem_func;
};
struct Dtool_GeneratorWrapper {
Dtool_WrapperBase _base;
iternextfunc _iternext_func;
};
EXPCL_PYPANDA Dtool_SequenceWrapper *Dtool_NewSequenceWrapper(PyObject *self, const char *name);
EXPCL_PYPANDA Dtool_MutableSequenceWrapper *Dtool_NewMutableSequenceWrapper(PyObject *self, const char *name);
EXPCL_PYPANDA Dtool_MappingWrapper *Dtool_NewMappingWrapper(PyObject *self, const char *name);
EXPCL_PYPANDA Dtool_MappingWrapper *Dtool_NewMutableMappingWrapper(PyObject *self, const char *name);
EXPCL_PYPANDA PyObject *Dtool_NewGenerator(PyObject *self, iternextfunc func);
EXPCL_PYPANDA PyObject *Dtool_NewStaticProperty(PyTypeObject *obj, const PyGetSetDef *getset);
#endif // HAVE_PYTHON
#endif // PY_WRAPPERS_H

View File

View File

@ -52,4 +52,9 @@ PyObject _Py_FalseStruct;
typedef void *visitproc; typedef void *visitproc;
typedef enum {
PyRefTracer_CREATE = 0,
PyRefTracer_DESTROY = 1,
} PyRefTracerEvent;
#endif // PYTHON_H #endif // PYTHON_H

View File

@ -0,0 +1 @@
#include <xmmintrin.h>

View File

@ -0,0 +1,29 @@
#pragma once
#include <initializer_list>
namespace std {
class random_device;
class seed_seq;
template<class IntType = int> class uniform_int_distribution;
template<class RealType = double> class uniform_real_distribution;
class bernoulli_distribution;
template<class IntType = int> class binomial_distribution;
template<class IntType = int> class geometric_distribution;
template<class IntType = int> class negative_binomial_distribution;
template<class IntType = int> class poisson_distribution;
template<class RealType = double> class exponential_distribution;
template<class RealType = double> class gamma_distribution;
template<class RealType = double> class weibull_distribution;
template<class RealType = double> class extreme_value_distribution;
template<class RealType = double> class normal_distribution;
template<class RealType = double> class lognormal_distribution;
template<class RealType = double> class chi_squared_distribution;
template<class RealType = double> class cauchy_distribution;
template<class RealType = double> class fisher_f_distribution;
template<class RealType = double> class student_t_distribution;
template<class IntType = int> class discrete_distribution;
template<class RealType = double> class piecewise_constant_distribution;
template<class RealType = double> class piecewise_linear_distribution;
}

View File

@ -97,7 +97,6 @@ reload_implicit_pages() {
} }
_implicit_pages.clear(); _implicit_pages.clear();
#ifndef ANDROID
// If we are running inside a deployed application, see if it exposes // If we are running inside a deployed application, see if it exposes
// information about how the PRC data should be initialized. // information about how the PRC data should be initialized.
struct BlobInfo { struct BlobInfo {
@ -129,11 +128,13 @@ reload_implicit_pages() {
// const BlobInfo *blobinfo = (const BlobInfo *)dlsym(RTLD_SELF, "blobinfo"); // const BlobInfo *blobinfo = (const BlobInfo *)dlsym(RTLD_SELF, "blobinfo");
#elif defined(__EMSCRIPTEN__) #elif defined(__EMSCRIPTEN__)
const BlobInfo *blobinfo = nullptr; const BlobInfo *blobinfo = nullptr;
#elif defined(ANDROID)
const BlobInfo *blobinfo = nullptr;
#else #else
const BlobInfo *blobinfo = (const BlobInfo *)dlsym(dlopen(nullptr, RTLD_NOW), "blobinfo"); const BlobInfo *blobinfo = (const BlobInfo *)dlsym(dlopen(nullptr, RTLD_NOW), "blobinfo");
#endif #endif
if (blobinfo == nullptr) { if (blobinfo == nullptr) {
#if !defined(_WIN32) && !defined(__EMSCRIPTEN__) #if !defined(_WIN32) && !defined(__EMSCRIPTEN__) && !defined(ANDROID)
// Clear the error flag. // Clear the error flag.
dlerror(); dlerror();
#endif #endif
@ -482,7 +483,6 @@ reload_implicit_pages() {
} }
} }
} }
#endif // ANDROID
if (!_loaded_implicit) { if (!_loaded_implicit) {
config_initialized(); config_initialized();
@ -492,6 +492,21 @@ reload_implicit_pages() {
_currently_loading = false; _currently_loading = false;
invalidate_cache(); invalidate_cache();
// These important variables are updated here to avoid recursion, since
// they may be accessed by the PRC system.
ConfigVariableBool notify_timestamp
("notify-timestamp", false,
PRC_DESC("Set true to output the date & time with each notify message."));
NotifyCategory::_notify_timestamp = notify_timestamp;
#ifndef NDEBUG
ConfigVariableBool check_debug_notify_protect
("check-debug-notify-protect", false,
PRC_DESC("Set true to issue a warning message if a debug or spam "
"notify output is not protected within an if statement."));
NotifyCategory::_check_debug_notify_protect = check_debug_notify_protect;
#endif
#ifdef USE_PANDAFILESTREAM #ifdef USE_PANDAFILESTREAM
// Update this very low-level config variable here, for lack of any better // Update this very low-level config variable here, for lack of any better
// place. // place.

View File

@ -27,6 +27,7 @@
#endif #endif
#ifdef ANDROID #ifdef ANDROID
#include <sys/stat.h>
#include <android/log.h> #include <android/log.h>
#include "androidLogStream.h" #include "androidLogStream.h"
#endif #endif
@ -35,6 +36,18 @@
#include "emscriptenLogStream.h" #include "emscriptenLogStream.h"
#endif #endif
#ifndef NDEBUG
#ifdef PHAVE_EXECINFO_H
#include <execinfo.h> // for backtrace()
#include <dlfcn.h>
#include <cxxabi.h>
#endif
#ifdef _WIN32
#include <dbghelp.h>
#endif
#endif // NDEBUG
using std::cerr; using std::cerr;
using std::cout; using std::cout;
using std::ostream; using std::ostream;
@ -348,15 +361,16 @@ assert_failure(const char *expression, int line,
<< expression << " at line " << line << " of " << source_file; << expression << " at line " << line << " of " << source_file;
string message = message_str.str(); string message = message_str.str();
if (!_assert_failed) { Notify *self = ptr();
if (!self->_assert_failed) {
// We only save the first assertion failure message, as this is usually // We only save the first assertion failure message, as this is usually
// the most meaningful when several occur in a row. // the most meaningful when several occur in a row.
_assert_failed = true; self->_assert_failed = true;
_assert_error_message = message; self->_assert_error_message = message;
} }
if (has_assert_handler()) { if (self->has_assert_handler()) {
return (*_assert_handler)(expression, line, source_file); return (*self->_assert_handler)(expression, line, source_file);
} }
#ifdef ANDROID #ifdef ANDROID
@ -380,6 +394,33 @@ assert_failure(const char *expression, int line,
// Make sure the error message has been flushed to the output. // Make sure the error message has been flushed to the output.
nout.flush(); nout.flush();
// Capture and list a stack trace.
#ifdef NDEBUG
#elif defined(PHAVE_EXECINFO_H)
void *trace[64];
int size = backtrace(trace, 64);
if (size > 0) {
// Remove the frame(s) corresponding to the current function.
void **tracep = trace;
void *return_addr = __builtin_return_address(0);
for (int i = 0; i < size; ++i) {
if (trace[i] == return_addr) {
tracep = trace + (i + 1);
size -= i + 1;
break;
}
}
write_backtrace(tracep, size);
}
#elif defined(_WIN32)
const ULONG max_size = 62;
void *trace[max_size];
int size = CaptureStackBackTrace(1, max_size, trace, nullptr);
if (size > 0) {
write_backtrace(trace, size);
}
#endif
#ifdef _MSC_VER #ifdef _MSC_VER
// How to trigger an exception in VC++ that offers to take us into the // How to trigger an exception in VC++ that offers to take us into the
// debugger? abort() doesn't do it. We used to be able to assert(false), // debugger? abort() doesn't do it. We used to be able to assert(false),
@ -411,6 +452,141 @@ assert_failure(const char *expression, int line,
return true; return true;
} }
/**
*
*/
void Notify::
write_backtrace(void **trace, int size) {
std::ostream &out = nout;
#ifdef NDEBUG
#elif defined(PHAVE_EXECINFO_H)
char namebuf[128];
for (int i = 0; i < size; ++i) {
void *addr = trace[i];
Dl_info info;
if (dladdr((char *)addr - 1, &info) != 0) {
const char *name = nullptr;
if (info.dli_sname != nullptr) {
int status = 0;
size_t size = 0;
char *demangled = nullptr;
if (info.dli_sname[0] == '_') {
size = sizeof(namebuf) - 1;
demangled = abi::__cxa_demangle(info.dli_sname, namebuf, &size, &status);
}
if (status == 0 && demangled != nullptr) {
namebuf[size] = 0;
name = demangled;
//if (strncmp(name, "Notify::assert_failure", 22) == 0) {
//continue;
//}
} else {
name = info.dli_sname;
}
}
out << "[" << addr << "] ";
if (info.dli_fname != nullptr) {
const char *slash = strrchr(info.dli_fname, '/');
out << std::left << std::setw(30) << (slash ? slash + 1 : info.dli_fname) << " ";
}
if (name != nullptr) {
out << name;
} else {
out << info.dli_saddr;
}
int offset = reinterpret_cast<uintptr_t>(addr) - reinterpret_cast<uintptr_t>(info.dli_saddr);
if (offset > 0) {
out << " + " << offset;
}
out << "\n";
} else {
out << "[" << addr << "]\n";
}
}
#elif defined(_WIN32)
HMODULE handle = LoadLibraryA("dbghelp.dll");
if (!handle) {
return;
}
auto pSymInitialize = (BOOL (WINAPI *)(HANDLE, PCSTR, BOOL))GetProcAddress(handle, "SymInitialize");
auto pSymCleanup = (BOOL (WINAPI *)(HANDLE))GetProcAddress(handle, "SymCleanup");
auto pSymFromAddr = (BOOL (WINAPI *)(HANDLE, DWORD64, DWORD64 *, PSYMBOL_INFO))GetProcAddress(handle, "SymFromAddr");
auto pSymGetModuleInfo64 = (BOOL (WINAPI *)(HANDLE, DWORD64, PIMAGEHLP_MODULE64))GetProcAddress(handle, "SymGetModuleInfo64");
auto pSymGetLineFromAddr64 = (BOOL (WINAPI *)(HANDLE, DWORD64, PDWORD, PIMAGEHLP_LINE64))GetProcAddress(handle, "SymGetLineFromAddr64");
if (!pSymInitialize || !pSymCleanup || !pSymFromAddr) {
FreeLibrary(handle);
return;
}
HANDLE process = GetCurrentProcess();
pSymInitialize(process, nullptr, TRUE);
for (int i = 0; i < size; ++i) {
DWORD64 addr = (DWORD64)trace[i];
alignas(SYMBOL_INFO) char buffer[sizeof(SYMBOL_INFO) + 256] = {0};
SYMBOL_INFO *symbol = (SYMBOL_INFO *)buffer;
symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
symbol->MaxNameLen = 255;
out << "[" << (void *)addr << "]";
// Show the name of the library.
IMAGEHLP_MODULE64 module;
module.SizeOfStruct = sizeof(module);
const char *basename = "???";
if (pSymGetModuleInfo64 && pSymGetModuleInfo64(process, addr, &module)) {
const char *slash = strrchr(module.ImageName, '\\');
basename = (slash ? slash + 1 : module.ImageName);
}
// Look up the symbol name. If the reported name comes from the export
// table and the address is way off, ignore it, it's probably not right.
if (pSymFromAddr(process, addr, nullptr, symbol) &&
((symbol->Flags & SYMFLAG_EXPORT) == 0 || addr - (DWORD64)symbol->Address < 0x4000)) {
out << " " << std::left << std::setw(30) << basename << " " << symbol->Name;
DWORD64 offset = addr - (DWORD64)(symbol->Address);
if (offset > 0) {
out << " + " << offset;
}
// Look up filename + line number if available.
IMAGEHLP_LINE64 line;
DWORD displacement = 0;
line.SizeOfStruct = sizeof(line);
if (pSymGetLineFromAddr64(process, addr, &displacement, &line)) {
const char *slash = strrchr(line.FileName, '\\');
const char *basename = (slash ? slash + 1 : line.FileName);
out << " (" << basename << ":" << line.LineNumber;
//if (displacement > 0) {
// out << " + " << displacement;
//}
out << ")";
}
} else {
out << " " << basename;
}
out << "\n";
}
pSymCleanup(process);
FreeLibrary(handle);
#endif
out.flush();
}
/** /**
* Given a string, one of "debug", "info", "warning", etc., return the * Given a string, one of "debug", "info", "warning", etc., return the
* corresponding Severity level, or NS_unspecified if none of the strings * corresponding Severity level, or NS_unspecified if none of the strings
@ -460,22 +636,7 @@ config_initialized() {
// notify-output even after the initial import of Panda3D modules. However, // notify-output even after the initial import of Panda3D modules. However,
// it cannot be changed after the first time it is set. // it cannot be changed after the first time it is set.
#if defined(ANDROID) #if defined(__EMSCRIPTEN__)
// Android redirects stdio and stderr to /dev/null,
// but does provide its own logging system. We use a special
// type of stream that redirects it to Android's log system.
Notify *ptr = Notify::ptr();
for (int severity = 0; severity <= NS_fatal; ++severity) {
int priority = ANDROID_LOG_UNKNOWN;
if (severity != NS_unspecified) {
priority = severity + 1;
}
ptr->_log_streams[severity] = new AndroidLogStream(priority);
}
#elif defined(__EMSCRIPTEN__)
// We have no writable filesystem in JavaScript. Instead, we set up a // We have no writable filesystem in JavaScript. Instead, we set up a
// special stream that logs straight into the Javascript console. // special stream that logs straight into the Javascript console.
@ -540,11 +701,38 @@ config_initialized() {
} }
#endif // BUILD_IPHONE #endif // BUILD_IPHONE
} }
#ifdef ANDROID #ifdef ANDROID
for (int severity = 0; severity <= NS_fatal; ++severity) {
ptr->_log_streams[severity] = ptr->_ostream_ptr;
}
} else { } else {
// By default, we always redirect the notify stream to the Android log. // By default, we always redirect the notify stream to the Android log,
// except if we are running from the adb shell. We decide this based
// on whether stderr is redirected to /dev/null.
Notify *ptr = Notify::ptr(); Notify *ptr = Notify::ptr();
ptr->set_ostream_ptr(new AndroidLogStream(ANDROID_LOG_INFO), true); struct stat a, b;
if (fstat(STDERR_FILENO, &a) == 0 && stat("/dev/null", &b) == 0 &&
a.st_dev == b.st_dev && a.st_ino == b.st_ino) {
// Android redirects stdio and stderr to /dev/null,
// but does provide its own logging system. We use a special
// type of stream that redirects it to Android's log system.
for (int severity = 0; severity <= NS_fatal; ++severity) {
int priority = ANDROID_LOG_UNKNOWN;
if (severity != NS_unspecified) {
priority = severity + 1;
}
ptr->_log_streams[severity] = new AndroidLogStream(priority);
}
ptr->set_ostream_ptr(new AndroidLogStream(ANDROID_LOG_INFO), true);
} else {
// Running from the terminal, set all the log streams to point to the
// same output.
for (int severity = 0; severity <= NS_fatal; ++severity) {
ptr->_log_streams[severity] = &cerr;
}
}
#endif #endif
} }
} }

View File

@ -21,6 +21,9 @@
#include <time.h> // for strftime(). #include <time.h> // for strftime().
#include <assert.h> #include <assert.h>
bool NotifyCategory::_notify_timestamp = false;
bool NotifyCategory::_check_debug_notify_protect = false;
long NotifyCategory::_server_delta = 0; long NotifyCategory::_server_delta = 0;
/** /**
@ -55,7 +58,7 @@ std::ostream &NotifyCategory::
out(NotifySeverity severity, bool prefix) const { out(NotifySeverity severity, bool prefix) const {
if (is_on(severity)) { if (is_on(severity)) {
if (prefix) { if (prefix) {
if (get_notify_timestamp()) { if (_notify_timestamp) {
// Format a timestamp to include as a prefix as well. // Format a timestamp to include as a prefix as well.
time_t now = time(nullptr) + _server_delta; time_t now = time(nullptr) + _server_delta;
struct tm atm; struct tm atm;
@ -79,7 +82,7 @@ out(NotifySeverity severity, bool prefix) const {
return Notify::out(severity); return Notify::out(severity);
} }
} else if (severity <= NS_debug && get_check_debug_notify_protect()) { } else if (severity <= NS_debug && _check_debug_notify_protect) {
// Someone issued a debug Notify output statement without protecting it // Someone issued a debug Notify output statement without protecting it
// within an if statement. This can cause a significant runtime // within an if statement. This can cause a significant runtime
// performance hit, since it forces the iostream library to fully format // performance hit, since it forces the iostream library to fully format
@ -172,36 +175,3 @@ update_severity_cache() {
mark_cache_valid(_local_modified); mark_cache_valid(_local_modified);
} }
/**
* Returns the value of the notify-timestamp ConfigVariable. This is defined
* using a method accessor rather than a static ConfigVariableBool, to protect
* against the variable needing to be accessed at static init time.
*/
bool NotifyCategory::
get_notify_timestamp() {
static ConfigVariableBool *notify_timestamp = nullptr;
if (notify_timestamp == nullptr) {
notify_timestamp = new ConfigVariableBool
("notify-timestamp", false,
"Set true to output the date & time with each notify message.");
}
return *notify_timestamp;
}
/**
* Returns the value of the check-debug-notify-protect ConfigVariable. This
* is defined using a method accessor rather than a static ConfigVariableBool,
* to protect against the variable needing to be accessed at static init time.
*/
bool NotifyCategory::
get_check_debug_notify_protect() {
static ConfigVariableBool *check_debug_notify_protect = nullptr;
if (check_debug_notify_protect == nullptr) {
check_debug_notify_protect = new ConfigVariableBool
("check-debug-notify-protect", false,
"Set true to issue a warning message if a debug or spam "
"notify output is not protected within an if statement.");
}
return *check_debug_notify_protect;
}

View File

@ -76,8 +76,6 @@ PUBLISHED:
private: private:
std::string get_config_name() const; std::string get_config_name() const;
void update_severity_cache(); void update_severity_cache();
static bool get_notify_timestamp();
static bool get_check_debug_notify_protect();
std::string _fullname; std::string _fullname;
std::string _basename; std::string _basename;
@ -86,12 +84,16 @@ private:
typedef std::vector<NotifyCategory *> Children; typedef std::vector<NotifyCategory *> Children;
Children _children; Children _children;
static bool _notify_timestamp;
static bool _check_debug_notify_protect;
static long _server_delta; // not a time_t because server delta may be signed. static long _server_delta; // not a time_t because server delta may be signed.
AtomicAdjust::Integer _local_modified; AtomicAdjust::Integer _local_modified;
NotifySeverity _severity_cache; NotifySeverity _severity_cache;
friend class Notify; friend class Notify;
friend class ConfigPageManager;
}; };
INLINE std::ostream &operator << (std::ostream &out, const NotifyCategory &cat); INLINE std::ostream &operator << (std::ostream &out, const NotifyCategory &cat);

View File

@ -76,10 +76,12 @@ PUBLISHED:
public: public:
static ios_fmtflags get_literal_flag(); static ios_fmtflags get_literal_flag();
bool assert_failure(const std::string &expression, int line, static bool assert_failure(const std::string &expression, int line,
const char *source_file); const char *source_file);
bool assert_failure(const char *expression, int line, static bool assert_failure(const char *expression, int line,
const char *source_file); const char *source_file);
static void write_backtrace(void **trace, int size);
static NotifySeverity string_severity(const std::string &string); static NotifySeverity string_severity(const std::string &string);
@ -100,7 +102,7 @@ private:
Categories _categories; Categories _categories;
#if defined(ANDROID) #if defined(ANDROID)
AndroidLogStream *_log_streams[NS_fatal + 1]; std::ostream *_log_streams[NS_fatal + 1];
#elif defined(__EMSCRIPTEN__) #elif defined(__EMSCRIPTEN__)
EmscriptenLogStream *_log_streams[NS_fatal + 1]; EmscriptenLogStream *_log_streams[NS_fatal + 1];
#endif #endif
@ -204,7 +206,7 @@ private:
#define nassertr(condition, return_value) \ #define nassertr(condition, return_value) \
{ \ { \
if (_nassert_check(condition)) { \ if (_nassert_check(condition)) { \
if (Notify::ptr()->assert_failure(#condition, __LINE__, __FILE__)) { \ if (Notify::assert_failure(#condition, __LINE__, __FILE__)) { \
return return_value; \ return return_value; \
} \ } \
} \ } \
@ -213,7 +215,7 @@ private:
#define nassertv(condition) \ #define nassertv(condition) \
{ \ { \
if (_nassert_check(condition)) { \ if (_nassert_check(condition)) { \
if (Notify::ptr()->assert_failure(#condition, __LINE__, __FILE__)) { \ if (Notify::assert_failure(#condition, __LINE__, __FILE__)) { \
return; \ return; \
} \ } \
} \ } \
@ -221,12 +223,12 @@ private:
#define nassertd(condition) \ #define nassertd(condition) \
if (_nassert_check(condition) && \ if (_nassert_check(condition) && \
Notify::ptr()->assert_failure(#condition, __LINE__, __FILE__)) Notify::assert_failure(#condition, __LINE__, __FILE__))
#define nassertr_always(condition, return_value) nassertr(condition, return_value) #define nassertr_always(condition, return_value) nassertr(condition, return_value)
#define nassertv_always(condition) nassertv(condition) #define nassertv_always(condition) nassertv(condition)
#define nassert_raise(message) Notify::ptr()->assert_failure(message, __LINE__, __FILE__) #define nassert_raise(message) Notify::assert_failure(message, __LINE__, __FILE__)
#endif // NDEBUG #endif // NDEBUG

View File

@ -114,6 +114,56 @@ output_c_string(std::ostream &out, const string &string_name,
*/ */
EVP_PKEY * EVP_PKEY *
generate_key() { generate_key() {
#if OPENSSL_VERSION_MAJOR >= 3
EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, nullptr);
if (ctx == nullptr) {
EVP_PKEY_CTX_free(ctx);
output_ssl_errors();
exit(1);
}
if (EVP_PKEY_keygen_init(ctx) <= 0 ||
EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, 1024) <= 0) {
EVP_PKEY_CTX_free(ctx);
output_ssl_errors();
exit(1);
}
BIGNUM *e = BN_new();
if (e == nullptr || !BN_set_word(e, 7)) {
BN_free(e);
EVP_PKEY_CTX_free(ctx);
output_ssl_errors();
exit(1);
}
unsigned char *e_buf = nullptr;
int e_len = BN_num_bytes(e);
e_buf = (unsigned char *)OPENSSL_malloc(e_len);
BN_bn2bin(e, e_buf);
size_t bits = 1024;
OSSL_PARAM params[] = {
OSSL_PARAM_construct_size_t("bits", &bits),
OSSL_PARAM_construct_BN("e", e_buf, e_len),
OSSL_PARAM_END
};
EVP_PKEY *pkey = nullptr;
if (EVP_PKEY_CTX_set_params(ctx, params) <= 0 ||
EVP_PKEY_generate(ctx, &pkey) <= 0) {
OPENSSL_free(e_buf);
BN_free(e);
EVP_PKEY_CTX_free(ctx);
output_ssl_errors();
exit(1);
}
OPENSSL_free(e_buf);
BN_free(e);
EVP_PKEY_CTX_free(ctx);
return pkey;
#else
RSA *rsa = RSA_new(); RSA *rsa = RSA_new();
BIGNUM *e = BN_new(); BIGNUM *e = BN_new();
if (rsa == nullptr || e == nullptr) { if (rsa == nullptr || e == nullptr) {
@ -134,6 +184,7 @@ generate_key() {
EVP_PKEY *pkey = EVP_PKEY_new(); EVP_PKEY *pkey = EVP_PKEY_new();
EVP_PKEY_assign_RSA(pkey, rsa); EVP_PKEY_assign_RSA(pkey, rsa);
return pkey; return pkey;
#endif
} }
/** /**

View File

@ -383,6 +383,7 @@ SectionGroup "Python modules" SecGroupPython
!insertmacro PyBindingSection 3.12-32 .cp312-win32.pyd !insertmacro PyBindingSection 3.12-32 .cp312-win32.pyd
!insertmacro PyBindingSection 3.13-32 .cp313-win32.pyd !insertmacro PyBindingSection 3.13-32 .cp313-win32.pyd
!insertmacro PyBindingSection 3.14-32 .cp314-win32.pyd !insertmacro PyBindingSection 3.14-32 .cp314-win32.pyd
!insertmacro PyBindingSection 3.15-32 .cp315-win32.pyd
!else !else
!insertmacro PyBindingSection 3.5 .cp35-win_amd64.pyd !insertmacro PyBindingSection 3.5 .cp35-win_amd64.pyd
!insertmacro PyBindingSection 3.6 .cp36-win_amd64.pyd !insertmacro PyBindingSection 3.6 .cp36-win_amd64.pyd
@ -394,6 +395,7 @@ SectionGroup "Python modules" SecGroupPython
!insertmacro PyBindingSection 3.12 .cp312-win_amd64.pyd !insertmacro PyBindingSection 3.12 .cp312-win_amd64.pyd
!insertmacro PyBindingSection 3.13 .cp313-win_amd64.pyd !insertmacro PyBindingSection 3.13 .cp313-win_amd64.pyd
!insertmacro PyBindingSection 3.14 .cp314-win_amd64.pyd !insertmacro PyBindingSection 3.14 .cp314-win_amd64.pyd
!insertmacro PyBindingSection 3.15 .cp315-win_amd64.pyd
!endif !endif
SectionGroupEnd SectionGroupEnd
@ -506,6 +508,7 @@ Function .onInit
!insertmacro MaybeEnablePyBindingSection 3.12-32 !insertmacro MaybeEnablePyBindingSection 3.12-32
!insertmacro MaybeEnablePyBindingSection 3.13-32 !insertmacro MaybeEnablePyBindingSection 3.13-32
!insertmacro MaybeEnablePyBindingSection 3.14-32 !insertmacro MaybeEnablePyBindingSection 3.14-32
!insertmacro MaybeEnablePyBindingSection 3.15-32
${EndIf} ${EndIf}
!else !else
!insertmacro MaybeEnablePyBindingSection 3.5 !insertmacro MaybeEnablePyBindingSection 3.5
@ -519,6 +522,7 @@ Function .onInit
!insertmacro MaybeEnablePyBindingSection 3.12 !insertmacro MaybeEnablePyBindingSection 3.12
!insertmacro MaybeEnablePyBindingSection 3.13 !insertmacro MaybeEnablePyBindingSection 3.13
!insertmacro MaybeEnablePyBindingSection 3.14 !insertmacro MaybeEnablePyBindingSection 3.14
!insertmacro MaybeEnablePyBindingSection 3.15
${EndIf} ${EndIf}
!endif !endif
@ -548,6 +552,10 @@ Function .onInit
SectionSetFlags ${SecPyBindings3.14} ${SF_RO} SectionSetFlags ${SecPyBindings3.14} ${SF_RO}
SectionSetInstTypes ${SecPyBindings3.14} 0 SectionSetInstTypes ${SecPyBindings3.14} 0
!endif !endif
!ifdef SecPyBindings3.15
SectionSetFlags ${SecPyBindings3.15} ${SF_RO}
SectionSetInstTypes ${SecPyBindings3.15} 0
!endif
${EndUnless} ${EndUnless}
FunctionEnd FunctionEnd
@ -853,6 +861,7 @@ Section Uninstall
!insertmacro RemovePythonPath 3.12-32 !insertmacro RemovePythonPath 3.12-32
!insertmacro RemovePythonPath 3.13-32 !insertmacro RemovePythonPath 3.13-32
!insertmacro RemovePythonPath 3.14-32 !insertmacro RemovePythonPath 3.14-32
!insertmacro RemovePythonPath 3.15-32
!else !else
!insertmacro RemovePythonPath 3.5 !insertmacro RemovePythonPath 3.5
!insertmacro RemovePythonPath 3.6 !insertmacro RemovePythonPath 3.6
@ -864,6 +873,7 @@ Section Uninstall
!insertmacro RemovePythonPath 3.12 !insertmacro RemovePythonPath 3.12
!insertmacro RemovePythonPath 3.13 !insertmacro RemovePythonPath 3.13
!insertmacro RemovePythonPath 3.14 !insertmacro RemovePythonPath 3.14
!insertmacro RemovePythonPath 3.15
!endif !endif
SetDetailsPrint both SetDetailsPrint both
@ -936,6 +946,7 @@ SectionEnd
!insertmacro MUI_DESCRIPTION_TEXT ${SecPyBindings3.12-32} $(DESC_SecPyBindings3.12-32) !insertmacro MUI_DESCRIPTION_TEXT ${SecPyBindings3.12-32} $(DESC_SecPyBindings3.12-32)
!insertmacro MUI_DESCRIPTION_TEXT ${SecPyBindings3.13-32} $(DESC_SecPyBindings3.13-32) !insertmacro MUI_DESCRIPTION_TEXT ${SecPyBindings3.13-32} $(DESC_SecPyBindings3.13-32)
!insertmacro MUI_DESCRIPTION_TEXT ${SecPyBindings3.14-32} $(DESC_SecPyBindings3.14-32) !insertmacro MUI_DESCRIPTION_TEXT ${SecPyBindings3.14-32} $(DESC_SecPyBindings3.14-32)
!insertmacro MUI_DESCRIPTION_TEXT ${SecPyBindings3.15-32} $(DESC_SecPyBindings3.15-32)
!else !else
!insertmacro MUI_DESCRIPTION_TEXT ${SecPyBindings3.5} $(DESC_SecPyBindings3.5) !insertmacro MUI_DESCRIPTION_TEXT ${SecPyBindings3.5} $(DESC_SecPyBindings3.5)
!insertmacro MUI_DESCRIPTION_TEXT ${SecPyBindings3.6} $(DESC_SecPyBindings3.6) !insertmacro MUI_DESCRIPTION_TEXT ${SecPyBindings3.6} $(DESC_SecPyBindings3.6)
@ -947,6 +958,7 @@ SectionEnd
!insertmacro MUI_DESCRIPTION_TEXT ${SecPyBindings3.12} $(DESC_SecPyBindings3.12) !insertmacro MUI_DESCRIPTION_TEXT ${SecPyBindings3.12} $(DESC_SecPyBindings3.12)
!insertmacro MUI_DESCRIPTION_TEXT ${SecPyBindings3.13} $(DESC_SecPyBindings3.13) !insertmacro MUI_DESCRIPTION_TEXT ${SecPyBindings3.13} $(DESC_SecPyBindings3.13)
!insertmacro MUI_DESCRIPTION_TEXT ${SecPyBindings3.14} $(DESC_SecPyBindings3.14) !insertmacro MUI_DESCRIPTION_TEXT ${SecPyBindings3.14} $(DESC_SecPyBindings3.14)
!insertmacro MUI_DESCRIPTION_TEXT ${SecPyBindings3.15} $(DESC_SecPyBindings3.15)
!endif !endif
!ifdef INCLUDE_PYVER !ifdef INCLUDE_PYVER
!insertmacro MUI_DESCRIPTION_TEXT ${SecPython} $(DESC_SecPython) !insertmacro MUI_DESCRIPTION_TEXT ${SecPython} $(DESC_SecPython)

Some files were not shown because too many files have changed in this diff Show More