Browse Source

Merge branch 'master' into shaderpipeline

rdb 3 years ago
parent
commit
282f5c1cdb
100 changed files with 1651 additions and 847 deletions
  1. 4 4
      .github/workflows/ci.yml
  2. 5 29
      CMakeLists.txt
  3. 20 23
      README.md
  4. 12 77
      cmake/macros/BuildMetalib.cmake
  5. 2 15
      cmake/macros/PackageConfig.cmake
  6. 53 33
      cmake/modules/FindLibSquish.cmake
  7. 0 88
      cmake/modules/FindOpenEXR.cmake
  8. 5 5
      contrib/src/rplight/shadowSource.h
  9. 101 66
      direct/src/dist/FreezeTool.py
  10. 5 0
      direct/src/dist/_android.py
  11. 27 18
      direct/src/dist/commands.py
  12. 2 1
      direct/src/distributed/ConnectionRepository.py
  13. 13 0
      direct/src/distributed/PyDatagramIterator.py
  14. 4 5
      direct/src/distributed/cConnectionRepository.cxx
  15. 15 5
      direct/src/filter/CommonFilters.py
  16. 1 1
      direct/src/filter/FilterManager.py
  17. 2 1
      direct/src/filter/filterBloomI.py
  18. 3 2
      direct/src/filter/filterBloomX.py
  19. 2 1
      direct/src/filter/filterBloomY.py
  20. 4 4
      direct/src/filter/filterBlurX.py
  21. 4 4
      direct/src/filter/filterBlurY.py
  22. 3 2
      direct/src/filter/filterCopy.py
  23. 3 2
      direct/src/filter/filterDown4.py
  24. 0 6
      direct/src/gui/DirectGuiBase.py
  25. 2 1
      direct/src/motiontrail/cMotionTrail.h
  26. 4 4
      direct/src/showbase/BufferViewer.py
  27. 2 2
      direct/src/showbase/DirectObject.py
  28. 5 1
      direct/src/showbase/ShowBase.py
  29. 7 7
      direct/src/stdpy/threading.py
  30. 9 5
      direct/src/task/Task.py
  31. 12 32
      dtool/CompilerFlags.cmake
  32. 33 17
      dtool/Package.cmake
  33. 0 4
      dtool/PandaVersion.cmake
  34. 211 93
      dtool/metalibs/dtoolconfig/pydtool.cxx
  35. 2 2
      dtool/src/cppparser/cppManifest.cxx
  36. 1 1
      dtool/src/cppparser/cppManifest.h
  37. 2 2
      dtool/src/cppparser/cppNamespace.cxx
  38. 2 2
      dtool/src/cppparser/cppStructType.cxx
  39. 7 0
      dtool/src/dtoolbase/CMakeLists.txt
  40. 1 1
      dtool/src/dtoolbase/mutexWin32Impl.cxx
  41. 1 0
      dtool/src/dtoolbase/p3dtoolbase_composite2.cxx
  42. 211 6
      dtool/src/dtoolbase/patomic.I
  43. 168 0
      dtool/src/dtoolbase/patomic.cxx
  44. 85 18
      dtool/src/dtoolbase/patomic.h
  45. 0 1
      dtool/src/dtoolbase/typeHandle_ext.cxx
  46. 39 6
      dtool/src/dtoolutil/executionEnvironment.cxx
  47. 15 1
      dtool/src/dtoolutil/filename.cxx
  48. 2 0
      dtool/src/dtoolutil/pandaSystem.cxx
  49. 10 2
      dtool/src/dtoolutil/panda_getopt_impl.cxx
  50. 3 4
      dtool/src/dtoolutil/pfstreamBuf.cxx
  51. 3 0
      dtool/src/interrogate/functionRemap.cxx
  52. 4 4
      dtool/src/interrogate/interfaceMaker.cxx
  53. 2 2
      dtool/src/interrogate/interfaceMakerPythonNative.cxx
  54. 2 2
      dtool/src/interrogate/interfaceMakerPythonObj.cxx
  55. 2 2
      dtool/src/interrogate/interfaceMakerPythonSimple.cxx
  56. 7 0
      dtool/src/interrogate/interrogate.cxx
  57. 22 3
      dtool/src/interrogate/interrogateBuilder.cxx
  58. 40 0
      dtool/src/interrogate/typeManager.cxx
  59. 1 0
      dtool/src/interrogate/typeManager.h
  60. 11 0
      dtool/src/interrogatedb/interrogateDatabase.cxx
  61. 16 0
      dtool/src/interrogatedb/interrogateFunction.I
  62. 6 1
      dtool/src/interrogatedb/interrogateFunction.h
  63. 11 0
      dtool/src/interrogatedb/interrogateFunctionWrapper.I
  64. 2 0
      dtool/src/interrogatedb/interrogateFunctionWrapper.h
  65. 67 1
      dtool/src/interrogatedb/interrogate_interface.cxx
  66. 13 1
      dtool/src/interrogatedb/interrogate_interface.h
  67. 2 2
      dtool/src/interrogatedb/py_panda.cxx
  68. 6 0
      dtool/src/prc/CMakeLists.txt
  69. 1 0
      dtool/src/prc/configFlags.h
  70. 2 1
      dtool/src/prc/configVariableBase.h
  71. 2 1
      dtool/src/prc/configVariableCore.h
  72. 1 1
      dtool/src/prc/notifyCategory.I
  73. 47 10
      makepanda/makepackage.py
  74. 80 30
      makepanda/makepanda.py
  75. 34 3
      makepanda/makepandacore.py
  76. 15 0
      makepanda/makewheel.py
  77. 8 2
      panda/src/android/PythonActivity.java
  78. 2 3
      panda/src/audio/audioLoadRequest.I
  79. 5 0
      panda/src/audiotraits/CMakeLists.txt
  80. 3 2
      panda/src/bullet/bulletAllHitsRayResult.h
  81. 2 1
      panda/src/bullet/bulletClosestHitRayResult.h
  82. 2 1
      panda/src/bullet/bulletClosestHitSweepResult.h
  83. 1 1
      panda/src/bullet/bulletConvexHullShape.cxx
  84. 2 2
      panda/src/bullet/bulletConvexPointCloudShape.I
  85. 2 2
      panda/src/bullet/bulletTriangleMeshShape.cxx
  86. 2 2
      panda/src/chan/animControl.cxx
  87. 7 7
      panda/src/char/character.cxx
  88. 6 3
      panda/src/cocoadisplay/cocoaGraphicsWindow.mm
  89. 2 0
      panda/src/cocoadisplay/config_cocoadisplay.h
  90. 5 0
      panda/src/cocoadisplay/config_cocoadisplay.mm
  91. 4 36
      panda/src/collide/collisionBox.I
  92. 54 81
      panda/src/collide/collisionBox.cxx
  93. 7 10
      panda/src/collide/collisionBox.h
  94. 0 9
      panda/src/collide/collisionHandlerEvent.I
  95. 0 1
      panda/src/collide/collisionHandlerEvent.h
  96. 0 1
      panda/src/collide/collisionHandlerPhysical_ext.cxx
  97. 7 7
      panda/src/collide/collisionLevelStateBase.I
  98. 3 3
      panda/src/collide/collisionTraverser.cxx
  99. 0 1
      panda/src/collide/collisionTraverser_ext.cxx
  100. 4 4
      panda/src/device/evdevInputDevice.cxx

+ 4 - 4
.github/workflows/ci.yml

@@ -333,7 +333,7 @@ jobs:
     if: "!contains(github.event.head_commit.message, '[skip ci]') && !contains(github.event.head_commit.message, '[ci skip]')"
     strategy:
       matrix:
-        os: [ubuntu-18.04, windows-2016, macOS-11]
+        os: [ubuntu-18.04, windows-2019, macOS-11]
     runs-on: ${{ matrix.os }}
     steps:
     - uses: actions/checkout@v1
@@ -365,7 +365,7 @@ jobs:
     - name: Build Python 3.9
       shell: bash
       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
+        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
     - name: Test Python 3.9
       shell: bash
       run: |
@@ -378,7 +378,7 @@ jobs:
     - name: Build Python 3.8
       shell: bash
       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
+        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
     - name: Test Python 3.8
       shell: bash
       run: |
@@ -391,7 +391,7 @@ jobs:
     - name: Build Python 3.7
       shell: bash
       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
+        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
     - name: Test Python 3.7
       shell: bash
       run: |

+ 5 - 29
CMakeLists.txt

@@ -1,36 +1,14 @@
-cmake_minimum_required(VERSION 3.0.2)
+cmake_minimum_required(VERSION 3.13)
 set(CMAKE_DISABLE_SOURCE_CHANGES ON) # Must go before project() below
 set(CMAKE_DISABLE_IN_SOURCE_BUILD ON) # Must go before project() below
 
-if(CMAKE_VERSION VERSION_GREATER "3.11" OR POLICY CMP0072)
-  # Prefer GLVND over libGL when available; this will be enabled by default
-  # once the minimum CMake version is at least 3.11.
-  cmake_policy(SET CMP0072 NEW)
-endif()
-
-if(CMAKE_VERSION VERSION_GREATER "3.12" OR POLICY CMP0074)
-  # Needed for THIRDPARTY_DIRECTORY support; this will be enabled by default
-  # once the minimum CMake version is at least 3.12.
-  cmake_policy(SET CMP0074 NEW)
-endif()
-
 if(POLICY CMP0091)
   # Needed for CMake to pass /MD flag properly with non-VC generators.
   cmake_policy(SET CMP0091 NEW)
 endif()
 
 # Determine whether we are using a multi-config generator.
-if(CMAKE_VERSION VERSION_GREATER "3.8")
-  get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
-else()
-  message(WARNING "Multi-configuration builds may not work properly when using
-a CMake < 3.9. Making a guess if this is a multi-config generator.")
-  if(DEFINED CMAKE_CONFIGURATION_TYPES)
-    set(IS_MULTICONFIG ON)
-  else()
-    set(IS_MULTICONFIG OFF)
-  endif()
-endif()
+get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
 
 # Set the default CMAKE_BUILD_TYPE before calling project().
 if(IS_MULTICONFIG)
@@ -89,11 +67,9 @@ string(REPLACE "$(EFFECTIVE_PLATFORM_NAME)" "" PANDA_CFG_INTDIR "${CMAKE_CFG_INT
 set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules/")
 set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake/macros/")
 
-if(CMAKE_VERSION VERSION_GREATER "3.8")
-  # When using the Xcode generator, don't append the platform name to the
-  # intermediate configuration directory.
-  set_property(GLOBAL PROPERTY XCODE_EMIT_EFFECTIVE_PLATFORM_NAME OFF)
-endif()
+# When using the Xcode generator, don't append the platform name to the
+# intermediate configuration directory.
+set_property(GLOBAL PROPERTY XCODE_EMIT_EFFECTIVE_PLATFORM_NAME OFF)
 
 # Include modules builtin to CMake
 include(GNUInstallDirs)     # Defines CMAKE_INSTALL_<dir> variables

+ 20 - 23
README.md

@@ -58,7 +58,7 @@ You will also need to install the [Windows SDK](https://developer.microsoft.com/
 and if you intend to target Windows Vista, you will also need the
 [Windows 8.1 SDK](https://go.microsoft.com/fwlink/p/?LinkId=323507).
 
-You will also need to have the third-party dependency libraries available for
+You will also need the thirdparty dependency libraries available for
 the build scripts to use.  These are available from one of these two URLs,
 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
@@ -177,35 +177,32 @@ directory which you can install using `pkg install`.
 Android
 -------
 
-Note: building on Android is very experimental and not guaranteed to work.
+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
+.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
+need to get the latest thirdparty packages, which can be obtained from the
+artifacts page of the last successful run here:
 
-You can experimentally build the Android Python runner via the [termux](https://termux.com/)
-shell.  You will need to install [Termux](https://play.google.com/store/apps/details?id=com.termux)
-and [Termux API](https://play.google.com/store/apps/details?id=com.termux.api)
-from the Play Store.  Many of the dependencies can be installed by running the
-following command in the Termux shell:
+https://github.com/rdb/panda3d-thirdparty/actions?query=branch%3Amain+is%3Asuccess+event%3Apush
 
-```bash
-pkg install python ndk-sysroot clang bison freetype harfbuzz libpng eigen openal-soft opusfile libvorbis assimp libopus ecj dx patchelf aapt apksigner libcrypt openssl pkg-config
-```
+This does not include Python at the moment, which can be extracted from
+[this archive](https://rdb.name/thirdparty-android.tar.gz) instead.
 
-Then, you can build the .apk using this command:
+These commands show how to compile wheels for the supported Android ABIs:
 
 ```bash
-python makepanda/makepanda.py --everything --target android-21 --no-tiff --installer
+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.8 makepanda/makepanda.py --everything --outputdir built-droid-armv7a --arch armv7a --target android-19 --threads 6 --wheel
+python3.8 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
 ```
 
-You can install the generated panda3d.apk by browsing to the panda3d folder
-using a file manager.  You may need to copy it to `/sdcard` to be able to
-access it from other apps.
-
-To launch a Python program from Termux, you can use the `run_python.sh` script
-inside the `panda/src/android` directory.  It will launch Python in a separate
-activity, load it with the Python script you passed as argument, and use a
-socket for returning the command-line output to the Termux shell.  Do note
-that this requires the Python application to reside on the SD card and that
-Termux needs to be set up with access to the SD card (using the
-`termux-setup-storage` command).
+It is now possible to use the generated wheels with `build_apps`, as explained
+on this page:
+
+https://discourse.panda3d.org/t/deployment-for-android/28226
 
 Running Tests
 =============

+ 12 - 77
cmake/macros/BuildMetalib.cmake

@@ -5,83 +5,6 @@
 #   instead just an agglomeration of the various component libraries that get
 #   linked into them. A library of libraries - a "metalibrary."
 
-#
-# Function: target_link_libraries(...)
-#
-# Overrides CMake's target_link_libraries() to support "linking" object
-# libraries. This is a partial reimplementation of CMake commit dc38970f83,
-# which is only available in CMake 3.12+
-#
-if(CMAKE_VERSION VERSION_LESS "3.12")
-  function(target_link_libraries target)
-    get_target_property(target_type "${target}" TYPE)
-    if(NOT target_type STREQUAL "OBJECT_LIBRARY")
-      _target_link_libraries("${target}" ${ARGN})
-      return()
-    endif()
-
-    foreach(library ${ARGN})
-      # This is a quick and dirty regex to tell targets apart from other stuff.
-      # It just checks if it's alphanumeric and starts with p3/panda.
-      if(library MATCHES "^(PKG::|p3|panda)[A-Za-z0-9]*$")
-        # We need to add "library"'s include directories to "target"
-        # (and transitively to INTERFACE_INCLUDE_DIRECTORIES so further
-        # dependencies will work)
-        set(include_directories "$<TARGET_PROPERTY:${library},INTERFACE_INCLUDE_DIRECTORIES>")
-        set_property(TARGET "${target}" APPEND PROPERTY INCLUDE_DIRECTORIES "${include_directories}")
-        set_property(TARGET "${target}" APPEND PROPERTY INTERFACE_INCLUDE_DIRECTORIES "${include_directories}")
-
-        # SYSTEM include directories should still be reported as SYSTEM, so
-        # that warnings from those includes are suppressed
-        set(sys_include_directories
-          "$<TARGET_PROPERTY:${library},INTERFACE_SYSTEM_INCLUDE_DIRECTORIES>")
-        target_include_directories("${target}" SYSTEM PUBLIC "${sys_include_directories}")
-
-        # And for INTERFACE_COMPILE_DEFINITIONS as well
-        set(compile_definitions "$<TARGET_PROPERTY:${library},INTERFACE_COMPILE_DEFINITIONS>")
-        set_property(TARGET "${target}" APPEND PROPERTY COMPILE_DEFINITIONS "${compile_definitions}")
-        set_property(TARGET "${target}" APPEND PROPERTY INTERFACE_COMPILE_DEFINITIONS "${compile_definitions}")
-
-        # Build up some generator expressions for determining whether `library`
-        # is a component library or not.
-        if(library MATCHES ".*::.*")
-          # "::" messes up CMake's genex parser; fortunately, a library whose
-          # name contains that is either an interface library or alias, and
-          # definitely not a component
-          set(is_component 0)
-          set(name_of_component "")
-          set(name_of_non_component "${library}")
-
-        else()
-          set(is_component "$<TARGET_PROPERTY:${library},IS_COMPONENT>")
-
-          # CMake complains if we lookup IS_COMPONENT on an INTERFACE library :(
-          set(is_object "$<STREQUAL:$<TARGET_PROPERTY:${library},TYPE>,OBJECT_LIBRARY>")
-          set(is_component "$<BOOL:$<${is_object}:${is_component}>>")
-
-          set(name_of_component "$<${is_component}:$<TARGET_NAME:${library}>>")
-          set(name_of_non_component "$<$<NOT:${is_component}>:$<TARGET_NAME:${library}>>")
-
-        endif()
-
-        # Libraries are only linked transitively if they aren't components.
-        set_property(TARGET "${target}" APPEND PROPERTY
-          INTERFACE_LINK_LIBRARIES "${name_of_non_component}")
-
-      else()
-        # This is a file path to an out-of-tree library - this needs to be
-        # recorded so that the metalib can link them. (They aren't needed at
-        # all for the object libraries themselves, so they don't have to work
-        # transitively.)
-        set_property(TARGET "${target}" APPEND PROPERTY INTERFACE_LINK_LIBRARIES "${library}")
-
-      endif()
-
-    endforeach(library)
-
-  endfunction(target_link_libraries)
-endif()
-
 #
 # Function: add_component_library(target [SYMBOL building_symbol]
 #                                 [SOURCES] [[NOINIT]/[INIT func [header]]])
@@ -305,6 +228,7 @@ function(add_metalib target_name)
   set(interface_defines)
   set(includes)
   set(libs)
+  set(link_options)
   set(component_init_funcs "")
   foreach(component ${components})
     if(NOT TARGET "${component}")
@@ -392,6 +316,16 @@ function(add_metalib target_name)
         endif()
       endforeach(component_library)
 
+      # All the linker options applied to an individual component get applied
+      # when building the metalib (for things like --exclude-libs).
+      get_target_property(component_link_options "${component}" LINK_OPTIONS)
+      foreach(component_link_option ${component_link_options})
+        if(component_link_option)
+          list(APPEND link_options "${component_link_option}")
+
+        endif()
+      endforeach(component_link_option)
+
       # Consume this component's objects
       list(APPEND sources "$<TARGET_OBJECTS:${component}>")
 
@@ -419,6 +353,7 @@ function(add_metalib target_name)
     PRIVATE ${private_defines}
     INTERFACE ${interface_defines})
   target_link_libraries("${target_name}" ${libs})
+  target_link_options("${target_name}" PRIVATE ${link_options})
   target_include_directories("${target_name}"
     PUBLIC ${includes}
     INTERFACE "$<INSTALL_INTERFACE:$<INSTALL_PREFIX>/${CMAKE_INSTALL_INCLUDEDIR}/panda3d>")

+ 2 - 15
cmake/macros/PackageConfig.cmake

@@ -176,18 +176,6 @@ function(package_option name)
   # Create the INTERFACE library used to depend on this package.
   add_library(PKG::${name} INTERFACE IMPORTED GLOBAL)
 
-  # Explicitly record the package's include directories as system include
-  # directories.  CMake does do this automatically for INTERFACE libraries, but
-  # it does it by discovering all transitive links first, then reading
-  # INTERFACE_INCLUDE_DIRECTORIES for those which are INTERFACE libraries.  So,
-  # this would be broken for the metalib system (pre CMake 3.12) which doesn't
-  # "link" the object libraries.
-  if(CMAKE_VERSION VERSION_LESS "3.12")
-    set_target_properties(PKG::${name} PROPERTIES
-      INTERFACE_SYSTEM_INCLUDE_DIRECTORIES
-      "$<TARGET_PROPERTY:PKG::${name},INTERFACE_INCLUDE_DIRECTORIES>")
-  endif()
-
   # If the option actually is enabled, populate the INTERFACE library created above
   if(HAVE_${name})
     set(use_variables ON)
@@ -412,9 +400,8 @@ function(export_packages filename)
             endforeach(config)
           endif()
 
-        elseif(CMAKE_VERSION VERSION_GREATER "3.8")
-          # This is an INTERFACE_LIBRARY, and CMake is new enough to support
-          # IMPORTED_IMPLIB
+        else()
+          # This is an INTERFACE_LIBRARY.
           get_target_property(imported_libname "${head}" IMPORTED_LIBNAME)
           if(imported_libname)
             list(APPEND libraries ${imported_libname})

+ 53 - 33
cmake/modules/FindLibSquish.cmake

@@ -14,42 +14,62 @@
 #   LIBSQUISH_DEBUG_LIBRARY   - the filepath of the libsquish debug library
 #
 
-# Find the libsquish include files
-find_path(LIBSQUISH_INCLUDE_DIR
-  NAMES "squish.h"
-  PATHS "/usr/include"
-        "/usr/local/include"
-        "/sw/include"
-        "/opt/include"
-        "/opt/local/include"
-        "/opt/csw/include"
-  PATH_SUFFIXES "" "cppunit"
-)
+if(LibSquish_ROOT)
+  # Search exclusively under the root
+  find_path(LIBSQUISH_INCLUDE_DIR
+    NAMES "squish.h"
+    PATHS ${LibSquish_ROOT}
+    PATH_SUFFIXES "include"
+  )
 
-# Find the libsquish library built for release
-find_library(LIBSQUISH_RELEASE_LIBRARY
-  NAMES "squish" "libsquish"
-  PATHS "/usr"
-        "/usr/local"
-        "/usr/freeware"
-        "/sw"
-        "/opt"
-        "/opt/csw"
-  PATH_SUFFIXES "lib" "lib32" "lib64"
-)
+  find_library(LIBSQUISH_RELEASE_LIBRARY
+    NAMES "squish" "libsquish"
+    PATHS ${LibSquish_ROOT}
+    PATH_SUFFIXES "lib"
+  )
 
-# Find the libsquish library built for debug
-find_library(LIBSQUISH_DEBUG_LIBRARY
-  NAMES "squishd" "libsquishd"
-  PATHS "/usr"
-        "/usr/local"
-        "/usr/freeware"
-        "/sw"
-        "/opt"
-        "/opt/csw"
-  PATH_SUFFIXES "lib" "lib32" "lib64"
-)
+  find_library(LIBSQUISH_DEBUG_LIBRARY
+    NAMES "squishd" "libsquishd"
+    PATHS ${LibSquish_ROOT}
+    PATH_SUFFIXES "lib"
+  )
+else()
+  # Find the libsquish include files
+  find_path(LIBSQUISH_INCLUDE_DIR
+    NAMES "squish.h"
+    PATHS "/usr/include"
+          "/usr/local/include"
+          "/sw/include"
+          "/opt/include"
+          "/opt/local/include"
+          "/opt/csw/include"
+    PATH_SUFFIXES "" "cppunit"
+  )
 
+  # Find the libsquish library built for release
+  find_library(LIBSQUISH_RELEASE_LIBRARY
+    NAMES "squish" "libsquish"
+    PATHS "/usr"
+          "/usr/local"
+          "/usr/freeware"
+          "/sw"
+          "/opt"
+          "/opt/csw"
+    PATH_SUFFIXES "lib" "lib32" "lib64"
+  )
+
+  # Find the libsquish library built for debug
+  find_library(LIBSQUISH_DEBUG_LIBRARY
+    NAMES "squishd" "libsquishd"
+    PATHS "/usr"
+          "/usr/local"
+          "/usr/freeware"
+          "/sw"
+          "/opt"
+          "/opt/csw"
+    PATH_SUFFIXES "lib" "lib32" "lib64"
+  )
+endif()
 
 mark_as_advanced(LIBSQUISH_INCLUDE_DIR)
 mark_as_advanced(LIBSQUISH_RELEASE_LIBRARY)

+ 0 - 88
cmake/modules/FindOpenEXR.cmake

@@ -1,88 +0,0 @@
-# Filename: FindOpenEXR.cmake
-# Authors: CFSworks (5 Nov, 2018)
-#
-# Usage:
-#   find_package(OpenEXR [REQUIRED] [QUIET])
-#
-# Once done this will define:
-#   OPENEXR_FOUND       - system has OpenEXR
-#   OPENEXR_INCLUDE_DIR - the include directory containing OpenEXR header files
-#   OPENEXR_LIBRARIES   - the path to the OpenEXR libraries
-#
-
-find_path(OPENEXR_INCLUDE_DIR
-  "ImfVersion.h"
-  PATH_SUFFIXES "OpenEXR")
-
-mark_as_advanced(OPENEXR_INCLUDE_DIR)
-
-find_library(OPENEXR_imf_LIBRARY
-  NAMES "IlmImf")
-
-if(OPENEXR_imf_LIBRARY)
-  get_filename_component(_imf_dir "${OPENEXR_imf_LIBRARY}" DIRECTORY)
-  find_library(OPENEXR_imfutil_LIBRARY
-    NAMES "IlmImfUtil"
-    PATHS "${_imf_dir}"
-    NO_DEFAULT_PATH)
-
-  find_library(OPENEXR_ilmthread_LIBRARY
-    NAMES "IlmThread"
-    PATHS "${_imf_dir}"
-    NO_DEFAULT_PATH)
-
-  find_library(OPENEXR_iex_LIBRARY
-    NAMES "Iex"
-    PATHS "${_imf_dir}"
-    NO_DEFAULT_PATH)
-
-  find_library(OPENEXR_iexmath_LIBRARY
-    NAMES "IexMath"
-    PATHS "${_imf_dir}"
-    NO_DEFAULT_PATH)
-
-  find_library(OPENEXR_imath_LIBRARY
-    NAMES "Imath"
-    PATHS "${_imf_dir}"
-    NO_DEFAULT_PATH)
-
-  find_library(OPENEXR_half_LIBRARY
-    NAMES "Half"
-    PATHS "${_imf_dir}"
-    NO_DEFAULT_PATH)
-
-  unset(_imf_dir)
-endif()
-
-mark_as_advanced(
-  OPENEXR_imf_LIBRARY
-  OPENEXR_imfutil_LIBRARY
-  OPENEXR_ilmthread_LIBRARY
-  OPENEXR_iex_LIBRARY
-  OPENEXR_iexmath_LIBRARY
-  OPENEXR_imath_LIBRARY
-  OPENEXR_half_LIBRARY
-)
-
-set(OPENEXR_LIBRARIES
-  ${OPENEXR_imf_LIBRARY}
-  ${OPENEXR_imfutil_LIBRARY}
-  ${OPENEXR_ilmthread_LIBRARY}
-  ${OPENEXR_iex_LIBRARY}
-  ${OPENEXR_iexmath_LIBRARY}
-  ${OPENEXR_imath_LIBRARY}
-  ${OPENEXR_half_LIBRARY}
-)
-
-include(FindPackageHandleStandardArgs)
-find_package_handle_standard_args(OpenEXR DEFAULT_MSG
-  OPENEXR_INCLUDE_DIR OPENEXR_LIBRARIES
-
-  OPENEXR_imf_LIBRARY
-  OPENEXR_imfutil_LIBRARY
-  OPENEXR_ilmthread_LIBRARY
-  OPENEXR_iex_LIBRARY
-  OPENEXR_iexmath_LIBRARY
-  OPENEXR_imath_LIBRARY
-  OPENEXR_half_LIBRARY
-)

+ 5 - 5
contrib/src/rplight/shadowSource.h

@@ -30,6 +30,7 @@
 
 #include "pandabase.h"
 #include "luse.h"
+#include "memoryBase.h"
 #include "transformState.h"
 #include "look_at.h"
 #include "compose_matrix.h"
@@ -48,7 +49,7 @@
  *   and a view-projection matrix. The shadow manager regenerates the shadow maps
  *   using the data from the shadow sources.
  */
-class ShadowSource {
+class ShadowSource : public MemoryBase {
 public:
   ShadowSource();
 
@@ -78,14 +79,13 @@ public:
   inline const BoundingSphere& get_bounds() const;
 
 private:
-  int _slot;
-  bool _needs_update;
-  size_t _resolution;
   LMatrix4 _mvp;
   LVecBase4i _region;
   LVecBase4 _region_uv;
-
   BoundingSphere _bounds;
+  int _slot;
+  bool _needs_update;
+  size_t _resolution;
 };
 
 #include "shadowSource.I"

+ 101 - 66
direct/src/dist/FreezeTool.py

@@ -80,6 +80,7 @@ defaultHiddenImports = {
     ],
     'pandas.compat': ['lzma', 'cmath'],
     'pandas._libs.tslibs.conversion': ['pandas._libs.tslibs.base'],
+    'plyer': ['plyer.platforms'],
 }
 
 
@@ -791,10 +792,6 @@ class Freezer:
         # default object will be created when it is needed.
         self.cenv = None
 
-        # This is the search path to use for Python modules.  Leave it
-        # to the default value of None to use sys.path.
-        self.path = path
-
         # The filename extension to append to the source file before
         # compiling.
         self.sourceExtension = '.c'
@@ -843,32 +840,44 @@ class Freezer:
         # builds.  It can be explicitly included if desired.
         self.modules['doctest'] = self.ModuleDef('doctest', exclude = True)
 
-        self.mf = None
-
         # Actually, make sure we know how to find all of the
         # already-imported modules.  (Some of them might do their own
         # special path mangling.)
         for moduleName, module in list(sys.modules.items()):
             if module and getattr(module, '__path__', None) is not None:
-                path = list(getattr(module, '__path__'))
-                if path:
-                    modulefinder.AddPackagePath(moduleName, path[0])
+                modPath = list(getattr(module, '__path__'))
+                if modPath:
+                    modulefinder.AddPackagePath(moduleName, modPath[0])
 
         # Module with non-obvious dependencies
         self.hiddenImports = defaultHiddenImports.copy()
         if hiddenImports is not None:
             self.hiddenImports.update(hiddenImports)
 
+        # Special hack for plyer, which has platform-specific hidden imports
+        plyer_platform = None
+        if self.platform.startswith('android'):
+            plyer_platform = 'android'
+        elif self.platform.startswith('linux'):
+            plyer_platform = 'linux'
+        elif self.platform.startswith('mac'):
+            plyer_platform = 'macosx'
+        elif self.platform.startswith('win'):
+            plyer_platform = 'win'
+
+        if plyer_platform:
+            self.hiddenImports['plyer'].append(f'plyer.platforms.{plyer_platform}.*')
+
         # Suffix/extension for Python C extension modules
         if self.platform == PandaSystem.getPlatform():
-            self.moduleSuffixes = imp.get_suffixes()
+            suffixes = imp.get_suffixes()
 
             # Set extension for Python files to binary mode
-            for i, suffix in enumerate(self.moduleSuffixes):
+            for i, suffix in enumerate(suffixes):
                 if suffix[2] == imp.PY_SOURCE:
-                    self.moduleSuffixes[i] = (suffix[0], 'rb', imp.PY_SOURCE)
+                    suffixes[i] = (suffix[0], 'rb', imp.PY_SOURCE)
         else:
-            self.moduleSuffixes = [('.py', 'rb', 1), ('.pyc', 'rb', 2)]
+            suffixes = [('.py', 'rb', 1), ('.pyc', 'rb', 2)]
 
             abi_version = '{0}{1}'.format(*sys.version_info)
             abi_flags = ''
@@ -876,7 +885,7 @@ class Freezer:
                 abi_flags += 'm'
 
             if 'linux' in self.platform:
-                self.moduleSuffixes += [
+                suffixes += [
                     ('.cpython-{0}{1}-x86_64-linux-gnu.so'.format(abi_version, abi_flags), 'rb', 3),
                     ('.cpython-{0}{1}-i686-linux-gnu.so'.format(abi_version, abi_flags), 'rb', 3),
                     ('.abi{0}.so'.format(sys.version_info[0]), 'rb', 3),
@@ -884,24 +893,26 @@ class Freezer:
                 ]
             elif 'win' in self.platform:
                 # ABI flags are not appended on Windows.
-                self.moduleSuffixes += [
+                suffixes += [
                     ('.cp{0}-win_amd64.pyd'.format(abi_version), 'rb', 3),
                     ('.cp{0}-win32.pyd'.format(abi_version), 'rb', 3),
                     ('.pyd', 'rb', 3),
                 ]
             elif 'mac' in self.platform:
-                self.moduleSuffixes += [
+                suffixes += [
                     ('.cpython-{0}{1}-darwin.so'.format(abi_version, abi_flags), 'rb', 3),
                     ('.abi{0}.so'.format(sys.version_info[0]), 'rb', 3),
                     ('.so', 'rb', 3),
                 ]
             else: # FreeBSD et al.
-                self.moduleSuffixes += [
+                suffixes += [
                     ('.cpython-{0}{1}.so'.format(abi_version, abi_flags), 'rb', 3),
                     ('.abi{0}.so'.format(sys.version_info[0]), 'rb', 3),
                     ('.so', 'rb', 3),
                 ]
 
+        self.mf = PandaModuleFinder(excludes=['doctest'], suffixes=suffixes, path=path)
+
     def excludeFrom(self, freezer):
         """ Excludes all modules that have already been processed by
         the indicated FreezeTool.  This is equivalent to passing the
@@ -921,8 +932,6 @@ class Freezer:
         allowChildren is true, the children of the indicated module
         may still be included."""
 
-        assert self.mf is None
-
         self.modules[moduleName] = self.ModuleDef(
             moduleName, exclude = True,
             forbid = forbid, allowChildren = allowChildren,
@@ -947,24 +956,6 @@ class Freezer:
         files can be found.  If the module is a .py file and not a
         directory, returns None. """
 
-        # First, try to import the module directly.  That's the most
-        # reliable answer, if it works.
-        try:
-            module = __import__(moduleName)
-        except:
-            print("couldn't import %s" % (moduleName))
-            module = None
-
-        if module is not None:
-            for symbol in moduleName.split('.')[1:]:
-                module = getattr(module, symbol)
-            if hasattr(module, '__path__'):
-                return module.__path__
-
-        # If it didn't work--maybe the module is unimportable because
-        # it makes certain assumptions about the builtins, or
-        # whatever--then just look for file on disk.  That's usually
-        # good enough.
         path = None
         baseName = moduleName
         if '.' in baseName:
@@ -974,34 +965,20 @@ class Freezer:
                 return None
 
         try:
-            file, pathname, description = imp.find_module(baseName, path)
+            file, pathname, description = self.mf.find_module(baseName, path)
         except ImportError:
             return None
 
-        if not os.path.isdir(pathname):
+        if not self.mf._dir_exists(pathname):
             return None
+
         return [pathname]
 
     def getModuleStar(self, moduleName):
         """ Looks for the indicated directory module and returns the
         __all__ member: the list of symbols within the module. """
 
-        # First, try to import the module directly.  That's the most
-        # reliable answer, if it works.
-        try:
-            module = __import__(moduleName)
-        except:
-            print("couldn't import %s" % (moduleName))
-            module = None
-
-        if module is not None:
-            for symbol in moduleName.split('.')[1:]:
-                module = getattr(module, symbol)
-            if hasattr(module, '__all__'):
-                return module.__all__
-
-        # If it didn't work, just open the directory and scan for *.py
-        # files.
+        # Open the directory and scan for *.py files.
         path = None
         baseName = moduleName
         if '.' in baseName:
@@ -1011,16 +988,16 @@ class Freezer:
                 return None
 
         try:
-            file, pathname, description = imp.find_module(baseName, path)
+            file, pathname, description = self.mf.find_module(baseName, path)
         except ImportError:
             return None
 
-        if not os.path.isdir(pathname):
+        if not self.mf._dir_exists(pathname):
             return None
 
         # Scan the directory, looking for .py files.
         modules = []
-        for basename in sorted(os.listdir(pathname)):
+        for basename in sorted(self.mf._listdir(pathname)):
             if basename.endswith('.py') and basename != '__init__.py':
                 modules.append(basename[:-3])
 
@@ -1054,8 +1031,8 @@ class Freezer:
             modulePath = self.getModulePath(topName)
             if modulePath:
                 for dirname in modulePath:
-                    for basename in sorted(os.listdir(dirname)):
-                        if os.path.exists(os.path.join(dirname, basename, '__init__.py')):
+                    for basename in sorted(self.mf._listdir(dirname)):
+                        if self.mf._file_exists(os.path.join(dirname, basename, '__init__.py')):
                             parentName = '%s.%s' % (topName, basename)
                             newParentName = '%s.%s' % (newTopName, basename)
                             if self.getModulePath(parentName):
@@ -1100,8 +1077,6 @@ class Freezer:
         directories within a particular directory.
         """
 
-        assert self.mf is None
-
         if not newName:
             newName = moduleName
 
@@ -1122,8 +1097,6 @@ class Freezer:
         to done(), you may not add any more modules until you call
         reset(). """
 
-        assert self.mf is None
-
         # If we are building an exe, we also need to implicitly
         # bring in Python's startup modules.
         if addStartupModules:
@@ -1165,7 +1138,9 @@ class Freezer:
             else:
                 includes.append(mdef)
 
-        self.mf = PandaModuleFinder(excludes=list(excludeDict.keys()), suffixes=self.moduleSuffixes, path=self.path)
+        # Add the excludes to the ModuleFinder.
+        for exclude in excludeDict:
+            self.mf.excludes.append(exclude)
 
         # Attempt to import the explicit modules into the modulefinder.
 
@@ -1212,6 +1187,18 @@ class Freezer:
                 else:
                     self.__loadModule(self.ModuleDef(modname, implicit = True))
 
+        # Special case for sysconfig, which depends on a platform-specific
+        # sysconfigdata module on POSIX systems.
+        if 'sysconfig' in self.mf.modules:
+            if sys.version_info >= (3, 6):
+                if 'linux' in self.platform:
+                    arch = self.platform.split('_', 1)[1]
+                    self.__loadModule(self.ModuleDef('_sysconfigdata__linux_' + arch + '-linux-gnu', implicit=True))
+                elif 'mac' in self.platform:
+                    self.__loadModule(self.ModuleDef('_sysconfigdata__darwin_darwin', implicit=True))
+            elif 'linux' in self.platform or 'mac' in self.platform:
+                self.__loadModule(self.ModuleDef('_sysconfigdata', implicit=True))
+
         # Now, any new modules we found get added to the export list.
         for origName in list(self.mf.modules.keys()):
             if origName not in origToNewName:
@@ -2428,6 +2415,17 @@ class PandaModuleFinder(modulefinder.ModuleFinder):
 
         return None
 
+    def _file_exists(self, path):
+        if os.path.exists(path):
+            return os.path.isfile(path)
+
+        fh = self._open_file(path, 'rb')
+        if fh:
+            fh.close()
+            return True
+
+        return False
+
     def _dir_exists(self, path):
         """Returns True if the given directory exists, either on disk or inside
         a wheel."""
@@ -2466,6 +2464,43 @@ class PandaModuleFinder(modulefinder.ModuleFinder):
 
         return False
 
+    def _listdir(self, path):
+        """Lists files in the given directory if it exists."""
+
+        if os.path.isdir(path):
+            return os.listdir(path)
+
+        # Is there a zip file along the path?
+        dir, dirname = os.path.split(path.rstrip(os.path.sep + '/'))
+        fn = dirname
+        while dirname:
+            if os.path.isfile(dir):
+                # Okay, this is actually a file.  Is it a zip file?
+                if dir in self._zip_files:
+                    # Yes, and we've previously opened this.
+                    zip = self._zip_files[dir]
+                elif zipfile.is_zipfile(dir):
+                    zip = zipfile.ZipFile(dir)
+                    self._zip_files[dir] = zip
+                else:
+                    # It's not a directory or zip file.
+                    return []
+
+                # List files whose path start with our directory name.
+                prefix = fn.replace(os.path.sep, '/') + '/'
+                result = []
+                for name in zip.namelist():
+                    if name.startswith(prefix) and '/' not in name[len(prefix):]:
+                        result.append(name[len(prefix):])
+
+                return result
+
+            # Look at the parent directory.
+            dir, dirname = os.path.split(dir)
+            fn = os.path.join(dirname, fn)
+
+        return []
+
     def load_module(self, fqname, fp, pathname, file_info):
         """Copied from ModuleFinder.load_module with fixes to handle sending bytes
         to compile() for PY_SOURCE types. Sending bytes to compile allows it to
@@ -2712,7 +2747,7 @@ class PandaModuleFinder(modulefinder.ModuleFinder):
         modules = {}
         for dir in m.__path__:
             try:
-                names = os.listdir(dir)
+                names = self._listdir(dir)
             except OSError:
                 self.msg(2, "can't list directory", dir)
                 continue

+ 5 - 0
direct/src/dist/_android.py

@@ -169,9 +169,11 @@ ANDROID_ATTRIBUTES = {
     'alwaysRetainTaskState': bool_resource(0x1010203),
     'clearTaskOnLaunch': bool_resource(0x1010015),
     'debuggable': bool_resource(0x0101000f),
+    'documentLaunchMode': enum_resource(0x1010445, "none", "intoExisting", "always", "never"),
     'configChanges': flag_resource(0x0101001f, mcc=0x0001, mnc=0x0002, locale=0x0004, touchscreen=0x0008, keyboard=0x0010, keyboardHidden=0x0020, navigation=0x0040, orientation=0x0080, screenLayout=0x0100, uiMode=0x0200, screenSize=0x0400, smallestScreenSize=0x0800, layoutDirection=0x2000, fontScale=0x40000000),
     'enabled': bool_resource(0x101000e),
     'excludeFromRecents': bool_resource(0x1010017),
+    'exported': bool_resource(0x1010010),
     'extractNativeLibs': bool_resource(0x10104ea),
     'finishOnTaskLaunch': bool_resource(0x1010014),
     'fullBackupContent': bool_resource(0x10104eb),
@@ -189,9 +191,12 @@ ANDROID_ATTRIBUTES = {
     'minSdkVersion': int_resource(0x101020c),
     'multiprocess': bool_resource(0x1010013),
     'name': str_resource(0x1010003),
+    'noHistory': bool_resource(0x101022d),
     'pathPattern': str_resource(0x101002c),
+    'resizeableActivity': bool_resource(0x10104f6),
     'required': bool_resource(0x101028e),
     'scheme': str_resource(0x1010027),
+    'screenOrientation': enum_resource(0x101001e, 'landscape', 'portrait', 'user', 'behind', 'sensor', 'nosensor', 'sensorLandscape', 'sensorPortrait', 'reverseLandscape', 'reversePortrait', 'fullSensor', 'userLandscape', 'userPortrait', 'fullUser', 'locked'),
     'stateNotNeeded': bool_resource(0x1010016),
     'supportsRtl': bool_resource(0x010103af),
     'supportsUploading': bool_resource(0x101029b),

+ 27 - 18
direct/src/dist/commands.py

@@ -116,7 +116,7 @@ from _frozen_importlib import _imp, FrozenImporter
 
 sys.frozen = True
 
-if sys.platform == 'win32':
+if sys.platform == 'win32' and sys.version_info < (3, 10):
     # Make sure the preferred encoding is something we actually support.
     import _bootlocale
     enc = _bootlocale.getpreferredencoding().lower()
@@ -155,7 +155,9 @@ from android_log import write as android_log_write
 
 
 sys.frozen = True
-sys.platform = "android"
+
+# Temporary hack for plyer to detect Android, see kivy/plyer#670
+os.environ['ANDROID_ARGUMENT'] = ''
 
 
 # Replace stdout/stderr with something that writes to the Android log.
@@ -465,6 +467,12 @@ class build_apps(setuptools.Command):
         elif not self.android_abis:
             self.android_abis = ['arm64-v8a', 'armeabi-v7a', 'x86_64', 'x86']
 
+        supported_abis = 'armeabi', 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64', 'mips', 'mips64'
+        unsupported_abis = set(self.android_abis) - set(supported_abis)
+        if unsupported_abis:
+            raise ValueError(f'Unrecognized value(s) for android_abis: {", ".join(unsupported_abis)}\n'
+                             f'Valid ABIs are: {", ".join(supported_abis)}')
+
         self.icon_objects = {}
         for app, iconpaths in self.icons.items():
             if not isinstance(iconpaths, list) and not isinstance(iconpaths, tuple):
@@ -513,7 +521,9 @@ class build_apps(setuptools.Command):
                     else: # e.g. x86, x86_64, mips, mips64
                         suffix = '_' + abi.replace('-', '_')
 
-                    self.build_binaries(lib_dir, platform + suffix)
+                    # We end up copying the data multiple times to the same
+                    # directory, but that's probably fine for now.
+                    self.build_binaries(platform + suffix, lib_dir, data_dir)
 
                 # Write out the icons to the res directory.
                 for appname, icon in self.icon_objects.items():
@@ -532,13 +542,13 @@ class build_apps(setuptools.Command):
                     if icon.getLargestSize() >= 192:
                         icon.writeSize(192, os.path.join(res_dir, 'mipmap-xxxhdpi-v4', basename))
 
-                self.build_data(data_dir, platform)
+                self.build_assets(platform, data_dir)
 
                 # Generate an AndroidManifest.xml
                 self.generate_android_manifest(os.path.join(build_dir, 'AndroidManifest.xml'))
             else:
-                self.build_binaries(build_dir, platform)
-                self.build_data(build_dir, platform)
+                self.build_binaries(platform, build_dir, build_dir)
+                self.build_assets(platform, build_dir)
 
             # Bundle into an .app on macOS
             if self.macos_main_app and 'macosx' in platform:
@@ -727,7 +737,7 @@ class build_apps(setuptools.Command):
 
         for appname in self.gui_apps:
             activity = ET.SubElement(application, 'activity')
-            activity.set('android:name', 'org.panda3d.android.PandaActivity')
+            activity.set('android:name', 'org.panda3d.android.PythonActivity')
             activity.set('android:label', appname)
             activity.set('android:theme', '@android:style/Theme.NoTitleBar')
             activity.set('android:configChanges', 'orientation|keyboardHidden')
@@ -750,7 +760,7 @@ class build_apps(setuptools.Command):
         with open(path, 'wb') as fh:
             tree.write(fh, encoding='utf-8', xml_declaration=True)
 
-    def build_binaries(self, binary_dir, platform):
+    def build_binaries(self, platform, binary_dir, data_dir=None):
         """ Builds the binary data for the given platform. """
 
         use_wheels = True
@@ -1015,7 +1025,7 @@ class build_apps(setuptools.Command):
 
             freezer_extras.update(freezer.extras)
             freezer_modules.update(freezer.getAllModuleNames())
-            for suffix in freezer.moduleSuffixes:
+            for suffix in freezer.mf.suffixes:
                 if suffix[2] == imp.C_EXTENSION:
                     ext_suffixes.add(suffix[0])
 
@@ -1030,12 +1040,9 @@ class build_apps(setuptools.Command):
             self.warn("Detected use of tkinter, but tkinter is not specified in requirements.txt!")
 
         # Copy extension modules
-        whl_modules = []
-        whl_modules_ext = ''
+        whl_modules = {}
         if use_wheels:
             # Get the module libs
-            whl_modules = []
-
             for i in p3dwhl.namelist():
                 if not i.startswith('deploy_libs/'):
                     continue
@@ -1050,8 +1057,7 @@ class build_apps(setuptools.Command):
 
                 base = os.path.basename(i)
                 module, _, ext = base.partition('.')
-                whl_modules.append(module)
-                whl_modules_ext = ext
+                whl_modules[module] = i
 
         # Make sure to copy any builtins that have shared objects in the
         # deploy libs, assuming they are not already in freezer_extras.
@@ -1103,7 +1109,7 @@ class build_apps(setuptools.Command):
             else:
                 # Builtin module, but might not be builtin in wheel libs, so double check
                 if module in whl_modules:
-                    source_path = os.path.join(p3dwhlfn, 'deploy_libs/{}.{}'.format(module, whl_modules_ext))#'{0}/deploy_libs/{1}.{2}'.format(p3dwhlfn, module, whl_modules_ext)
+                    source_path = os.path.join(p3dwhlfn, whl_modules[module])
                     basename = os.path.basename(source_path)
                     #XXX should we remove python version string here too?
                 else:
@@ -1125,6 +1131,9 @@ class build_apps(setuptools.Command):
                       os.path.join(binary_dir, '..', '..', 'classes.dex'))
 
         # Extract any other data files from dependency packages.
+        if data_dir is None:
+            return
+
         for module, datadesc in self.package_data_dirs.items():
             if module not in freezer_modules:
                 continue
@@ -1165,11 +1174,11 @@ class build_apps(setuptools.Command):
                             else:
                                 self.copy(source_path, target_path)
 
-    def build_data(self, data_dir, platform):
+    def build_assets(self, platform, data_dir):
         """ Builds the data files for the given platform. """
 
         # Copy Game Files
-        self.announce('Copying game files for platform: {}'.format(platform), distutils.log.INFO)
+        self.announce('Copying assets for platform: {}'.format(platform), distutils.log.INFO)
         ignore_copy_list = [
             '**/__pycache__/**',
             '**/*.pyc',

+ 2 - 1
direct/src/distributed/ConnectionRepository.py

@@ -9,7 +9,6 @@ from direct.showbase import GarbageReport
 from direct.showbase.MessengerGlobal import messenger
 from .PyDatagramIterator import PyDatagramIterator
 
-import inspect
 import gc
 
 __all__ = ["ConnectionRepository", "GCTrigger"]
@@ -311,6 +310,8 @@ class ConnectionRepository(
 
         # Now get the class definition for the classes named in the DC
         # file.
+        import inspect
+
         for i in range(dcFile.getNumClasses()):
             dclass = dcFile.getClass(i)
             number = dclass.getNumber()

+ 13 - 0
direct/src/distributed/PyDatagramIterator.py

@@ -30,6 +30,19 @@ class PyDatagramIterator(DatagramIterator):
 
     getChannel = DatagramIterator.getUint64
 
+    def __init__(self, datagram=None, offset=0):
+        if datagram is not None:
+            super().__init__(datagram, offset)
+
+            # Retain a reference to it so that it doesn't get deleted.
+            self.__initialDatagram = datagram
+        else:
+            super().__init__()
+
+    def assign(self, datagram, offset = 0):
+        super().assign(datagram, offset)
+        self.__initialDatagram = datagram
+
     def getArg(self, subatomicType, divisor=1):
         # Import the type numbers
         if divisor == 1:

+ 4 - 5
direct/src/distributed/cConnectionRepository.cxx

@@ -58,19 +58,18 @@ CConnectionRepository(bool has_owner_view, bool threaded_net) :
   _bdc(4096000,4096000,1400),
   _native(false),
 #endif
+  _has_owner_view(has_owner_view),
+  _handle_c_updates(true),
   _client_datagram(true),
   _handle_datagrams_internally(handle_datagrams_internally),
   _simulated_disconnect(false),
   _verbose(distributed_cat.is_spam()),
+  _in_quiet_zone(0),
   _time_warning(0.0),
-// _msg_channels(),
   _msg_sender(0),
   _msg_type(0),
-  _has_owner_view(has_owner_view),
-  _handle_c_updates(true),
   _want_message_bundling(true),
-  _bundling_msgs(0),
-  _in_quiet_zone(0)
+  _bundling_msgs(0)
 {
 #if defined(HAVE_NET) && defined(SIMULATE_NETWORK_DELAY)
   if (min_lag != 0.0 || max_lag != 0.0) {

+ 15 - 5
direct/src/filter/CommonFilters.py

@@ -20,6 +20,7 @@ from panda3d.core import LVecBase4, LPoint2
 from panda3d.core import AuxBitplaneAttrib
 from panda3d.core import Texture, Shader, ATSNone
 from panda3d.core import FrameBufferProperties
+from panda3d.core import getDefaultCoordinateSystem, CS_zup_right, CS_zup_left
 
 from direct.task.TaskManagerGlobal import taskMgr
 
@@ -52,6 +53,7 @@ o_color = lerp(o_color, k_cartooncolor, cartoon_thresh);
 SSAO_BODY = """//Cg
 
 void vshader(float4 vtx_position : POSITION,
+             float2 vtx_texcoord : TEXCOORD0,
              out float4 l_position : POSITION,
              out float2 l_texcoord : TEXCOORD0,
              out float2 l_texcoordD : TEXCOORD1,
@@ -61,9 +63,9 @@ void vshader(float4 vtx_position : POSITION,
              uniform float4x4 mat_modelproj)
 {
   l_position = mul(mat_modelproj, vtx_position);
-  l_texcoord = vtx_position.xz;
-  l_texcoordD = (vtx_position.xz * texpad_depth.xy) + texpad_depth.xy;
-  l_texcoordN = (vtx_position.xz * texpad_normal.xy) + texpad_normal.xy;
+  l_texcoord = vtx_texcoord;
+  l_texcoordD = vtx_texcoord * texpad_depth.xy * 2;
+  l_texcoordN = vtx_texcoord * texpad_normal.xy * 2;
 }
 
 float3 sphere[16] = float3[](float3(0.53812504, 0.18565957, -0.43192),float3(0.13790712, 0.24864247, 0.44301823),float3(0.33715037, 0.56794053, -0.005789503),float3(-0.6999805, -0.04511441, -0.0019965635),float3(0.06896307, -0.15983082, -0.85477847),float3(0.056099437, 0.006954967, -0.1843352),float3(-0.014653638, 0.14027752, 0.0762037),float3(0.010019933, -0.1924225, -0.034443386),float3(-0.35775623, -0.5301969, -0.43581226),float3(-0.3169221, 0.106360726, 0.015860917),float3(0.010350345, -0.58698344, 0.0046293875),float3(-0.08972908, -0.49408212, 0.3287904),float3(0.7119986, -0.0154690035, -0.09183723),float3(-0.053382345, 0.059675813, -0.5411899),float3(0.035267662, -0.063188605, 0.54602677),float3(-0.47761092, 0.2847911, -0.0271716));
@@ -292,11 +294,19 @@ class CommonFilters:
             text += "{\n"
             text += "  l_position = mul(mat_modelproj, vtx_position);\n"
 
+            # The card is oriented differently depending on our chosen
+            # coordinate system.  We could just use vtx_texcoord, but this
+            # saves on an additional variable.
+            if getDefaultCoordinateSystem() in (CS_zup_right, CS_zup_left):
+                pos = "vtx_position.xz"
+            else:
+                pos = "vtx_position.xy"
+
             for texcoord, padTex in texcoordPadding.items():
                 if padTex is None:
-                    text += "  %s = vtx_position.xz * float2(0.5, 0.5) + float2(0.5, 0.5);\n" % (texcoord)
+                    text += "  %s = %s * float2(0.5, 0.5) + float2(0.5, 0.5);\n" % (texcoord, pos)
                 else:
-                    text += "  %s = (vtx_position.xz * texpad_tx%s.xy) + texpad_tx%s.xy;\n" % (texcoord, padTex, padTex)
+                    text += "  %s = (%s * texpad_tx%s.xy) + texpad_tx%s.xy;\n" % (texcoord, pos, padTex, padTex)
 
                     if "HalfPixelShift" in configuration:
                         text += "  %s += texpix_tx%s.xy * 0.5;\n" % (texcoord, padTex)

+ 1 - 1
direct/src/filter/FilterManager.py

@@ -323,7 +323,7 @@ class FilterManager(DirectObject):
             props.setAuxRgba(1)
         if auxtex1 is not None:
             props.setAuxRgba(2)
-        buffer=base.graphicsEngine.makeOutput(
+        buffer=self.engine.makeOutput(
             self.win.getPipe(), name, -1,
             props, winprops, GraphicsPipe.BFRefuseWindow | GraphicsPipe.BFResizeable,
             self.win.getGsg(), self.win)

+ 2 - 1
direct/src/filter/filterBloomI.py

@@ -32,6 +32,7 @@ BLOOM_I = """
 
 
 void vshader(float4 vtx_position : POSITION,
+             float2 vtx_texcoord : TEXCOORD0,
              out float4 l_position : POSITION,
              out float2 l_texcoordNW : TEXCOORD0,
              out float2 l_texcoordNE : TEXCOORD1,
@@ -42,7 +43,7 @@ void vshader(float4 vtx_position : POSITION,
              uniform float4x4 mat_modelproj)
 {
   l_position=mul(mat_modelproj, vtx_position);
-  float2 c=(vtx_position.xz * texpad_src.xy) + texpad_src.xy;
+  float2 c = vtx_texcoord * texpad_src.xy * 2;
   float4 offs = texpix_src * 0.5;
   l_texcoordNW = c + float2( offs.x, -offs.y);
   l_texcoordNE = c + float2( offs.x,  offs.y);

+ 3 - 2
direct/src/filter/filterBloomX.py

@@ -2,6 +2,7 @@ BLOOM_X = """
 //Cg
 
 void vshader(float4 vtx_position : POSITION,
+             float2 vtx_texcoord : TEXCOORD0,
              out float4 l_position : POSITION,
              out float4 l_texcoord0 : TEXCOORD0,
              out float4 l_texcoord1 : TEXCOORD1,
@@ -10,8 +11,8 @@ void vshader(float4 vtx_position : POSITION,
              uniform float4 texpix_src,
              uniform float4x4 mat_modelproj)
 {
-  l_position=mul(mat_modelproj, vtx_position);
-  float2 c=(vtx_position.xz * texpad_src.xy) + texpad_src.xy;
+  l_position = mul(mat_modelproj, vtx_position);
+  float2 c = vtx_texcoord * texpad_src.xy * 2;
   float offset = texpix_src.x;
   float pad = texpad_src.x * 2;
   l_texcoord0 = float4(min(c.x-offset* -4, pad), min(c.x-offset* -3, pad), min(c.x-offset* -2, pad), c.y);

+ 2 - 1
direct/src/filter/filterBloomY.py

@@ -2,6 +2,7 @@ BLOOM_Y = """
 //Cg
 
 void vshader(float4 vtx_position : POSITION,
+             float2 vtx_texcoord : TEXCOORD0,
              out float4 l_position : POSITION,
              out float4 l_texcoord0 : TEXCOORD0,
              out float4 l_texcoord1 : TEXCOORD1,
@@ -11,7 +12,7 @@ void vshader(float4 vtx_position : POSITION,
              uniform float4x4 mat_modelproj)
 {
   l_position=mul(mat_modelproj, vtx_position);
-  float2 c=(vtx_position.xz * texpad_src.xy) + texpad_src.xy;
+  float2 c = vtx_texcoord * texpad_src.xy * 2;
   float offset = texpix_src.y;
   float pad = texpad_src.y * 2;
   l_texcoord0 = float4(min(c.y-offset* -4, pad), min(c.y-offset* -3, pad), min(c.y-offset* -2, pad), c.x);

+ 4 - 4
direct/src/filter/filterBlurX.py

@@ -4,14 +4,14 @@ BLUR_X = """
 //Cg profile arbvp1 arbfp1
 
 void vshader(float4 vtx_position : POSITION,
-             float2 vtx_texcoord0 : TEXCOORD0,
+             float2 vtx_texcoord : TEXCOORD0,
              out float4 l_position : POSITION,
-      	     out float2 l_texcoord0 : TEXCOORD0,
+             out float2 l_texcoord0 : TEXCOORD0,
              uniform float4 texpad_src,
              uniform float4x4 mat_modelproj)
 {
-  l_position=mul(mat_modelproj, vtx_position);
-  l_texcoord0 = (vtx_position.xz * texpad_src.xy) + texpad_src.xy;
+  l_position = mul(mat_modelproj, vtx_position);
+  l_texcoord0 = vtx_texcoord * texpad_src.xy * 2;
 }
 
 

+ 4 - 4
direct/src/filter/filterBlurY.py

@@ -4,14 +4,14 @@ BLUR_Y = """
 //Cg profile arbvp1 arbfp1
 
 void vshader(float4 vtx_position : POSITION,
-             float2 vtx_texcoord0 : TEXCOORD0,
+             float2 vtx_texcoord : TEXCOORD0,
              out float4 l_position : POSITION,
-      	     out float2 l_texcoord0 : TEXCOORD0,
+             out float2 l_texcoord0 : TEXCOORD0,
              uniform float4 texpad_src,
              uniform float4x4 mat_modelproj)
 {
-  l_position=mul(mat_modelproj, vtx_position);
-  l_texcoord0 = (vtx_position.xz * texpad_src.xy) + texpad_src.xy;
+  l_position = mul(mat_modelproj, vtx_position);
+  l_texcoord0 = vtx_texcoord * texpad_src.xy * 2;
 }
 
 

+ 3 - 2
direct/src/filter/filterCopy.py

@@ -3,13 +3,14 @@ COPY = """
 
 
 void vshader(float4 vtx_position : POSITION,
+             float2 vtx_texcoord : TEXCOORD0,
              out float4 l_position : POSITION,
              out float2 l_texcoord : TEXCOORD0,
              uniform float4 texpad_src,
              uniform float4x4 mat_modelproj)
 {
-  l_position=mul(mat_modelproj, vtx_position);
-  l_texcoord = (vtx_position.xz * texpad_src.xy) + texpad_src.xy;
+  l_position = mul(mat_modelproj, vtx_position);
+  l_texcoord = vtx_texcoord * texpad_src.xy * 2;
 }
 
 void fshader(float2 l_texcoord : TEXCOORD0,

+ 3 - 2
direct/src/filter/filterDown4.py

@@ -2,6 +2,7 @@ DOWN_4 = """
 //Cg
 
 void vshader(float4 vtx_position : POSITION,
+             float2 vtx_texcoord : TEXCOORD0,
              out float4 l_position : POSITION,
              out float2 l_texcoordNW : TEXCOORD0,
              out float2 l_texcoordNE : TEXCOORD1,
@@ -11,8 +12,8 @@ void vshader(float4 vtx_position : POSITION,
              uniform float4 texpix_src,
              uniform float4x4 mat_modelproj)
 {
-  l_position=mul(mat_modelproj, vtx_position);
-  float2 c=(vtx_position.xz * texpad_src.xy) + texpad_src.xy;
+  l_position = mul(mat_modelproj, vtx_position);
+  float2 c = vtx_texcoord * texpad_src.xy * 2;
   l_texcoordNW = c + float2( texpix_src.x, -texpix_src.y);
   l_texcoordNE = c + float2( texpix_src.x,  texpix_src.y);
   l_texcoordSW = c + float2(-texpix_src.x, -texpix_src.y);

+ 0 - 6
direct/src/gui/DirectGuiBase.py

@@ -100,8 +100,6 @@ from direct.showbase import DirectObject
 from direct.task import Task
 from direct.task.TaskManagerGlobal import taskMgr
 
-guiObjectCollector = PStatCollector("Client::GuiObjects")
-
 _track_gui_items = ConfigVariableBool('track-gui-items', False)
 
 
@@ -732,8 +730,6 @@ class DirectGuiWidget(DirectGuiBase, NodePath):
         self.guiId = self.guiItem.getId()
 
         if ShowBaseGlobal.__dev__:
-            guiObjectCollector.addLevel(1)
-            guiObjectCollector.flushLevel()
             # track gui items by guiId for tracking down leaks
             if _track_gui_items:
                 if not hasattr(ShowBase, 'guiItems'):
@@ -1033,8 +1029,6 @@ class DirectGuiWidget(DirectGuiBase, NodePath):
     def destroy(self):
         if hasattr(self, "frameStyle"):
             if ShowBaseGlobal.__dev__:
-                guiObjectCollector.subLevel(1)
-                guiObjectCollector.flushLevel()
                 if hasattr(ShowBase, 'guiItems'):
                     ShowBase.guiItems.pop(self.guiId, None)
 

+ 2 - 1
direct/src/motiontrail/cMotionTrail.h

@@ -21,11 +21,12 @@
 #include "geomVertexWriter.h"
 #include "geomTriangles.h"
 #include "luse.h"
+#include "memoryBase.h"
 #include "nurbsCurveEvaluator.h"
 #include "plist.h"
 #include "epvector.h"
 
-class CMotionTrailVertex {
+class CMotionTrailVertex : public MemoryBase {
 public:
   LPoint4 _vertex;
   LVecBase4 _start_color;

+ 4 - 4
direct/src/showbase/BufferViewer.py

@@ -240,10 +240,10 @@ class BufferViewer(DirectObject):
             offsetx = (ringoffset[ring]*2.0) / float(sizex)
             offsety = (ringoffset[ring]*2.0) / float(sizey)
             bright = ringbright[ring]
-            vwriter.addData3f(-1-offsetx, 0, -1-offsety)
-            vwriter.addData3f(1+offsetx, 0, -1-offsety)
-            vwriter.addData3f(1+offsetx, 0,  1+offsety)
-            vwriter.addData3f(-1-offsetx, 0,  1+offsety)
+            vwriter.addData3f(Vec3.rfu(-1 - offsetx, 0, -1 - offsety))
+            vwriter.addData3f(Vec3.rfu( 1 + offsetx, 0, -1 - offsety))
+            vwriter.addData3f(Vec3.rfu( 1 + offsetx, 0,  1 + offsety))
+            vwriter.addData3f(Vec3.rfu(-1 - offsetx, 0,  1 + offsety))
             cwriter.addData3f(bright, bright, bright)
             cwriter.addData3f(bright, bright, bright)
             cwriter.addData3f(bright, bright, bright)

+ 2 - 2
direct/src/showbase/DirectObject.py

@@ -44,14 +44,14 @@ class DirectObject:
     def addTask(self, *args, **kwargs):
         if not hasattr(self, "_taskList"):
             self._taskList = {}
-        kwargs['owner']=self
+        kwargs['owner'] = self
         task = taskMgr.add(*args, **kwargs)
         return task
 
     def doMethodLater(self, *args, **kwargs):
         if not hasattr(self, "_taskList"):
             self._taskList = {}
-        kwargs['owner']=self
+        kwargs['owner'] = self
         task = taskMgr.doMethodLater(*args, **kwargs)
         return task
 

+ 5 - 1
direct/src/showbase/ShowBase.py

@@ -1930,7 +1930,11 @@ class ShowBase(DirectObject.DirectObject):
         if port is None:
             port = -1
         PStatClient.connect(hostname, port)
-        return PStatClient.isConnected()
+        if PStatClient.isConnected():
+            PStatClient.mainTick()
+            return True
+        else:
+            return False
 
     def addSfxManager(self, extraSfxManager):
         """

+ 7 - 7
direct/src/stdpy/threading.py

@@ -216,8 +216,8 @@ class RLock(core.ReMutex):
         core.ReMutex.__init__(self, name)
 
 
-class Condition(core.ConditionVarFull):
-    """ This class provides a wrapper around Panda's ConditionVarFull
+class Condition(core.ConditionVar):
+    """ This class provides a wrapper around Panda's ConditionVar
     object.  The wrapper is designed to emulate Python's own
     threading.Condition object. """
 
@@ -230,7 +230,7 @@ class Condition(core.ConditionVarFull):
         assert isinstance(lock, Lock)
 
         self.__lock = lock
-        core.ConditionVarFull.__init__(self, self.__lock)
+        core.ConditionVar.__init__(self, self.__lock)
 
     def acquire(self, *args, **kw):
         return self.__lock.acquire(*args, **kw)
@@ -240,12 +240,12 @@ class Condition(core.ConditionVarFull):
 
     def wait(self, timeout = None):
         if timeout is None:
-            core.ConditionVarFull.wait(self)
+            core.ConditionVar.wait(self)
         else:
-            core.ConditionVarFull.wait(self, timeout)
+            core.ConditionVar.wait(self, timeout)
 
     def notifyAll(self):
-        core.ConditionVarFull.notifyAll(self)
+        core.ConditionVar.notifyAll(self)
 
     notify_all = notifyAll
 
@@ -295,7 +295,7 @@ class Event:
 
     def __init__(self):
         self.__lock = core.Mutex("Python Event")
-        self.__cvar = core.ConditionVarFull(self.__lock)
+        self.__cvar = core.ConditionVar(self.__lock)
         self.__flag = False
 
     def is_set(self):

+ 9 - 5
direct/src/task/Task.py

@@ -16,11 +16,17 @@ from direct.showbase.MessengerGlobal import messenger
 import types
 import random
 import importlib
+import sys
 
-try:
-    import _signal as signal
-except ImportError:
+# On Android, there's no use handling SIGINT, and in fact we can't, since we
+# run the application in a separate thread from the main thread.
+if hasattr(sys, 'getandroidapilevel'):
     signal = None
+else:
+    try:
+        import _signal as signal
+    except ImportError:
+        signal = None
 
 from panda3d.core import *
 from direct.extensions_native import HTTPChannel_extensions
@@ -31,7 +37,6 @@ def print_exc_plus():
     Print the usual traceback information, followed by a listing of all the
     local variables in each frame.
     """
-    import sys
     import traceback
 
     tb = sys.exc_info()[2]
@@ -1271,7 +1276,6 @@ class TaskManager:
 
 if __debug__:
     def checkLeak():
-        import sys
         import gc
         gc.enable()
         from direct.showbase.DirectObject import DirectObject

+ 12 - 32
dtool/CompilerFlags.cmake

@@ -48,21 +48,9 @@ elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GCC")
 
 endif()
 
-# Panda3D is now a C++11 project. Newer versions of CMake support this out of
-# the box; for older versions we take a shot in the dark:
-if(CMAKE_VERSION VERSION_LESS "3.1")
-  check_cxx_compiler_flag("-std=gnu++11" COMPILER_SUPPORTS_CXX11)
-  if(COMPILER_SUPPORTS_CXX11)
-    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++11")
-  else()
-    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++0x")
-  endif()
-
-else()
-  set(CMAKE_CXX_STANDARD 11)
-  set(CMAKE_CXX_STANDARD_REQUIRED ON)
-
-endif()
+# Panda3D is now a C++11 project.
+set(CMAKE_CXX_STANDARD 11)
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
 
 # Set certain CMake flags we expect
 set(CMAKE_INCLUDE_CURRENT_DIR_IN_INTERFACE ON)
@@ -110,21 +98,6 @@ if(APPLE)
   set(CMAKE_SHARED_MODULE_SUFFIX ".dylib")
 endif()
 
-# We want the output structured like build/CONFIG/bin, not build/bin/CONFIG per
-# the default for multi-configuration generators. In CMake 3.4+, it switches
-# automatically if the *_OUTPUT_DIRECTORY property contains a generator
-# expresssion, but as of this writing we support as early as CMake 3.0.2.
-#
-# So, let's just do this:
-if(CMAKE_VERSION VERSION_LESS "3.4")
-  foreach(_type RUNTIME ARCHIVE LIBRARY)
-    foreach(_config ${CMAKE_CONFIGURATION_TYPES})
-      string(TOUPPER "${_config}" _config)
-      set(CMAKE_${_type}_OUTPUT_DIRECTORY_${_config} "${CMAKE_${_type}_OUTPUT_DIRECTORY}")
-    endforeach(_config)
-  endforeach(_type)
-endif()
-
 # Set warning levels
 if(MSVC)
   set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /W3")
@@ -138,7 +111,7 @@ endif()
 
 if(CMAKE_CXX_COMPILER_ID MATCHES "(GNU|Clang)")
   set(global_flags
-    "-Wno-unused-function -Wno-unused-parameter -fno-strict-aliasing")
+    "-Wno-unused-function -Wno-unused-parameter -fno-strict-aliasing -Werror=return-type")
   set(release_flags "-Wno-unused-variable")
 
   if(NOT MSVC)
@@ -154,7 +127,7 @@ if(CMAKE_CXX_COMPILER_ID MATCHES "(GNU|Clang)")
   endif()
 
   set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${global_flags}")
-  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${global_flags} -Wno-reorder")
+  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${global_flags}")
   set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} ${release_flags}")
   set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} ${release_flags}")
   set(CMAKE_CXX_FLAGS_MINSIZEREL "${CMAKE_CXX_FLAGS_MINSIZEREL} ${release_flags}")
@@ -245,3 +218,10 @@ if(NOT MSVC)
     add_compile_options("-fvisibility=hidden")
   endif()
 endif()
+
+if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
+  check_cxx_compiler_flag("-fno-semantic-interposition" COMPILER_SUPPORTS_FNO_SEMANTIC_INTERPOSITION)
+  if(COMPILER_SUPPORTS_FNO_SEMANTIC_INTERPOSITION)
+    add_compile_options("-fno-semantic-interposition")
+  endif()
+endif()

+ 33 - 17
dtool/Package.cmake

@@ -1,5 +1,5 @@
 set(_thirdparty_dir_default "${PROJECT_SOURCE_DIR}/thirdparty")
-if(NOT (APPLE OR WIN32) OR NOT IS_DIRECTORY "${_thirdparty_dir_default}")
+if(NOT IS_DIRECTORY "${_thirdparty_dir_default}")
   set(_thirdparty_dir_default "")
 endif()
 
@@ -47,6 +47,27 @@ if(THIRDPARTY_DIRECTORY)
     set(BISON_ROOT "${THIRDPARTY_DIRECTORY}/win-util")
     set(FLEX_ROOT "${THIRDPARTY_DIRECTORY}/win-util")
 
+  elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux")
+    if(CMAKE_SYSTEM_PROCESSOR STREQUAL "aarch64")
+      set(_package_dir ${THIRDPARTY_DIRECTORY}/linux-libs-arm64)
+    elseif(CMAKE_SIZEOF_VOID_P EQUAL 8)
+      set(_package_dir ${THIRDPARTY_DIRECTORY}/linux-libs-x64)
+    else()
+      set(_package_dir ${THIRDPARTY_DIRECTORY}/linux-libs-a)
+    endif()
+
+  elseif(CMAKE_SYSTEM_NAME STREQUAL "FreeBSD")
+    if(CMAKE_SYSTEM_PROCESSOR STREQUAL "aarch64")
+      set(_package_dir ${THIRDPARTY_DIRECTORY}/freebsd-libs-arm64)
+    elseif(CMAKE_SIZEOF_VOID_P EQUAL 8)
+      set(_package_dir ${THIRDPARTY_DIRECTORY}/freebsd-libs-x64)
+    else()
+      set(_package_dir ${THIRDPARTY_DIRECTORY}/freebsd-libs-a)
+    endif()
+
+  elseif(CMAKE_SYSTEM_NAME STREQUAL "Android")
+    set(_package_dir ${THIRDPARTY_DIRECTORY}/android-libs-${CMAKE_ANDROID_ARCH})
+
   else()
     message(FATAL_ERROR
       "You can't use THIRDPARTY_DIRECTORY on this platform. Unset it to continue.")
@@ -187,19 +208,6 @@ if(Python_FOUND)
   set(PYTHON_INCLUDE_DIRS ${Python_INCLUDE_DIRS})
   set(PYTHON_LIBRARY_DIRS ${Python_LIBRARY_DIRS})
   set(PYTHON_VERSION_STRING ${Python_VERSION})
-
-elseif(CMAKE_VERSION VERSION_LESS "3.12")
-  find_package(PythonInterp ${WANT_PYTHON_VERSION} QUIET)
-  find_package(PythonLibs ${PYTHON_VERSION_STRING} QUIET)
-
-  if(PYTHONLIBS_FOUND)
-    set(PYTHON_FOUND ON)
-
-    if(NOT PYTHON_VERSION_STRING)
-      set(PYTHON_VERSION_STRING ${PYTHONLIBS_VERSION_STRING})
-    endif()
-  endif()
-
 endif()
 
 if(CMAKE_VERSION VERSION_LESS "3.15")
@@ -366,14 +374,22 @@ package_option(TIFF "Enable support for loading .tif images.")
 package_status(TIFF "libtiff")
 
 # OpenEXR
-find_package(OpenEXR QUIET MODULE)
+find_package(OpenEXR QUIET)
 
-package_option(OpenEXR "Enable support for loading .exr images.")
+if (TARGET OpenEXR::IlmImf AND NOT TARGET OpenEXR::OpenEXR)
+  package_option(OpenEXR
+    "Enable support for loading .exr images."
+    IMPORTED_AS OpenEXR::IlmImf)
+else()
+  package_option(OpenEXR
+    "Enable support for loading .exr images."
+    IMPORTED_AS OpenEXR::OpenEXR)
+endif()
 
 package_status(OpenEXR "OpenEXR")
 
 # libsquish
-find_package(LibSquish QUIET)
+find_package(LibSquish QUIET MODULE)
 
 package_option(SQUISH
   "Enables support for automatic compression of DXT textures."

+ 0 - 4
dtool/PandaVersion.cmake

@@ -46,10 +46,6 @@ math(EXPR PANDA_NUMERIC_VERSION "${PROJECT_VERSION_MAJOR}*1000000 + ${PROJECT_VE
 
 # If SOURCE_DATE_EPOCH is set, it affects PandaSystem::get_build_date()
 if(DEFINED ENV{SOURCE_DATE_EPOCH})
-  if(CMAKE_VERSION VERSION_LESS "3.8")
-    message(FATAL_ERROR "CMake 3.8 is required to support SOURCE_DATE_EPOCH properly.")
-  endif()
-
   string(TIMESTAMP _build_date "%b %d %Y %H:%M:%S" UTC)
 
   # CMake doesn't support %e, replace leading zero in day with space

File diff suppressed because it is too large
+ 211 - 93
dtool/metalibs/dtoolconfig/pydtool.cxx


+ 2 - 2
dtool/src/cppparser/cppManifest.cxx

@@ -226,8 +226,8 @@ output(std::ostream &out) const {
         out << "$1";
       }
 
-      for (int i = 1; i < _num_parameters; ++i) {
-        if (_variadic_param == i) {
+      for (size_t i = 1; i < _num_parameters; ++i) {
+        if (_variadic_param == (int)i) {
           out << ", ...";
         } else {
           out << ", $" << i + 1;

+ 1 - 1
dtool/src/cppparser/cppManifest.h

@@ -43,7 +43,7 @@ public:
 
   std::string _name;
   bool _has_parameters;
-  int _num_parameters;
+  size_t _num_parameters;
   int _variadic_param;
   cppyyltype _loc;
   CPPExpression *_expr;

+ 2 - 2
dtool/src/cppparser/cppNamespace.cxx

@@ -22,9 +22,9 @@
 CPPNamespace::
 CPPNamespace(CPPIdentifier *ident, CPPScope *scope, const CPPFile &file) :
   CPPDeclaration(file),
+  _is_inline(false),
   _ident(ident),
-  _scope(scope),
-  _is_inline(false)
+  _scope(scope)
 {
 }
 

+ 2 - 2
dtool/src/cppparser/cppStructType.cxx

@@ -58,8 +58,8 @@ CPPStructType(const CPPStructType &copy) :
   CPPExtensionType(copy),
   _scope(copy._scope),
   _incomplete(copy._incomplete),
-  _derivation(copy._derivation),
-  _final(copy._final)
+  _final(copy._final),
+  _derivation(copy._derivation)
 {
   _subst_decl_recursive_protect = false;
 }

+ 7 - 0
dtool/src/dtoolbase/CMakeLists.txt

@@ -46,6 +46,7 @@ set(P3DTOOLBASE_HEADERS
   typeRegistryNode.I typeRegistryNode.h
   typedObject.I typedObject.h
   pallocator.T pallocator.h
+  patomic.h patomic.I
   pdeque.h plist.h pmap.h pset.h
   pvector.h epvector.h
   lookup3.h
@@ -70,6 +71,7 @@ set(P3DTOOLBASE_SOURCES
   mutexWin32Impl.cxx
   mutexSpinlockImpl.cxx
   neverFreeMemory.cxx
+  patomic.cxx
   pdtoa.cxx
   pstrtod.cxx
   register_type.cxx
@@ -95,6 +97,11 @@ target_include_directories(p3dtoolbase PUBLIC
 target_link_libraries(p3dtoolbase PKG::EIGEN PKG::THREADS PKG::MIMALLOC)
 target_interrogate(p3dtoolbase ${P3DTOOLBASE_SOURCES} EXTENSIONS ${P3DTOOLBASE_IGATEEXT})
 
+if(HAVE_MIMALLOC AND CMAKE_CXX_COMPILER_ID MATCHES "^(GNU|Clang)$")
+  # Do not re-export symbols from these libraries.
+  target_link_options(p3dtoolbase PRIVATE "LINKER:--exclude-libs,libmimalloc.a")
+endif()
+
 if(NOT BUILD_METALIBS)
   install(TARGETS p3dtoolbase
     EXPORT Core COMPONENT Core

+ 1 - 1
dtool/src/dtoolbase/mutexWin32Impl.cxx

@@ -13,7 +13,7 @@
 
 #include "selectThreadImpl.h"
 
-#if defined(_WIN32) && !defined(CPPPARSER)
+#ifdef _WIN32
 
 #include "mutexWin32Impl.h"
 

+ 1 - 0
dtool/src/dtoolbase/p3dtoolbase_composite2.cxx

@@ -1,3 +1,4 @@
+#include "patomic.cxx"
 #include "mutexPosixImpl.cxx"
 #include "mutexWin32Impl.cxx"
 #include "mutexSpinlockImpl.cxx"

+ 211 - 6
dtool/src/dtoolbase/patomic.I

@@ -11,6 +11,7 @@
  * @date 2022-01-28
  */
 
+#if defined(THREAD_DUMMY_IMPL) || defined(THREAD_SIMPLE_IMPL)
 /**
  * Value initializer.
  */
@@ -247,21 +248,225 @@ operator ^=(T arg) noexcept {
   return _value ^= arg;
 }
 
+#endif  // defined(THREAD_DUMMY_IMPL) || defined(THREAD_SIMPLE_IMPL)
+
+/**
+ * Initializes the variable to zero (according to C++20 semantics, NOT C++11
+ * semantics!)
+ */
+constexpr patomic_unsigned_lock_free::
+patomic_unsigned_lock_free() noexcept :
+  patomic<uint32_t>(0u) {
+}
+
+/**
+ * Initializes the variable to the given value.
+ */
+constexpr patomic_unsigned_lock_free::
+patomic_unsigned_lock_free(uint32_t desired) noexcept :
+  patomic<uint32_t>(desired) {
+}
+
+/**
+ * Waits until the value is no longer equal to the given value.
+ */
+ALWAYS_INLINE void patomic_unsigned_lock_free::
+wait(uint32_t old, std::memory_order order) const noexcept {
+  if (load(order) == old) {
+    patomic_wait((const volatile uint32_t *)this, old);
+  }
+}
+
+/**
+ * Wakes up at least one thread waiting for the value to change.
+ */
+ALWAYS_INLINE void patomic_unsigned_lock_free::
+notify_one() noexcept {
+  patomic_notify_one((volatile uint32_t *)this);
+}
+
+/**
+ * Wakes up at least one thread waiting for the value to change.
+ */
+ALWAYS_INLINE void patomic_unsigned_lock_free::
+notify_all() noexcept {
+  patomic_notify_all((volatile uint32_t *)this);
+}
+
+/**
+ * Initializes the variable to zero (according to C++20 semantics, NOT C++11
+ * semantics!)
+ */
+constexpr patomic_signed_lock_free::
+patomic_signed_lock_free() noexcept :
+  patomic<int32_t>(0) {
+}
+
+/**
+ * Initializes the variable to the given value.
+ */
+constexpr patomic_signed_lock_free::
+patomic_signed_lock_free(int32_t desired) noexcept :
+  patomic<int32_t>(desired) {
+}
+
+/**
+ * Waits until the value is no longer equal to the given value.
+ */
+ALWAYS_INLINE void patomic_signed_lock_free::
+wait(int32_t old, std::memory_order order) const noexcept {
+  if (load(order) == old) {
+    patomic_wait((const volatile int32_t *)this, old);
+  }
+}
+
+/**
+ * Wakes up at least one thread waiting for the value to change.
+ */
+ALWAYS_INLINE void patomic_signed_lock_free::
+notify_one() noexcept {
+  patomic_notify_one((volatile int32_t *)this);
+}
+
+/**
+ * Wakes up at least one thread waiting for the value to change.
+ */
+ALWAYS_INLINE void patomic_signed_lock_free::
+notify_all() noexcept {
+  patomic_notify_all((volatile int32_t *)this);
+}
+
+/**
+ * Allows assignment from ATOMIC_FLAG_INIT.
+ */
+constexpr patomic_flag::
+patomic_flag(bool desired) noexcept : _value(desired) {
+}
+
+/**
+ * Sets the flag to false.
+ */
+ALWAYS_INLINE void patomic_flag::
+clear(std::memory_order order) noexcept {
+  _value.store(0u, order);
+}
 
 /**
  * Sets the flag to true and returns the previous value.
  */
 ALWAYS_INLINE bool patomic_flag::
 test_and_set(std::memory_order order) noexcept {
-  bool value = __internal_flag;
-  __internal_flag = true;
-  return value;
+  return _value.exchange(1u, order) != 0u;
 }
 
 /**
- * Sets the flag to false.
+ * Returns the current value of the flag.
+ */
+ALWAYS_INLINE bool patomic_flag::
+test(std::memory_order order) const noexcept {
+  return _value.load(order) != 0u;
+}
+
+/**
+ * Waits until the value is no longer equal to the given value.
  */
 ALWAYS_INLINE void patomic_flag::
-clear(std::memory_order order) noexcept {
-  __internal_flag = false;
+wait(bool old, std::memory_order order) const noexcept {
+  _value.wait(old, order);
+}
+
+/**
+ * Wakes up at least one thread waiting for the value to change.
+ */
+ALWAYS_INLINE void patomic_flag::
+notify_one() noexcept {
+  _value.notify_one();
+}
+
+/**
+ * Wakes up at least one thread waiting for the value to change.
+ */
+ALWAYS_INLINE void patomic_flag::
+notify_all() noexcept {
+  _value.notify_all();
+}
+
+/**
+ *
+ */
+ALWAYS_INLINE void
+patomic_wait(const volatile int32_t *value, int32_t old) {
+  patomic_wait((const volatile uint32_t *)value, (uint32_t)old);
+}
+
+/**
+ *
+ */
+ALWAYS_INLINE void
+patomic_notify_one(volatile int32_t *value) {
+  patomic_notify_one((volatile uint32_t *)value);
+}
+
+/**
+ *
+ */
+ALWAYS_INLINE void
+patomic_notify_all(volatile int32_t *value) {
+  patomic_notify_all((volatile uint32_t *)value);
+}
+
+/**
+ *
+ */
+ALWAYS_INLINE void
+patomic_wait(const volatile uint32_t *value, uint32_t old) {
+#ifdef __linux__
+  while (__atomic_load_n(value, __ATOMIC_SEQ_CST) == old) {
+    syscall(SYS_futex, old, FUTEX_WAIT_PRIVATE, old, 0, 0, 0);
+  }
+//#elif _WIN32_WINNT >= _WIN32_WINNT_WIN8
+//  while (*value == old) {
+//    WaitOnAddress((volatile void *)value, &old, sizeof(uint32_t), INFINITE);
+//  }
+#elif defined(_WIN32)
+  while (*value == old) {
+    _patomic_wait_func((volatile void *)value, &old, sizeof(uint32_t), INFINITE);
+  }
+#elif defined(HAVE_POSIX_THREADS)
+  _patomic_wait(value, old);
+#else
+  while (*value == old);
+#endif
+}
+
+/**
+ *
+ */
+ALWAYS_INLINE void
+patomic_notify_one(volatile uint32_t *value) {
+#ifdef __linux__
+  syscall(SYS_futex, value, FUTEX_WAKE_PRIVATE, 1, 0, 0, 0);
+//#elif _WIN32_WINNT >= _WIN32_WINNT_WIN8
+//  WakeByAddressSingle((void *)value);
+#elif defined(_WIN32)
+  _patomic_wake_one_func((void *)value);
+#elif defined(HAVE_POSIX_THREADS)
+  _patomic_notify_all(value);
+#endif
+}
+
+/**
+ *
+ */
+ALWAYS_INLINE void
+patomic_notify_all(volatile uint32_t *value) {
+#ifdef __linux__
+  syscall(SYS_futex, value, FUTEX_WAKE_PRIVATE, INT_MAX, 0, 0, 0);
+//#elif _WIN32_WINNT >= _WIN32_WINNT_WIN8
+//  WakeByAddressAll((void *)value);
+#elif defined(_WIN32)
+  _patomic_wake_all_func((void *)value);
+#elif defined(HAVE_POSIX_THREADS)
+  _patomic_notify_all(value);
+#endif
 }

+ 168 - 0
dtool/src/dtoolbase/patomic.cxx

@@ -0,0 +1,168 @@
+/**
+ * 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 patomic.cxx
+ * @author rdb
+ * @date 2022-02-23
+ */
+
+#include "patomic.h"
+
+#include <functional>
+
+static_assert(sizeof(patomic_unsigned_lock_free) == sizeof(uint32_t),
+              "expected atomic uint32_t to have same size as uint32_t");
+static_assert(sizeof(patomic_signed_lock_free) == sizeof(int32_t),
+              "expected atomic int32_t to have same size as int32_t");
+static_assert(sizeof(uint32_t) == sizeof(int32_t),
+              "expected int32_t to have same size as uint32_t");
+
+#if !defined(CPPPARSER) && defined(_WIN32)
+
+// On Windows 7, we try to load the Windows 8 functions dynamically, and
+// fall back to a condition variable table if they aren't available.
+static BOOL __stdcall initialize_wait(volatile VOID *addr, PVOID cmp, SIZE_T size, DWORD timeout);
+static void __stdcall dummy_wake(PVOID addr) {}
+
+BOOL (__stdcall *_patomic_wait_func)(volatile VOID *, PVOID, SIZE_T, DWORD) = &initialize_wait;
+void (__stdcall *_patomic_wake_one_func)(PVOID) = &dummy_wake;
+void (__stdcall *_patomic_wake_all_func)(PVOID) = &dummy_wake;
+
+// Randomly pick an entry into the wait table based on the hash of the address.
+// It's possible to get hash collision, but that's not so bad, it just means
+// that the other thread will get a spurious wakeup.
+struct alignas(64) WaitTableEntry {
+  SRWLOCK _lock = SRWLOCK_INIT;
+  CONDITION_VARIABLE _cvar = CONDITION_VARIABLE_INIT;
+  DWORD _waiters = 0;
+};
+static WaitTableEntry _wait_table[64] = {};
+static const size_t _wait_hash_mask = 63;
+
+/**
+ * Emulates WakeByAddressSingle for Windows Vista and 7.
+ */
+static void __stdcall
+emulated_wake(PVOID addr) {
+  size_t i = std::hash<volatile void *>{}(addr) & (sizeof(_wait_table) / sizeof(WaitTableEntry) - 1);
+  WaitTableEntry &entry = _wait_table[i];
+  AcquireSRWLockExclusive(&entry._lock);
+  DWORD num_waiters = entry._waiters;
+  ReleaseSRWLockExclusive(&entry._lock);
+  if (num_waiters > 0) {
+    // We have to wake up all the threads, even if only one of them is for this
+    // address.  Some of them will get a spurious wakeup, but that's OK.
+    WakeAllConditionVariable(&entry._cvar);
+  }
+}
+
+/**
+ * Emulates WaitOnAddress for Windows Vista and 7.  Only supports aligned
+ * 32-bit values.
+ */
+static BOOL __stdcall
+emulated_wait(volatile VOID *addr, PVOID cmp, SIZE_T size, DWORD timeout) {
+  assert(size == sizeof(LONG));
+
+  LONG cmpval = *(LONG *)cmp;
+  if (*(LONG *)addr != cmpval) {
+    return TRUE;
+  }
+
+  size_t i = std::hash<volatile void *>{}(addr) & _wait_hash_mask;
+  WaitTableEntry &entry = _wait_table[i];
+  AcquireSRWLockExclusive(&entry._lock);
+  ++entry._waiters;
+  while (*(LONG *)addr == cmpval) {
+    if (SleepConditionVariableSRW(&entry._cvar, &entry._lock, timeout, 0) != 0) {
+      // Timeout.
+      --entry._waiters;
+      ReleaseSRWLockExclusive(&entry._lock);
+      return FALSE;
+    }
+  }
+  --entry._waiters;
+  ReleaseSRWLockExclusive(&entry._lock);
+  return TRUE;
+}
+
+/**
+ * Initially assigned to the wait function slot to initialize the function
+ * pointers.
+ */
+static BOOL __stdcall
+initialize_wait(volatile VOID *addr, PVOID cmp, SIZE_T size, DWORD timeout) {
+  // There's a chance of a race here, with two threads trying to initialize the
+  // functions at the same time.  That's OK, because they should all produce
+  // the same results, and the stores to the function pointers are atomic.
+  HMODULE lib = GetModuleHandleW(L"api-ms-win-core-synch-l1-2-0.dll");
+  if (lib) {
+    auto wait_func = (decltype(_patomic_wait_func))GetProcAddress(lib, "WaitOnAddress");
+    auto wake_one_func = (decltype(_patomic_wake_one_func))GetProcAddress(lib, "WakeByAddressSingle");
+    auto wake_all_func = (decltype(_patomic_wake_all_func))GetProcAddress(lib, "WakeByAddressAll");
+    if (wait_func && wake_one_func && wake_all_func) {
+      // Make sure that the wake function is guaranteed to be visible to other
+      // threads by the time we assign the wait function.
+      _patomic_wake_one_func = wake_one_func;
+      _patomic_wake_all_func = wake_all_func;
+      patomic_thread_fence(std::memory_order_release);
+      _patomic_wait_func = wait_func;
+      return wait_func(addr, cmp, size, timeout);
+    }
+  }
+
+  // We don't have Windows 8's functions, use the emulated wait and wake funcs.
+  _patomic_wake_one_func = &emulated_wake;
+  _patomic_wake_all_func = &emulated_wake;
+  patomic_thread_fence(std::memory_order_release);
+  _patomic_wait_func = &emulated_wait;
+
+  return emulated_wait(addr, cmp, size, timeout);
+}
+
+#elif !defined(CPPPARSER) && !defined(__linux__) && defined(HAVE_POSIX_THREADS)
+
+// Same as above, but using pthreads.
+struct alignas(64) WaitTableEntry {
+  pthread_mutex_t _lock = PTHREAD_MUTEX_INITIALIZER;
+  pthread_cond_t _cvar = PTHREAD_COND_INITIALIZER;
+  unsigned int _waiters = 0;
+};
+static WaitTableEntry _wait_table[64];
+static const size_t _wait_hash_mask = 63;
+
+/**
+ *
+ */
+void
+_patomic_wait(const volatile uint32_t *value, uint32_t old) {
+  WaitTableEntry &entry = _wait_table[std::hash<const volatile void *>{}(value) & _wait_hash_mask];
+  pthread_mutex_lock(&entry._lock);
+  ++entry._waiters;
+  while (__atomic_load_n(value, __ATOMIC_SEQ_CST) == old) {
+    pthread_cond_wait(&entry._cvar, &entry._lock);
+  }
+  --entry._waiters;
+  pthread_mutex_unlock(&entry._lock);
+}
+
+/**
+ *
+ */
+void
+_patomic_notify_all(volatile uint32_t *value) {
+  WaitTableEntry &entry = _wait_table[std::hash<const volatile void *>{}(value) & _wait_hash_mask];
+  pthread_mutex_lock(&entry._lock);
+  unsigned int num_waiters = entry._waiters;
+  pthread_mutex_unlock(&entry._lock);
+  if (num_waiters > 0) {
+    pthread_cond_broadcast(&entry._cvar);
+  }
+}
+
+#endif

+ 85 - 18
dtool/src/dtoolbase/patomic.h

@@ -19,6 +19,19 @@
 
 #include <atomic>
 
+#ifdef _WIN32
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN 1
+#endif
+#include <windows.h>
+#endif
+
+#ifdef __linux__
+#include <linux/futex.h>
+#include <sys/syscall.h>
+#include <unistd.h>
+#endif
+
 #if defined(THREAD_DUMMY_IMPL) || defined(THREAD_SIMPLE_IMPL)
 
 /**
@@ -73,36 +86,90 @@ private:
   T _value;
 };
 
+#define patomic_thread_fence(order) (std::atomic_signal_fence((order)))
+
+#else
+
+// We're using real threading, so use the real implementation.
+template<class T>
+using patomic = std::atomic<T>;
+
+#define patomic_thread_fence(order) (std::atomic_thread_fence((order)))
+
+#endif
+
 /**
- * Dummy implementation of std::atomic_flag that does not do any atomic
- * operations.
+ * Implementation of atomic_unsigned_lock_free with C++20 semantics.
  */
-struct EXPCL_DTOOL_DTOOLBASE patomic_flag {
-  constexpr patomic_flag() noexcept = default;
+class EXPCL_DTOOL_DTOOLBASE patomic_unsigned_lock_free : public patomic<uint32_t> {
+public:
+  typedef uint32_t value_type;
 
-  patomic_flag(const patomic_flag &) = delete;
-  patomic_flag &operator=(const patomic_flag &) = delete;
-
-  ALWAYS_INLINE bool test_and_set(std::memory_order order = std::memory_order_seq_cst) noexcept;
-  ALWAYS_INLINE void clear(std::memory_order order = std::memory_order_seq_cst) noexcept;
+  constexpr patomic_unsigned_lock_free() noexcept;
+  constexpr patomic_unsigned_lock_free(uint32_t desired) noexcept;
 
-  bool __internal_flag = false;
+  INLINE void wait(uint32_t old, std::memory_order order = std::memory_order_seq_cst) const noexcept;
+  ALWAYS_INLINE void notify_one() noexcept;
+  ALWAYS_INLINE void notify_all() noexcept;
 };
 
-#define patomic_thread_fence(order) (std::atomic_signal_fence((order)))
+/**
+ * Implementation of atomic_signed_lock_free with C++20 semantics.
+ */
+class EXPCL_DTOOL_DTOOLBASE patomic_signed_lock_free : public patomic<int32_t> {
+public:
+  typedef int32_t value_type;
 
-#include "patomic.I"
+  constexpr patomic_signed_lock_free() noexcept;
+  constexpr patomic_signed_lock_free(int32_t desired) noexcept;
 
-#else
+  INLINE void wait(int32_t old, std::memory_order order = std::memory_order_seq_cst) const noexcept;
+  ALWAYS_INLINE void notify_one() noexcept;
+  ALWAYS_INLINE void notify_all() noexcept;
+};
 
-// We're using real threading, so use the real implementation.
-template<class T>
-using patomic = std::atomic<T>;
+/**
+ * Implementation of atomic_flag with C++20 semantics.
+ */
+class EXPCL_DTOOL_DTOOLBASE patomic_flag {
+public:
+  constexpr patomic_flag() noexcept = default;
+  constexpr patomic_flag(bool desired) noexcept;
 
-typedef std::atomic_flag patomic_flag;
+  patomic_flag(const patomic_flag &) = delete;
+  patomic_flag &operator=(const patomic_flag &) = delete;
 
-#define patomic_thread_fence(order) (std::atomic_thread_fence((order)))
+  ALWAYS_INLINE void clear(std::memory_order order = std::memory_order_seq_cst) noexcept;
+  ALWAYS_INLINE bool test_and_set(std::memory_order order = std::memory_order_seq_cst) noexcept;
+  ALWAYS_INLINE bool test(std::memory_order order = std::memory_order_seq_cst) const noexcept;
+
+  ALWAYS_INLINE void wait(bool old, std::memory_order order = std::memory_order_seq_cst) const noexcept;
+  ALWAYS_INLINE void notify_one() noexcept;
+  ALWAYS_INLINE void notify_all() noexcept;
 
+private:
+  patomic_unsigned_lock_free _value { 0u };
+};
+
+#ifndef CPPPARSER
+ALWAYS_INLINE void patomic_wait(const volatile int32_t *value, int32_t old);
+ALWAYS_INLINE void patomic_notify_one(volatile int32_t *value);
+ALWAYS_INLINE void patomic_notify_all(volatile int32_t *value);
+
+ALWAYS_INLINE void patomic_wait(const volatile uint32_t *value, uint32_t old);
+ALWAYS_INLINE void patomic_notify_one(volatile uint32_t *value);
+ALWAYS_INLINE void patomic_notify_all(volatile uint32_t *value);
+
+#ifdef _WIN32
+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_all_func)(PVOID);
+#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_notify_all(volatile uint32_t *value);
 #endif
 
+#include "patomic.I"
+#endif  // CPPPARSER
+
 #endif

+ 0 - 1
dtool/src/dtoolbase/typeHandle_ext.cxx

@@ -38,7 +38,6 @@ make(PyTypeObject *tp) {
 PyObject *Extension<TypeHandle>::
 __reduce__() const {
   extern struct Dtool_PyTypedObject Dtool_TypeHandle;
-  extern struct Dtool_PyTypedObject Dtool_TypeRegistry;
 
   if (!*_this) {
     PyObject *func = PyObject_GetAttrString((PyObject *)&Dtool_TypeHandle, "none");

+ 39 - 6
dtool/src/dtoolutil/executionEnvironment.cxx

@@ -259,10 +259,14 @@ ns_has_environment_variable(const string &var) const {
     return true;
   }
 
-#ifndef PREREAD_ENVIRONMENT
-  return getenv(var.c_str()) != nullptr;
-#else
+#ifdef PREREAD_ENVIRONMENT
   return false;
+#elif defined(_MSC_VER)
+  size_t size = 0;
+  getenv_s(&size, nullptr, 0, var.c_str());
+  return size != 0;
+#else
+  return getenv(var.c_str()) != nullptr;
 #endif
 }
 
@@ -301,11 +305,24 @@ ns_get_environment_variable(const string &var) const {
   }
 
 #ifndef PREREAD_ENVIRONMENT
+#ifdef _MSC_VER
+  std::string value(128, '\0');
+  size_t size = value.size();
+  while (getenv_s(&size, &value[0], size, var.c_str()) == ERANGE) {
+    value.resize(size);
+  }
+  if (size != 0) {
+    // Strip off the trailing null byte.
+    value.resize(size - 1);
+    return value;
+  }
+#else
   const char *def = getenv(var.c_str());
   if (def != nullptr) {
     return def;
   }
 #endif
+#endif
 
 #ifdef _WIN32
   // On Windows only, we also simulate several standard folder names as
@@ -414,14 +431,15 @@ ns_get_environment_variable(const string &var) const {
 void ExecutionEnvironment::
 ns_set_environment_variable(const string &var, const string &value) {
   _variables[var] = value;
+
+#ifdef _MSC_VER
+  _putenv_s(var.c_str(), value.c_str());
+#else
   string putstr = var + "=" + value;
 
   // putenv() requires us to malloc a new C-style string.
   char *put = (char *)malloc(putstr.length() + 1);
   strcpy(put, putstr.c_str());
-#ifdef _MSC_VER
-  _putenv(put);
-#else
   putenv(put);
 #endif
 }
@@ -447,12 +465,27 @@ ns_clear_shadow(const string &var) {
 
 #ifdef PREREAD_ENVIRONMENT
   // Now we have to replace the value in the table.
+#ifdef _MSC_VER
+  std::string value(128, '\0');
+  size_t size = value.size();
+  while (getenv_s(&size, &value[0], size, var.c_str()) == ERANGE) {
+    value.resize(size);
+  }
+  if (size != 0) {
+    // Strip off the trailing null byte.
+    value.resize(size - 1);
+    (*vi).second = std::move(value);
+  } else {
+    _variables.erase(vi);
+  }
+#else
   const char *def = getenv(var.c_str());
   if (def != nullptr) {
     (*vi).second = def;
   } else {
     _variables.erase(vi);
   }
+#endif
 #endif  // PREREAD_ENVIRONMENT
 }
 

+ 15 - 1
dtool/src/dtoolutil/filename.cxx

@@ -152,10 +152,22 @@ get_panda_root() {
 
   if (panda_root == nullptr) {
     panda_root = new string;
+
+#ifdef _MSC_VER
+    char *envvar = nullptr;
+    size_t size = 0;
+    while (getenv_s(&size, envvar, size, "PANDA_ROOT") == ERANGE) {
+      envvar = (char *)alloca(size);
+    }
+    if (size != 0) {
+      (*panda_root) = front_to_back_slash(envvar);
+    }
+#else
     const char *envvar = getenv("PANDA_ROOT");
     if (envvar != nullptr) {
       (*panda_root) = front_to_back_slash(envvar);
     }
+#endif
 
     // Ensure the string ends in a backslash.  If PANDA_ROOT is empty or
     // undefined, this function must return a single backslash--not an empty
@@ -477,7 +489,8 @@ get_home_directory() {
   if (AtomicAdjust::get_ptr(_home_directory) == nullptr) {
     Filename home_directory;
 
-    // In all environments, check $HOME first.
+    // In all environments except Windows, check $HOME first.
+#ifndef _WIN32
     char *home = getenv("HOME");
     if (home != nullptr) {
       Filename dirname = from_os_specific(home);
@@ -487,6 +500,7 @@ get_home_directory() {
         }
       }
     }
+#endif
 
     if (home_directory.empty()) {
 #ifdef _WIN32

+ 2 - 0
dtool/src/dtoolutil/pandaSystem.cxx

@@ -53,6 +53,8 @@ PandaSystem() :
   set_system_tag("system", "malloc", "dlmalloc");
 #elif defined(USE_MEMORY_PTMALLOC2)
   set_system_tag("system", "malloc", "ptmalloc2");
+#elif defined(USE_MEMORY_MIMALLOC)
+  set_system_tag("system", "malloc", "mimalloc");
 #else
   set_system_tag("system", "malloc", "malloc");
 #endif

+ 10 - 2
dtool/src/dtoolutil/panda_getopt_impl.cxx

@@ -140,6 +140,10 @@ PandaGetopt(int argc, char *const argv[], const char *optstring,
   // _options[0] is used for invalid characters.
   _options.push_back(Option('?', no_argument));
 
+#ifdef _MSC_VER
+  size_t size;
+#endif
+
   if (optstring[0] == '-') {
     // RETURN_IN_ORDER: Non-option arguments (operands) are handled as if they
     // were the argument to an option with the value 1 ('\001').
@@ -154,8 +158,12 @@ PandaGetopt(int argc, char *const argv[], const char *optstring,
     // argument is reached, or when the element of argv is "--".
     ++optstring;
     _require_order = true;
-
-  } else if (getenv("POSIXLY_CORRECT") != nullptr) {
+  }
+#ifdef _MSC_VER
+  else if (getenv_s(&size, nullptr, 0, "POSIXLY_CORRECT") == 0 && size != 0) {
+#else
+  else if (getenv("POSIXLY_CORRECT") != nullptr) {
+#endif
     // REQUIRE_ORDER.
     _require_order = true;
 

+ 3 - 4
dtool/src/dtoolutil/pfstreamBuf.cxx

@@ -331,8 +331,9 @@ open_pipe(const string &cmd) {
 
   // Both WinExec() and CreateProcess() want a non-const char pointer.  Maybe
   // they change it, and maybe they don't.  I'm not taking chances.
-  char *cmdline = new char[cmd.length() + 1];
-  strcpy(cmdline, cmd.c_str());
+  char *cmdline = (char *)alloca(cmd.size() + 1);
+  memcpy(cmdline, cmd.data(), cmd.size());
+  cmdline[cmd.size()] = 0;
 
   // We should be using CreateProcess() instead of WinExec(), but that seems
   // to be likely to crash Win98.  WinExec() seems better behaved, and it's
@@ -345,8 +346,6 @@ open_pipe(const string &cmd) {
     // Don't return yet, since we still need to clean up.
   }
 
-  delete[] cmdline;
-
   // Now restore our own stdout, up here in the parent process.
   if (!SetStdHandle(STD_OUTPUT_HANDLE, hSaveStdout)) {
 #ifndef NDEBUG

+ 3 - 0
dtool/src/interrogate/functionRemap.cxx

@@ -327,6 +327,9 @@ make_wrapper_entry(FunctionIndex function_index) {
     if ((*pi)._has_name) {
       param._parameter_flags |= InterrogateFunctionWrapper::PF_has_name;
     }
+    if ((*pi)._remap->has_default_value()) {
+      param._parameter_flags |= InterrogateFunctionWrapper::PF_is_optional;
+    }
     iwrapper._parameters.push_back(param);
   }
 

+ 4 - 4
dtool/src/interrogate/interfaceMaker.cxx

@@ -77,8 +77,8 @@ InterfaceMaker::Function::
  */
 InterfaceMaker::MakeSeq::
 MakeSeq(const string &name, const InterrogateMakeSeq &imake_seq) :
-  _name(name),
   _imake_seq(imake_seq),
+  _name(name),
   _length_getter(nullptr),
   _element_getter(nullptr)
 {
@@ -237,7 +237,7 @@ generate_wrappers() {
 
   int num_global_elements = idb->get_num_global_elements();
   for (int gi = 0; gi < num_global_elements; ++gi) {
-    printf(" Global Type = %d", gi);
+    //printf(" Global Type = %d", gi);
     TypeIndex type_index = idb->get_global_element(gi);
     record_object(type_index);
   }
@@ -256,12 +256,12 @@ generate_wrappers() {
       FunctionIndex func_index = iman.get_getter();
       record_function(dummy_type, func_index);
     }
-    printf(" Manifests %d\n", mi);
+    //printf(" Manifests %d\n", mi);
   }
 
   int num_elements = idb->get_num_global_elements();
   for (int ei = 0; ei < num_elements; ei++) {
-    printf(" Element %d\n", ei);
+    //printf(" Element %d\n", ei);
 
     ElementIndex element_index = idb->get_global_element(ei);
     const InterrogateElement &ielement = idb->get_element(element_index);

+ 2 - 2
dtool/src/interrogate/interfaceMakerPythonNative.cxx

@@ -2510,11 +2510,11 @@ write_module_class(ostream &out, Object *obj) {
           out << "  if (arg2 != nullptr && arg2 != Py_None) {\n";
           out << "    PyObject *args = PyTuple_Pack(2, arg, arg2);\n";
           write_function_forset(out, two_param_remaps, 2, 2, expected_params, 4,
-                                true, true, AT_varargs, RF_pyobject | RF_err_null | RF_decref_args, true);
+                                true, true, AT_varargs, return_flags | RF_decref_args, true);
           out << "    Py_DECREF(args);\n";
           out << "  } else {\n";
           write_function_forset(out, one_param_remaps, 1, 1, expected_params, 4,
-                                true, true, AT_single_arg, RF_pyobject | RF_err_null, true);
+                                true, true, AT_single_arg, return_flags, true);
           out << "  }\n\n";
 
           out << "  if (!_PyErr_OCCURRED()) {\n";

+ 2 - 2
dtool/src/interrogate/interfaceMakerPythonObj.cxx

@@ -102,10 +102,10 @@ write_module(ostream &out,ostream *out_h, InterrogateModuleDef *def) {
          ++fi) {
       Function *func = (*fi);
       out << "  { \"" << func->_ifunc.get_name() << "\", &" << func->_name
-          << ", METH_VARARGS },\n";
+          << ", METH_VARARGS, nullptr },\n";
     }
   }
-  out << "  { nullptr, nullptr }\n"
+  out << "  { nullptr, nullptr, 0, nullptr }\n"
       << "};\n\n"
 
       << "#if PY_MAJOR_VERSION >= 3\n"

+ 2 - 2
dtool/src/interrogate/interfaceMakerPythonSimple.cxx

@@ -89,10 +89,10 @@ write_module(ostream &out,ostream *out_h, InterrogateModuleDef *def) {
     for (ri = func->_remaps.begin(); ri != func->_remaps.end(); ++ri) {
       FunctionRemap *remap = (*ri);
       out << "  { \"" << remap->_reported_name << "\", &"
-          << remap->_wrapper_name << ", METH_VARARGS },\n";
+          << remap->_wrapper_name << ", METH_VARARGS, nullptr },\n";
     }
   }
-  out << "  { nullptr, nullptr }\n"
+  out << "  { nullptr, nullptr, 0, nullptr }\n"
       << "};\n\n"
 
       << "#if PY_MAJOR_VERSION >= 3\n"

+ 7 - 0
dtool/src/interrogate/interrogate.cxx

@@ -547,8 +547,15 @@ main(int argc, char **argv) {
   // We allow overriding this value by setting SOURCE_DATE_EPOCH to support
   // reproducible builds.
   int file_identifier;
+#ifdef _MSC_VER
+  char source_date_epoch[64];
+  size_t source_date_epoch_size = 0;
+  if (getenv_s(&source_date_epoch_size, source_date_epoch,
+               sizeof(source_date_epoch), "SOURCE_DATE_EPOCH"), source_date_epoch_size > 1) {
+#else
   const char *source_date_epoch = getenv("SOURCE_DATE_EPOCH");
   if (source_date_epoch != nullptr && source_date_epoch[0] != 0) {
+#endif
     file_identifier = atoi(source_date_epoch);
   } else {
     file_identifier = time(nullptr);

+ 22 - 3
dtool/src/interrogate/interrogateBuilder.cxx

@@ -1071,6 +1071,10 @@ scan_function(CPPInstance *function) {
     return;
   }
 
+  if (TypeManager::involves_rvalue_reference(ftype)) {
+    return;
+  }
+
   get_function(function, "",
                nullptr, scope,
                InterrogateFunction::F_global);
@@ -1770,6 +1774,16 @@ get_function(CPPInstance *function, string description,
     ifunction->_flags |= InterrogateFunction::F_operator_typecast;
   }
 
+  if (ftype->_flags & CPPFunctionType::F_constructor) {
+    // This is a constructor.
+    ifunction->_flags |= InterrogateFunction::F_constructor;
+  }
+
+  if (ftype->_flags & CPPFunctionType::F_destructor) {
+    // This is a destructor.
+    ifunction->_flags |= InterrogateFunction::F_destructor;
+  }
+
   if (function->_storage_class & CPPInstance::SC_virtual) {
     // This is a virtual function.
     ifunction->_flags |= InterrogateFunction::F_virtual;
@@ -2747,7 +2761,8 @@ define_struct_type(InterrogateType &itype, CPPStructType *cpptype,
     function->_storage_class |= CPPInstance::SC_inline | CPPInstance::SC_defaulted;
     function->_vis = V_published;
 
-    FunctionIndex index = get_function(function, "", cpptype, cpptype->get_scope(), 0);
+    FunctionIndex index = get_function(function, "", cpptype, cpptype->get_scope(),
+                                       InterrogateFunction::F_constructor);
     if (find(itype._constructors.begin(), itype._constructors.end(),
              index) == itype._constructors.end()) {
       itype._constructors.push_back(index);
@@ -2773,7 +2788,8 @@ define_struct_type(InterrogateType &itype, CPPStructType *cpptype,
     function->_storage_class |= CPPInstance::SC_inline | CPPInstance::SC_defaulted;
     function->_vis = V_published;
 
-    FunctionIndex index = get_function(function, "", cpptype, cpptype->get_scope(), 0);
+    FunctionIndex index = get_function(function, "", cpptype, cpptype->get_scope(),
+                                       InterrogateFunction::F_constructor);
     if (find(itype._constructors.begin(), itype._constructors.end(),
              index) == itype._constructors.end()) {
       itype._constructors.push_back(index);
@@ -2814,7 +2830,7 @@ define_struct_type(InterrogateType &itype, CPPStructType *cpptype,
 
     itype._destructor = get_function(function, "",
                                      cpptype, cpptype->get_scope(),
-                                     0);
+                                     InterrogateFunction::F_destructor);
     itype._flags |= InterrogateType::F_implicit_destructor;
   }
 }
@@ -2988,6 +3004,9 @@ define_method(CPPInstance *function, InterrogateType &itype,
     // If it isn't, we should publish this method anyway.
   }
 
+  if (TypeManager::involves_rvalue_reference(ftype)) {
+    return;
+  }
 
   FunctionIndex index = get_function(function, "", struct_type, scope, 0);
   if (index != 0) {

+ 40 - 0
dtool/src/interrogate/typeManager.cxx

@@ -2017,6 +2017,46 @@ involves_protected(CPPType *type) {
   }
 }
 
+/**
+ * Returns true if the type involves an rvalue reference.
+ */
+bool TypeManager::
+involves_rvalue_reference(CPPType *type) {
+  switch (type->get_subtype()) {
+  case CPPDeclaration::ST_const:
+    return involves_rvalue_reference(type->as_const_type()->_wrapped_around);
+
+  case CPPDeclaration::ST_reference:
+    return type->as_reference_type()->_value_category == CPPReferenceType::VC_rvalue;
+
+  case CPPDeclaration::ST_pointer:
+    return involves_rvalue_reference(type->as_pointer_type()->_pointing_at);
+
+  case CPPDeclaration::ST_function:
+    {
+      CPPFunctionType *ftype = type->as_function_type();
+      if (involves_rvalue_reference(ftype->_return_type)) {
+        return true;
+      }
+      const CPPParameterList::Parameters &params =
+        ftype->_parameters->_parameters;
+      CPPParameterList::Parameters::const_iterator pi;
+      for (pi = params.begin(); pi != params.end(); ++pi) {
+        if (involves_rvalue_reference((*pi)->_type)) {
+          return true;
+        }
+      }
+      return false;
+    }
+
+  case CPPDeclaration::ST_typedef:
+    return involves_rvalue_reference(type->as_typedef_type()->_type);
+
+  default:
+    return false;
+  }
+}
+
 /**
  * Returns the type this pointer type points to.
  */

+ 1 - 0
dtool/src/interrogate/typeManager.h

@@ -118,6 +118,7 @@ public:
   static bool is_handle(CPPType *type);
   static bool involves_unpublished(CPPType *type);
   static bool involves_protected(CPPType *type);
+  static bool involves_rvalue_reference(CPPType *type);
 
   static bool is_ostream(CPPType *type);
   static bool is_pointer_to_ostream(CPPType *type);

+ 11 - 0
dtool/src/interrogatedb/interrogateDatabase.cxx

@@ -968,6 +968,17 @@ read_new(std::istream &in, InterrogateModuleDef *def) {
 
       add_type(index, type);
       num_types--;
+
+      // Older versions of interrogate were not setting these flags.
+      const InterrogateType &itype = get_type(index);
+      FunctionIndex dtor = itype.get_destructor();
+      if (dtor != 0) {
+        update_function(dtor)._flags |= InterrogateFunction::F_destructor;
+      }
+      for (int i = 0; i < itype.number_of_constructors(); ++i) {
+        FunctionIndex ctor = itype.get_constructor(i);
+        update_function(ctor)._flags |= InterrogateFunction::F_constructor;
+      }
     }
   }
 

+ 16 - 0
dtool/src/interrogatedb/interrogateFunction.I

@@ -54,6 +54,22 @@ is_operator_typecast() const {
   return (_flags & F_operator_typecast) != 0;
 }
 
+/**
+ * Returns true if the function is a constructor.
+ */
+INLINE bool InterrogateFunction::
+is_constructor() const {
+  return (_flags & F_constructor) != 0;
+}
+
+/**
+ * Returns true if the function is a destructor.
+ */
+INLINE bool InterrogateFunction::
+is_destructor() const {
+  return (_flags & F_destructor) != 0;
+}
+
 /**
  * Return the class that owns the method, if is_method() returns true.
  */

+ 6 - 1
dtool/src/interrogatedb/interrogateFunction.h

@@ -38,6 +38,8 @@ public:
   INLINE bool is_method() const;
   INLINE bool is_unary_op() const;
   INLINE bool is_operator_typecast() const;
+  INLINE bool is_constructor() const;
+  INLINE bool is_destructor() const;
   INLINE TypeIndex get_class() const;
 
   INLINE bool has_scoped_name() const;
@@ -70,7 +72,9 @@ private:
     F_setter          = 0x0020,
     F_unary_op        = 0x0040,
     F_operator_typecast = 0x0080,
-    F_item_assignment = 0x0100,
+    F_constructor     = 0x0100,
+    F_destructor      = 0x0200,
+    F_item_assignment = 0x0400,
   };
 
   int _flags;
@@ -98,6 +102,7 @@ public:
   std::string _expression;
 
   friend class InterrogateBuilder;
+  friend class InterrogateDatabase;
   friend class InterfaceMakerC;
   friend class InterfaceMakerPythonSimple;
   friend class InterfaceMakerPythonNative;

+ 11 - 0
dtool/src/interrogatedb/interrogateFunctionWrapper.I

@@ -148,6 +148,17 @@ parameter_is_this(int n) const {
   return false;
 }
 
+/**
+ *
+ */
+INLINE bool InterrogateFunctionWrapper::
+parameter_is_optional(int n) const {
+  if (n >= 0 && n < (int)_parameters.size()) {
+    return (_parameters[n]._parameter_flags & PF_is_optional) != 0;
+  }
+  return false;
+}
+
 /**
  *
  */

+ 2 - 0
dtool/src/interrogatedb/interrogateFunctionWrapper.h

@@ -45,6 +45,7 @@ public:
   INLINE bool parameter_has_name(int n) const;
   INLINE const std::string &parameter_get_name(int n) const;
   INLINE bool parameter_is_this(int n) const;
+  INLINE bool parameter_is_optional(int n) const;
 
   INLINE const std::string &get_unique_name() const;
 
@@ -66,6 +67,7 @@ private:
   enum ParameterFlags {
     PF_has_name       = 0x0001,
     PF_is_this        = 0x0002,
+    PF_is_optional    = 0x0004,
   };
 
   int _flags;

+ 67 - 1
dtool/src/interrogatedb/interrogate_interface.cxx

@@ -266,6 +266,31 @@ interrogate_function_class(FunctionIndex function) {
   return InterrogateDatabase::get_ptr()->get_function(function).get_class();
 }
 
+bool
+interrogate_function_is_unary_op(FunctionIndex function) {
+  // cerr << "interrogate_function_is_unary_op(" << function << ")\n";
+  return InterrogateDatabase::get_ptr()->get_function(function).is_unary_op();
+}
+
+bool
+interrogate_function_is_operator_typecast(FunctionIndex function) {
+  // cerr << "interrogate_function_is_operator_typecast(" << function <<
+  // ")\n";
+  return InterrogateDatabase::get_ptr()->get_function(function).is_operator_typecast();
+}
+
+bool
+interrogate_function_is_constructor(FunctionIndex function) {
+  // cerr << "interrogate_function_is_constructor(" << function << ")\n";
+  return InterrogateDatabase::get_ptr()->get_function(function).is_constructor();
+}
+
+bool
+interrogate_function_is_destructor(FunctionIndex function) {
+  // cerr << "interrogate_function_is_destructor(" << function << ")\n";
+  return InterrogateDatabase::get_ptr()->get_function(function).is_destructor();
+}
+
 bool
 interrogate_function_has_module_name(FunctionIndex function) {
   // cerr << "interrogate_function_has_module_name(" << function << ")\n";
@@ -411,6 +436,12 @@ interrogate_wrapper_parameter_is_this(FunctionWrapperIndex wrapper, int n) {
   return InterrogateDatabase::get_ptr()->get_wrapper(wrapper).parameter_is_this(n);
 }
 
+bool
+interrogate_wrapper_parameter_is_optional(FunctionWrapperIndex wrapper, int n) {
+  // cerr << "interrogate_wrapper_is_optional(" << wrapper << ", " << n << ")\n";
+  return InterrogateDatabase::get_ptr()->get_wrapper(wrapper).parameter_is_optional(n);
+}
+
 bool
 interrogate_wrapper_has_pointer(FunctionWrapperIndex wrapper) {
   // cerr << "interrogate_wrapper_has_pointer(" << wrapper << ")\n";
@@ -476,11 +507,22 @@ interrogate_make_seq_num_name(MakeSeqIndex make_seq) {
 const char *
 interrogate_make_seq_element_name(MakeSeqIndex make_seq) {
   // cerr << "interrogate_make_seq_element_name(" << make_seq << ")\n";
-  static string result;
   FunctionIndex function = InterrogateDatabase::get_ptr()->get_make_seq(make_seq).get_element_getter();
   return interrogate_function_name(function);
 }
 
+FunctionIndex
+interrogate_make_seq_num_getter(MakeSeqIndex make_seq) {
+  // cerr << "interrogate_make_seq_num_getter(" << make_seq << ")\n";
+  return InterrogateDatabase::get_ptr()->get_make_seq(make_seq).get_length_getter();
+}
+
+FunctionIndex
+interrogate_make_seq_element_getter(MakeSeqIndex make_seq) {
+  // cerr << "interrogate_make_seq_element_getter(" << make_seq << ")\n";
+  return InterrogateDatabase::get_ptr()->get_make_seq(make_seq).get_element_getter();
+}
+
 int
 interrogate_number_of_global_types() {
   // cerr << "interrogate_number_of_global_types()\n";
@@ -668,12 +710,30 @@ interrogate_type_wrapped_type(TypeIndex type) {
   return InterrogateDatabase::get_ptr()->get_type(type).get_wrapped_type();
 }
 
+bool
+interrogate_type_is_array(TypeIndex type) {
+  // cerr << "interrogate_type_is_array(" << type << ")\n";
+  return InterrogateDatabase::get_ptr()->get_type(type).is_array();
+}
+
+int
+interrogate_type_array_size(TypeIndex type) {
+  // cerr << "interrogate_type_array_size(" << type << ")\n";
+  return InterrogateDatabase::get_ptr()->get_type(type).get_array_size();
+}
+
 bool
 interrogate_type_is_enum(TypeIndex type) {
   // cerr << "interrogate_type_is_enum(" << type << ")\n";
   return InterrogateDatabase::get_ptr()->get_type(type).is_enum();
 }
 
+bool
+interrogate_type_is_scoped_enum(TypeIndex type) {
+  // cerr << "interrogate_type_is_scoped_enum(" << type << ")\n";
+  return InterrogateDatabase::get_ptr()->get_type(type).is_scoped_enum();
+}
+
 int
 interrogate_type_number_of_enum_values(TypeIndex type) {
   // cerr << "interrogate_type_number_of_enum_values(" << type << ")\n";
@@ -828,6 +888,12 @@ interrogate_type_get_derivation(TypeIndex type, int n) {
   return InterrogateDatabase::get_ptr()->get_type(type).get_derivation(n);
 }
 
+bool
+interrogate_type_is_final(TypeIndex type) {
+  // cerr << "interrogate_type_is_final(" << type << ")\n";
+  return InterrogateDatabase::get_ptr()->get_type(type).is_final();
+}
+
 bool
 interrogate_type_derivation_has_upcast(TypeIndex type, int n) {
   // cerr << "interrogate_type_derivation_has_upcast(" << type << ", " << n <<

+ 13 - 1
dtool/src/interrogatedb/interrogate_interface.h

@@ -210,6 +210,10 @@ EXPCL_INTERROGATEDB const char *interrogate_function_prototype(FunctionIndex fun
 // if the function is a class method.
 EXPCL_INTERROGATEDB bool interrogate_function_is_method(FunctionIndex function);
 EXPCL_INTERROGATEDB TypeIndex interrogate_function_class(FunctionIndex function);
+EXPCL_INTERROGATEDB bool interrogate_function_is_unary_op(FunctionIndex function);
+EXPCL_INTERROGATEDB bool interrogate_function_is_operator_typecast(FunctionIndex function);
+EXPCL_INTERROGATEDB bool interrogate_function_is_constructor(FunctionIndex function);
+EXPCL_INTERROGATEDB bool interrogate_function_is_destructor(FunctionIndex function);
 
 // This returns the module name reported for the function, if available.
 EXPCL_INTERROGATEDB bool interrogate_function_has_module_name(FunctionIndex function);
@@ -299,6 +303,7 @@ EXPCL_INTERROGATEDB TypeIndex interrogate_wrapper_parameter_type(FunctionWrapper
 EXPCL_INTERROGATEDB bool interrogate_wrapper_parameter_has_name(FunctionWrapperIndex wrapper, int n);
 EXPCL_INTERROGATEDB const char *interrogate_wrapper_parameter_name(FunctionWrapperIndex wrapper, int n);
 EXPCL_INTERROGATEDB bool interrogate_wrapper_parameter_is_this(FunctionWrapperIndex wrapper, int n);
+EXPCL_INTERROGATEDB bool interrogate_wrapper_parameter_is_optional(FunctionWrapperIndex wrapper, int n);
 
 // This returns a pointer to a function that may be called to invoke the
 // function, if the -fptrs option to return function pointers was specified to
@@ -345,7 +350,8 @@ EXPCL_INTERROGATEDB const char *interrogate_make_seq_comment(ElementIndex elemen
 EXPCL_INTERROGATEDB const char *interrogate_make_seq_num_name(MakeSeqIndex make_seq);
 // The name of the real method that returns the nth element, e.g.  "get_thing"
 EXPCL_INTERROGATEDB const char *interrogate_make_seq_element_name(MakeSeqIndex make_seq);
-
+EXPCL_INTERROGATEDB FunctionIndex interrogate_make_seq_num_getter(MakeSeqIndex make_seq);
+EXPCL_INTERROGATEDB FunctionIndex interrogate_make_seq_element_getter(MakeSeqIndex make_seq);
 
 // Types
 
@@ -420,9 +426,14 @@ EXPCL_INTERROGATEDB bool interrogate_type_is_const(TypeIndex type);
 EXPCL_INTERROGATEDB bool interrogate_type_is_typedef(TypeIndex type);
 EXPCL_INTERROGATEDB TypeIndex interrogate_type_wrapped_type(TypeIndex type);
 
+// If interrogate_type_is_array() returns true, this is an array type.
+EXPCL_INTERROGATEDB bool interrogate_type_is_array(TypeIndex type);
+EXPCL_INTERROGATEDB int interrogate_type_array_size(TypeIndex type);
+
 // If interrogate_type_is_enum() returns true, this is an enumerated type,
 // which means it may take any one of a number of named integer values.
 EXPCL_INTERROGATEDB bool interrogate_type_is_enum(TypeIndex type);
+EXPCL_INTERROGATEDB bool interrogate_type_is_scoped_enum(TypeIndex type);
 EXPCL_INTERROGATEDB int interrogate_type_number_of_enum_values(TypeIndex type);
 EXPCL_INTERROGATEDB const char *interrogate_type_enum_value_name(TypeIndex type, int n);
 EXPCL_INTERROGATEDB const char *interrogate_type_enum_value_scoped_name(TypeIndex type, int n);
@@ -499,6 +510,7 @@ EXPCL_INTERROGATEDB FunctionIndex interrogate_type_get_cast(TypeIndex type, int
 // list of base classes for this particular type.
 EXPCL_INTERROGATEDB int interrogate_type_number_of_derivations(TypeIndex type);
 EXPCL_INTERROGATEDB TypeIndex interrogate_type_get_derivation(TypeIndex type, int n);
+EXPCL_INTERROGATEDB bool interrogate_type_is_final(TypeIndex type);
 
 // For each base class, we might need to define an explicit upcast or downcast
 // operation to convert the pointer to the derived class to an appropriate

+ 2 - 2
dtool/src/interrogatedb/py_panda.cxx

@@ -744,7 +744,7 @@ PyObject *copy_from_make_copy(PyObject *self, PyObject *noargs) {
   if (callable == nullptr) {
     return nullptr;
   }
-  PyObject *result = _PyObject_CallNoArg(callable);
+  PyObject *result = PyObject_CallNoArgs(callable);
   Py_DECREF(callable);
   return result;
 }
@@ -768,7 +768,7 @@ PyObject *map_deepcopy_to_copy(PyObject *self, PyObject *args) {
   if (callable == nullptr) {
     return nullptr;
   }
-  PyObject *result = _PyObject_CallNoArg(callable);
+  PyObject *result = PyObject_CallNoArgs(callable);
   Py_DECREF(callable);
   return result;
 }

+ 6 - 0
dtool/src/prc/CMakeLists.txt

@@ -94,6 +94,12 @@ if(ANDROID)
   target_link_libraries(p3prc log)
 endif()
 
+if(HAVE_OPENSSL AND CMAKE_CXX_COMPILER_ID MATCHES "^(GNU|Clang)$")
+  # Do not re-export symbols from these libraries.
+  target_link_options(p3prc PRIVATE "LINKER:--exclude-libs,libssl.a")
+  target_link_options(p3prc PRIVATE "LINKER:--exclude-libs,libcrypto.a")
+endif()
+
 install(TARGETS p3prc
   EXPORT Core COMPONENT Core
   DESTINATION ${CMAKE_INSTALL_LIBDIR}

+ 1 - 0
dtool/src/prc/configFlags.h

@@ -17,6 +17,7 @@
 #include "dtoolbase.h"
 #include "numeric_types.h"
 #include "atomicAdjust.h"
+#include "memoryBase.h"
 
 /**
  * This class is the base class of both ConfigVariable and ConfigVariableCore.

+ 2 - 1
dtool/src/prc/configVariableBase.h

@@ -21,6 +21,7 @@
 #include "configVariableManager.h"
 #include "vector_string.h"
 #include "pset.h"
+#include "memoryBase.h"
 
 // Use this macro to wrap around a description passed to a ConfigVariable
 // constructor.  This allows the description to be completely compiled out, so
@@ -42,7 +43,7 @@
  * and/or ConfigDeclaration, more or less duplicating the interface presented
  * there.
  */
-class EXPCL_DTOOL_PRC ConfigVariableBase : public ConfigFlags {
+class EXPCL_DTOOL_PRC ConfigVariableBase : public ConfigFlags, public MemoryBase {
 protected:
   INLINE ConfigVariableBase(const std::string &name, ValueType type);
   ConfigVariableBase(const std::string &name, ValueType type,

+ 2 - 1
dtool/src/prc/configVariableCore.h

@@ -18,6 +18,7 @@
 #include "configFlags.h"
 #include "configPageManager.h"
 #include "pnotify.h"
+#include "memoryBase.h"
 
 #include <vector>
 
@@ -31,7 +32,7 @@ class ConfigDeclaration;
  * make() method, which may return a shared instance.  Once created, these
  * objects are never destructed.
  */
-class EXPCL_DTOOL_PRC ConfigVariableCore : public ConfigFlags {
+class EXPCL_DTOOL_PRC ConfigVariableCore : public ConfigFlags, public MemoryBase {
 private:
   ConfigVariableCore(const std::string &name);
   ConfigVariableCore(const ConfigVariableCore &templ, const std::string &name);

+ 1 - 1
dtool/src/prc/notifyCategory.I

@@ -49,7 +49,7 @@ set_severity(NotifySeverity severity) {
   _severity = severity;
 #else
   // enforce the no-debug, no-spam rule.
-  _severity = std::max(severity, NS_info);
+  _severity = (std::max)(severity, NS_info);
 #endif
   invalidate_cache();
 }

+ 47 - 10
makepanda/makepackage.py

@@ -189,31 +189,65 @@ def MakeInstallerNSIS(version, file, title, installdir, compressor="lzma", **kwa
     oscmd(cmd)
 
 
-def MakeDebugSymbolArchive(zipname, dirname):
-    outputdir = GetOutputDir()
-
+def MakeDebugSymbolZipArchive(zipname):
     import zipfile
 
-    zip = zipfile.ZipFile(zipname, 'w', zipfile.ZIP_DEFLATED)
+    outputdir = GetOutputDir()
+    zip = zipfile.ZipFile(zipname + '.zip', 'w', zipfile.ZIP_DEFLATED)
 
     for fn in glob.glob(os.path.join(outputdir, 'bin', '*.pdb')):
-        zip.write(fn, dirname + '/bin/' + os.path.basename(fn))
+        zip.write(fn, 'bin/' + os.path.basename(fn))
 
     for fn in glob.glob(os.path.join(outputdir, 'panda3d', '*.pdb')):
-        zip.write(fn, dirname + '/panda3d/' + os.path.basename(fn))
+        zip.write(fn, 'panda3d/' + os.path.basename(fn))
 
     for fn in glob.glob(os.path.join(outputdir, 'plugins', '*.pdb')):
-        zip.write(fn, dirname + '/plugins/' + os.path.basename(fn))
+        zip.write(fn, 'plugins/' + os.path.basename(fn))
 
     for fn in glob.glob(os.path.join(outputdir, 'python', '*.pdb')):
-        zip.write(fn, dirname + '/python/' + os.path.basename(fn))
+        zip.write(fn, 'python/' + os.path.basename(fn))
 
     for fn in glob.glob(os.path.join(outputdir, 'python', 'DLLs', '*.pdb')):
-        zip.write(fn, dirname + '/python/DLLs/' + os.path.basename(fn))
+        zip.write(fn, 'python/DLLs/' + os.path.basename(fn))
 
     zip.close()
 
 
+def MakeDebugSymbolSevenZipArchive(zipname, compressor):
+    zipname += '.7z'
+    flags = ['-t7z', '-y']
+
+    if compressor == 'zlib':
+        # This will still build an LZMA2 archive by default,
+        # but will complete significantly faster.
+        flags.extend(['-mx=3'])
+
+    # Remove the old archive before proceeding.
+    if os.path.exists(zipname):
+        os.remove(zipname)
+
+    outputdir = GetOutputDir()
+
+    # We'll be creating the archive inside the output
+    # directory, so we need the relative path to the archive
+    zipname = os.path.relpath(zipname, outputdir)
+
+    # Create a 7-zip archive, including all *.pdb files
+    # that are not in the tmp folder
+    cmd = [GetSevenZip(), 'a']
+    cmd.extend(flags)
+    cmd.extend(['-ir!*.pdb', '-x!' + os.path.join('tmp', '*'), zipname])
+
+    subprocess.call(cmd, stdout=subprocess.DEVNULL, cwd=outputdir)
+
+
+def MakeDebugSymbolArchive(zipname, compressor):
+    if HasSevenZip():
+        MakeDebugSymbolSevenZipArchive(zipname, compressor)
+    else:
+        MakeDebugSymbolZipArchive(zipname)
+
+
 def MakeInstallerLinux(version, debversion=None, rpmversion=None, rpmrelease=1,
                        python_versions=[], **kwargs):
     outputdir = GetOutputDir()
@@ -966,6 +1000,7 @@ def MakeInstallerAndroid(version, **kwargs):
 
 def MakeInstaller(version, **kwargs):
     target = GetTarget()
+
     if target == 'windows':
         dir = kwargs.pop('installdir', None)
         if dir is None:
@@ -988,8 +1023,10 @@ def MakeInstaller(version, **kwargs):
         if GetTargetArch() == 'x64':
             fn += '-x64'
 
+        compressor = kwargs.get('compressor')
+
         MakeInstallerNSIS(version, fn + '.exe', title, dir, **kwargs)
-        MakeDebugSymbolArchive(fn + '-pdb.zip', dir)
+        MakeDebugSymbolArchive(fn + '-pdb', compressor)
     elif target == 'linux':
         MakeInstallerLinux(version, **kwargs)
     elif target == 'darwin':

+ 80 - 30
makepanda/makepanda.py

@@ -652,9 +652,14 @@ if (COMPILER == "MSVC"):
         if os.path.isfile(GetThirdpartyDir() + "openssl/lib/libpandassl.lib"):
             LibName("OPENSSL", GetThirdpartyDir() + "openssl/lib/libpandassl.lib")
             LibName("OPENSSL", GetThirdpartyDir() + "openssl/lib/libpandaeay.lib")
-        else:
+        elif os.path.isfile(GetThirdpartyDir() + "openssl/lib/ssleay32.lib"):
             LibName("OPENSSL", GetThirdpartyDir() + "openssl/lib/libeay32.lib")
             LibName("OPENSSL", GetThirdpartyDir() + "openssl/lib/ssleay32.lib")
+        else:
+            LibName("OPENSSL", GetThirdpartyDir() + "openssl/lib/libssl.lib")
+            LibName("OPENSSL", GetThirdpartyDir() + "openssl/lib/libcrypto.lib")
+            LibName("OPENSSL", "crypt32.lib")
+            LibName("OPENSSL", "ws2_32.lib")
     if (PkgSkip("PNG")==0):
         if os.path.isfile(GetThirdpartyDir() + "png/lib/libpng16_static.lib"):
             LibName("PNG", GetThirdpartyDir() + "png/lib/libpng16_static.lib")
@@ -666,23 +671,39 @@ if (COMPILER == "MSVC"):
         else:
             LibName("TIFF", GetThirdpartyDir() + "tiff/lib/tiff.lib")
     if (PkgSkip("OPENEXR")==0):
-        suffix = ""
-        if os.path.isfile(GetThirdpartyDir() + "openexr/lib/IlmImf-2_2.lib"):
-            suffix = "-2_2"
-        elif os.path.isfile(GetThirdpartyDir() + "openexr/lib/IlmImf-2_3.lib"):
-            suffix = "-2_3"
-        elif os.path.isfile(GetThirdpartyDir() + "openexr/lib/IlmImf-2_4.lib"):
-            suffix = "-2_4"
-            LibName("OPENEXR", GetThirdpartyDir() + "openexr/lib/Imath" + suffix + ".lib")
-        if os.path.isfile(GetThirdpartyDir() + "openexr/lib/IlmImf" + suffix + "_s.lib"):
-            suffix += "_s"  # _s suffix observed for OpenEXR 2.3 only so far
-        LibName("OPENEXR", GetThirdpartyDir() + "openexr/lib/IlmImf" + suffix + ".lib")
-        LibName("OPENEXR", GetThirdpartyDir() + "openexr/lib/IlmThread" + suffix + ".lib")
-        LibName("OPENEXR", GetThirdpartyDir() + "openexr/lib/Iex" + suffix + ".lib")
-        if suffix == "-2_2":
-            LibName("OPENEXR", GetThirdpartyDir() + "openexr/lib/Half.lib")
+        if os.path.isfile(GetThirdpartyDir() + "openexr/lib/OpenEXRCore-3_1.lib"):
+            LibName("OPENEXR", GetThirdpartyDir() + "openexr/lib/OpenEXR-3_1.lib")
+            LibName("OPENEXR", GetThirdpartyDir() + "openexr/lib/IlmThread-3_1.lib")
+            LibName("OPENEXR", GetThirdpartyDir() + "openexr/lib/Imath-3_1.lib")
+            LibName("OPENEXR", GetThirdpartyDir() + "openexr/lib/Iex-3_1.lib")
+        elif os.path.isfile(GetThirdpartyDir() + "openexr/lib/OpenEXR-3_0.lib"):
+            LibName("OPENEXR", GetThirdpartyDir() + "openexr/lib/OpenEXR-3_0.lib")
+            LibName("OPENEXR", GetThirdpartyDir() + "openexr/lib/IlmThread-3_0.lib")
+            LibName("OPENEXR", GetThirdpartyDir() + "openexr/lib/Imath-3_0.lib")
+            LibName("OPENEXR", GetThirdpartyDir() + "openexr/lib/Iex-3_0.lib")
+        elif os.path.isfile(GetThirdpartyDir() + "openexr/lib/OpenEXR.lib"):
+            LibName("OPENEXR", GetThirdpartyDir() + "openexr/lib/OpenEXR.lib")
+            LibName("OPENEXR", GetThirdpartyDir() + "openexr/lib/IlmThread.lib")
+            LibName("OPENEXR", GetThirdpartyDir() + "openexr/lib/Imath.lib")
+            LibName("OPENEXR", GetThirdpartyDir() + "openexr/lib/Iex.lib")
         else:
-            LibName("OPENEXR", GetThirdpartyDir() + "openexr/lib/Half" + suffix + ".lib")
+            suffix = ""
+            if os.path.isfile(GetThirdpartyDir() + "openexr/lib/IlmImf-2_2.lib"):
+                suffix = "-2_2"
+            elif os.path.isfile(GetThirdpartyDir() + "openexr/lib/IlmImf-2_3.lib"):
+                suffix = "-2_3"
+            elif os.path.isfile(GetThirdpartyDir() + "openexr/lib/IlmImf-2_4.lib"):
+                suffix = "-2_4"
+                LibName("OPENEXR", GetThirdpartyDir() + "openexr/lib/Imath" + suffix + ".lib")
+            if os.path.isfile(GetThirdpartyDir() + "openexr/lib/IlmImf" + suffix + "_s.lib"):
+                suffix += "_s"  # _s suffix observed for OpenEXR 2.3 only so far
+            LibName("OPENEXR", GetThirdpartyDir() + "openexr/lib/IlmImf" + suffix + ".lib")
+            LibName("OPENEXR", GetThirdpartyDir() + "openexr/lib/IlmThread" + suffix + ".lib")
+            LibName("OPENEXR", GetThirdpartyDir() + "openexr/lib/Iex" + suffix + ".lib")
+            if suffix == "-2_2":
+                LibName("OPENEXR", GetThirdpartyDir() + "openexr/lib/Half.lib")
+            else:
+                LibName("OPENEXR", GetThirdpartyDir() + "openexr/lib/Half" + suffix + ".lib")
         IncDirectory("OPENEXR", GetThirdpartyDir() + "openexr/include/OpenEXR")
         IncDirectory("OPENEXR", GetThirdpartyDir() + "openexr/include/Imath")
     if (PkgSkip("JPEG")==0):     LibName("JPEG",     GetThirdpartyDir() + "jpeg/lib/jpeg-static.lib")
@@ -876,7 +897,6 @@ if (COMPILER=="GCC"):
     SmartPkgEnable("OPENAL",    "openal",    ("openal"), "AL/al.h", framework = "OpenAL")
     SmartPkgEnable("SQUISH",    "",          ("squish"), "squish.h")
     SmartPkgEnable("TIFF",      "libtiff-4", ("tiff"), "tiff.h")
-    SmartPkgEnable("OPENEXR",   "OpenEXR",   ("IlmImf", "Imath", "Half", "Iex", "IexMath", "IlmThread"), ("OpenEXR", "Imath", "OpenEXR/ImfOutputFile.h"))
     SmartPkgEnable("VRPN",      "",          ("vrpn", "quat"), ("vrpn", "quat.h", "vrpn/vrpn_Types.h"))
     SmartPkgEnable("BULLET", "bullet", ("BulletSoftBody", "BulletDynamics", "BulletCollision", "LinearMath"), ("bullet", "bullet/btBulletDynamicsCommon.h"))
     SmartPkgEnable("VORBIS",    "vorbisfile",("vorbisfile", "vorbis", "ogg"), ("ogg/ogg.h", "vorbis/vorbisfile.h"))
@@ -906,6 +926,23 @@ if (COMPILER=="GCC"):
             for ffmpeg_lib in ffmpeg_libs:
                 LibName("FFMPEG", "-Wl,--exclude-libs,%s.a" % (ffmpeg_lib))
 
+    if not PkgSkip("OPENEXR"):
+        # OpenEXR libraries have different names depending on the version.
+        openexr_libdir = os.path.join(GetThirdpartyDir(), "openexr", "lib")
+        openexr_incs = ("OpenEXR", "Imath", "OpenEXR/ImfOutputFile.h")
+        if os.path.isfile(os.path.join(openexr_libdir, "libOpenEXR-3_1.a")):
+            SmartPkgEnable("OPENEXR", "", ("OpenEXR-3_1", "IlmThread-3_1", "Imath-3_1", "Iex-3_1"), openexr_incs)
+        if os.path.isfile(os.path.join(openexr_libdir, "libOpenEXR-3_0.a")):
+            SmartPkgEnable("OPENEXR", "", ("OpenEXR-3_0", "IlmThread-3_0", "Imath-3_0", "Iex-3_0"), openexr_incs)
+        elif os.path.isfile(os.path.join(openexr_libdir, "libOpenEXR.a")):
+            SmartPkgEnable("OPENEXR", "", ("OpenEXR", "IlmThread", "Imath", "Iex"), openexr_incs)
+        elif os.path.isfile(os.path.join(openexr_libdir, "libIlmImf.a")):
+            SmartPkgEnable("OPENEXR", "", ("IlmImf", "Imath", "Half", "Iex", "IexMath", "IlmThread"), openexr_incs)
+        else:
+            # Find it in the system, preferably using pkg-config, otherwise
+            # using the OpenEXR 3 naming scheme.
+            SmartPkgEnable("OPENEXR", "OpenEXR", ("OpenEXR", "IlmThread", "Imath", "Iex"), openexr_incs)
+
     if GetTarget() != "darwin":
         for fcollada_lib in fcollada_libs:
             LibName("FCOLLADA", "-Wl,--exclude-libs,lib%s.a" % (fcollada_lib))
@@ -937,6 +974,9 @@ if (COMPILER=="GCC"):
             LibName("OPENEXR", "-Wl,--exclude-libs,libIlmImfUtil.a")
             LibName("OPENEXR", "-Wl,--exclude-libs,libIlmThread.a")
             LibName("OPENEXR", "-Wl,--exclude-libs,libImath.a")
+            LibName("OPENEXR", "-Wl,--exclude-libs,libOpenEXR.a")
+            LibName("OPENEXR", "-Wl,--exclude-libs,libOpenEXRCore.a")
+            LibName("OPENEXR", "-Wl,--exclude-libs,libOpenEXRUtil.a")
 
         if not PkgSkip("VORBIS"):
             LibName("VORBIS", "-Wl,--exclude-libs,libogg.a")
@@ -1074,8 +1114,6 @@ if (COMPILER=="GCC"):
             LibName("FFMPEG", "-undefined dynamic_lookup")
         if not PkgSkip("ASSIMP"):
             LibName("ASSIMP", "-undefined dynamic_lookup")
-        if not PkgSkip("OPENEXR"):
-            LibName("OPENEXR", "-undefined dynamic_lookup")
         if not PkgSkip("VRPN"):
             LibName("VRPN", "-undefined dynamic_lookup")
 
@@ -1216,7 +1254,7 @@ def CompileCxx(obj,src,opts):
             cmd = "cl "
             if GetTargetArch() == 'x64':
                 cmd += "/favor:blend "
-            cmd += "/wd4996 /wd4275 /wd4273 "
+            cmd += "/wd4996 "
 
             # Set the minimum version to Windows Vista.
             cmd += "/DWINVER=0x600 "
@@ -1248,7 +1286,7 @@ def CompileCxx(obj,src,opts):
             if (building):
                 cmd += " /DBUILDING_" + building
 
-            if ("BIGOBJ" in opts) or GetTargetArch() == 'x64':
+            if ("BIGOBJ" in opts) or GetTargetArch() == 'x64' or not PkgSkip("EIGEN"):
                 cmd += " /bigobj"
 
             cmd += " /Zm300"
@@ -1266,7 +1304,7 @@ def CompileCxx(obj,src,opts):
             cmd = "icl "
             if GetTargetArch() == 'x64':
                 cmd += "/favor:blend "
-            cmd += "/wd4996 /wd4275 /wd4267 /wd4101 /wd4273 "
+            cmd += "/wd4996 /wd4267 /wd4101 "
             cmd += "/DWINVER=0x600 "
             cmd += "/Fo" + obj + " /c"
             for x in ipath: cmd += " /I" + x
@@ -1377,6 +1415,10 @@ def CompileCxx(obj,src,opts):
                 if 'NOARCH:' + arch.upper() not in opts:
                     cmd += " -arch %s" % arch
 
+        elif 'clang' not in GetCXX().split('/')[-1]:
+            # Enable interprocedural optimizations in GCC.
+            cmd += " -fno-semantic-interposition"
+
         if "SYSROOT" in SDK:
             if GetTarget() != "android":
                 cmd += ' --sysroot=%s' % (SDK["SYSROOT"])
@@ -1463,10 +1505,7 @@ def CompileCxx(obj,src,opts):
         if (optlevel==4): cmd += " -O3 -DNDEBUG"
 
         # Enable more warnings.
-        cmd += " -Wall -Wno-unused-function"
-
-        if not src.endswith(".c"):
-            cmd += " -Wno-reorder"
+        cmd += " -Wall -Wno-unused-function -Werror=return-type"
 
         # Ignore unused variables in NDEBUG builds, often used in asserts.
         if optlevel == 4:
@@ -5036,8 +5075,10 @@ if GetTarget() == 'android' and not PkgSkip("EGL") and not PkgSkip("GLES2"):
 # DIRECTORY: panda/src/tinydisplay/
 #
 
-if (GetTarget() in ('windows', 'darwin') or not PkgSkip("X11")) and not PkgSkip("TINYDISPLAY"):
+if not PkgSkip("TINYDISPLAY"):
     OPTS=['DIR:panda/src/tinydisplay', 'BUILDING:TINYDISPLAY', 'X11']
+    if not PkgSkip("X11"):
+        OPTS += ['X11']
     TargetAdd('p3tinydisplay_composite1.obj', opts=OPTS, input='p3tinydisplay_composite1.cxx')
     TargetAdd('p3tinydisplay_composite2.obj', opts=OPTS, input='p3tinydisplay_composite2.cxx')
     TargetAdd('p3tinydisplay_ztriangle_1.obj', opts=OPTS, input='ztriangle_1.cxx')
@@ -5048,7 +5089,7 @@ if (GetTarget() in ('windows', 'darwin') or not PkgSkip("X11")) and not PkgSkip(
     if GetTarget() == 'windows':
         TargetAdd('libp3tinydisplay.dll', input='libp3windisplay.dll')
         TargetAdd('libp3tinydisplay.dll', opts=['WINIMM', 'WINGDI', 'WINKERNEL', 'WINOLDNAMES', 'WINUSER', 'WINMM'])
-    elif GetTarget() != 'darwin':
+    elif GetTarget() != 'darwin' and not PkgSkip("X11"):
         TargetAdd('libp3tinydisplay.dll', input='p3x11display_composite1.obj')
         TargetAdd('libp3tinydisplay.dll', opts=['X11'])
     TargetAdd('libp3tinydisplay.dll', input='p3tinydisplay_composite1.obj')
@@ -6057,17 +6098,21 @@ for VER in MAYAVERSIONS:
     TargetAdd('egg2maya'+VNUM+'.exe', opts=['ADVAPI']+ARCH_OPTS)
 
 if MAYA_BUILT:
+    OPTS=['DIR:pandatool/src/mayaprogs', 'DIR:pandatool/src/maya', 'DIR:pandatool/src/mayaegg', 'BUILDING:MISC', 'NOARCH:ARM64']
+
     TargetAdd('mayaprogs_mayaConversionClient.obj', opts=OPTS, input='mayaConversionClient.cxx')
 
     TargetAdd('maya2egg_mayaToEggClient.obj', opts=OPTS, input='mayaToEggClient.cxx')
     TargetAdd('maya2egg_client.exe', input='mayaprogs_mayaConversionClient.obj')
     TargetAdd('maya2egg_client.exe', input='maya2egg_mayaToEggClient.obj')
     TargetAdd('maya2egg_client.exe', input=COMMON_EGG2X_LIBS)
+    TargetAdd('maya2egg_client.exe', opts=['NOARCH:ARM64'])
 
     TargetAdd('egg2maya_eggToMayaClient.obj', opts=OPTS, input='eggToMayaClient.cxx')
     TargetAdd('egg2maya_client.exe', input='mayaprogs_mayaConversionClient.obj')
     TargetAdd('egg2maya_client.exe', input='egg2maya_eggToMayaClient.obj')
     TargetAdd('egg2maya_client.exe', input=COMMON_EGG2X_LIBS)
+    TargetAdd('egg2maya_client.exe', opts=['NOARCH:ARM64'])
 
 #
 # DIRECTORY: contrib/src/ai/
@@ -6125,7 +6170,7 @@ if PkgSkip("PYTHON") == 0:
 
     if GetTarget() == 'linux' or GetTarget() == 'freebsd':
         # Setup rpath so libs can be found in the same directory as the deployed game
-        LibName('DEPLOYSTUB', "-Wl,-rpath,\\$ORIGIN")
+        LibName('DEPLOYSTUB', "-Wl,--disable-new-dtags,-rpath,\\$ORIGIN")
         LibName('DEPLOYSTUB', "-Wl,-z,origin")
         LibName('DEPLOYSTUB', "-rdynamic")
 
@@ -6145,6 +6190,9 @@ if PkgSkip("PYTHON") == 0:
         PyTargetAdd('deploy-stubw.exe', input='deploy-stubw.obj')
         PyTargetAdd('deploy-stubw.exe', opts=['MACOS_APP_BUNDLE', 'DEPLOYSTUB', 'NOICON'])
     elif GetTarget() == 'android':
+        TargetAdd('org/jnius/NativeInvocationHandler.class', opts=OPTS, input='NativeInvocationHandler.java')
+        TargetAdd('classes.dex', input='org/jnius/NativeInvocationHandler.class')
+
         PyTargetAdd('deploy-stubw_android_main.obj', opts=OPTS, input='android_main.cxx')
         PyTargetAdd('deploy-stubw_android_log.obj', opts=OPTS, input='android_log.c')
         PyTargetAdd('libdeploy-stubw.dll', input='android_native_app_glue.obj')
@@ -6273,6 +6321,8 @@ def ParallelMake(tasklist):
             tasklist = extras
         sys.stdout.flush()
         if tasksqueued == 0:
+            if len(tasklist) > 0:
+                continue
             break
         donetask = donequeue.get()
         if donetask == 0:

+ 34 - 3
makepanda/makepandacore.py

@@ -572,6 +572,26 @@ def GetFlexVersion():
         Warn("Unable to detect flex version")
         return (0, 0, 0)
 
+SEVENZIP = None
+def GetSevenZip():
+    global SEVENZIP
+    if SEVENZIP is not None:
+        return SEVENZIP
+
+    win_util = os.path.join(GetThirdpartyBase(), 'win-util')
+    if GetHost() == 'windows' and os.path.isdir(win_util):
+        SEVENZIP = GetThirdpartyBase() + "/win-util/7za.exe"
+    elif LocateBinary('7z'):
+        SEVENZIP = '7z'
+    else:
+        # We don't strictly need it, so don't give an error
+        return None
+
+    return SEVENZIP
+
+def HasSevenZip():
+    return GetSevenZip() is not None
+
 ########################################################################
 ##
 ## LocateBinary
@@ -635,6 +655,9 @@ def oscmd(cmd, ignoreError = False, cwd=None):
 
         res = os.spawnl(os.P_WAIT, exe_path, cmd)
 
+        if res == -1073741510: # 0xc000013a
+            exit("keyboard interrupt")
+
         if cwd is not None:
             os.chdir(pwd)
     else:
@@ -854,8 +877,11 @@ def JavaGetImports(path):
     imports = []
     try:
         for match in JavaImportRegex.finditer(source, 0):
-            impname = match.group(1)
-            imports.append(impname.strip())
+            impname = match.group(1).strip()
+            if not impname.startswith('java.') and \
+               not impname.startswith('dalvik.') and \
+               not impname.startswith('android.'):
+                imports.append(impname.strip())
     except:
         print("Failed to determine dependencies of \"" + path  +"\".")
         raise
@@ -3300,7 +3326,12 @@ def SetOrigExt(x, v):
     ORIG_EXT[x] = v
 
 def GetExtensionSuffix():
-    if CrossCompiling():
+    if GetTarget() == 'windows':
+        if GetTargetArch() == 'x64':
+            return '.cp%d%d-win_amd64.pyd' % (sys.version_info[:2])
+        else:
+            return '.cp%d%d-win32.pyd' % (sys.version_info[:2])
+    elif CrossCompiling():
         return '.{0}.so'.format(GetPythonABI())
     else:
         import _imp

+ 15 - 0
makepanda/makewheel.py

@@ -800,6 +800,21 @@ if __debug__:
 
             whl.write_file(target_path, source_path)
 
+    # Include the special sysconfigdata module.
+    if os.name == 'posix':
+        import sysconfig
+
+        if hasattr(sysconfig, '_get_sysconfigdata_name'):
+            modname = sysconfig._get_sysconfigdata_name() + '.py'
+        else:
+            modname = '_sysconfigdata.py'
+
+        for entry in sys.path:
+            source_path = os.path.join(entry, modname)
+            if os.path.isfile(source_path):
+                whl.write_file('deploy_libs/' + modname, source_path)
+                break
+
     # Add plug-ins.
     for lib in PLUGIN_LIBS:
         plugin_name = 'lib' + lib

+ 8 - 2
panda/src/android/PythonActivity.java

@@ -16,8 +16,14 @@ package org.panda3d.android;
 import org.panda3d.android.PandaActivity;
 
 /**
- * This is only declared as a separate class from PandaActivity so that we
- * can have two separate activity definitions in ApplicationManifest.xml.
+ * Extends PandaActivity with some things that are useful in a Python
+ * application.
  */
 public class PythonActivity extends PandaActivity {
+    // This is required by plyer.
+    public static PythonActivity mActivity;
+
+    public PythonActivity() {
+        mActivity = this;
+    }
 }

+ 2 - 3
panda/src/audio/audioLoadRequest.I

@@ -59,7 +59,7 @@ get_positional() const {
  */
 INLINE bool AudioLoadRequest::
 is_ready() const {
-  return (FutureState)AtomicAdjust::get(_future_state) == FS_finished;
+  return (FutureState)_future_state.load(std::memory_order_relaxed) == FS_finished;
 }
 
 /**
@@ -70,6 +70,5 @@ is_ready() const {
  */
 INLINE AudioSound *AudioLoadRequest::
 get_sound() const {
-  nassertr_always(done(), nullptr);
-  return (AudioSound *)_result;
+  return (AudioSound *)get_result();
 }

+ 5 - 0
panda/src/audiotraits/CMakeLists.txt

@@ -60,6 +60,11 @@ if(HAVE_OPENAL)
   set_target_properties(p3openal_audio PROPERTIES DEFINE_SYMBOL BUILDING_OPENAL_AUDIO)
   target_link_libraries(p3openal_audio panda PKG::OPENAL)
 
+  if(CMAKE_CXX_COMPILER_ID MATCHES "^(GNU|Clang)$")
+    # When statically linking OpenAL, keep its symbols private to this module.
+    target_link_options(p3openal_audio PRIVATE "LINKER:--exclude-libs,libopenal.a")
+  endif()
+
   install(TARGETS p3openal_audio
     EXPORT OpenAL COMPONENT OpenAL
     DESTINATION ${CMAKE_INSTALL_LIBDIR}

+ 3 - 2
panda/src/bullet/bulletAllHitsRayResult.h

@@ -20,13 +20,14 @@
 #include "bullet_utils.h"
 
 #include "luse.h"
+#include "memoryBase.h"
 #include "pandaNode.h"
 #include "collideMask.h"
 
 /**
  *
  */
-struct EXPCL_PANDABULLET BulletRayHit {
+struct EXPCL_PANDABULLET BulletRayHit : public MemoryBase {
 
 PUBLISHED:
   INLINE static BulletRayHit empty();
@@ -61,7 +62,7 @@ private:
 /**
  *
  */
-struct EXPCL_PANDABULLET BulletAllHitsRayResult : public btCollisionWorld::AllHitsRayResultCallback {
+struct EXPCL_PANDABULLET BulletAllHitsRayResult : public btCollisionWorld::AllHitsRayResultCallback, public MemoryBase {
 
 PUBLISHED:
   INLINE static BulletAllHitsRayResult empty();

+ 2 - 1
panda/src/bullet/bulletClosestHitRayResult.h

@@ -20,13 +20,14 @@
 #include "bullet_utils.h"
 
 #include "luse.h"
+#include "memoryBase.h"
 #include "pandaNode.h"
 #include "collideMask.h"
 
 /**
  *
  */
-struct EXPCL_PANDABULLET BulletClosestHitRayResult : public btCollisionWorld::ClosestRayResultCallback {
+struct EXPCL_PANDABULLET BulletClosestHitRayResult : public btCollisionWorld::ClosestRayResultCallback, public MemoryBase {
 
 PUBLISHED:
   INLINE static BulletClosestHitRayResult empty();

+ 2 - 1
panda/src/bullet/bulletClosestHitSweepResult.h

@@ -20,13 +20,14 @@
 #include "bullet_utils.h"
 
 #include "luse.h"
+#include "memoryBase.h"
 #include "pandaNode.h"
 #include "collideMask.h"
 
 /**
  *
  */
-struct EXPCL_PANDABULLET BulletClosestHitSweepResult : public btCollisionWorld::ClosestConvexResultCallback {
+struct EXPCL_PANDABULLET BulletClosestHitSweepResult : public btCollisionWorld::ClosestConvexResultCallback, public MemoryBase {
 
 PUBLISHED:
   INLINE static BulletClosestHitSweepResult empty();

+ 1 - 1
panda/src/bullet/bulletConvexHullShape.cxx

@@ -194,7 +194,7 @@ make_from_bam(const FactoryParams &params) {
 void BulletConvexHullShape::
 fillin(DatagramIterator &scan, BamReader *manager) {
   BulletShape::fillin(scan, manager);
-  nassertv(_shape == nullptr);
+  nassertv(_shape);
 
   _shape->setMargin(scan.get_stdfloat());
   unsigned int num_points = scan.get_uint32();

+ 2 - 2
panda/src/bullet/bulletConvexPointCloudShape.I

@@ -16,8 +16,8 @@
  */
 INLINE BulletConvexPointCloudShape::
 BulletConvexPointCloudShape() :
-  _scale(1),
-  _shape(nullptr) {
+  _shape(nullptr),
+  _scale(1) {
 }
 
 /**

+ 2 - 2
panda/src/bullet/bulletTriangleMeshShape.cxx

@@ -29,9 +29,9 @@ TypeHandle BulletTriangleMeshShape::_type_handle;
  */
 BulletTriangleMeshShape::
 BulletTriangleMeshShape() :
-  _mesh(nullptr),
-  _gimpact_shape(nullptr),
   _bvh_shape(nullptr),
+  _gimpact_shape(nullptr),
+  _mesh(nullptr),
   _dynamic(false),
   _compress(false),
   _bvh(false) {

+ 2 - 2
panda/src/chan/animControl.cxx

@@ -32,8 +32,8 @@ AnimControl(const std::string &name, PartBundle *part,
   Namable(name),
   _pending_lock(name),
   _pending_cvar(_pending_lock),
-  _bound_joints(BitArray::all_on()),
-  _part(part)
+  _part(part),
+  _bound_joints(BitArray::all_on())
 {
 #ifdef DO_MEMORY_USAGE
   MemoryUsage::update_type(this, get_class_type());

+ 7 - 7
panda/src/char/character.cxx

@@ -38,16 +38,16 @@ PStatCollector Character::_animation_pcollector("*:Animation");
 Character::
 Character(const Character &copy, bool copy_bundles) :
   PartBundleNode(copy),
+  _last_auto_update(-1.0),
+  _view_frame(-1),
+  _view_distance2(0.0f),
   _lod_center(copy._lod_center),
   _lod_far_distance(copy._lod_far_distance),
   _lod_near_distance(copy._lod_near_distance),
   _lod_delay_factor(copy._lod_delay_factor),
   _do_lod_animation(copy._do_lod_animation),
   _joints_pcollector(copy._joints_pcollector),
-  _skinning_pcollector(copy._skinning_pcollector),
-  _last_auto_update(-1.0),
-  _view_frame(-1),
-  _view_distance2(0.0f)
+  _skinning_pcollector(copy._skinning_pcollector)
 {
   set_cull_callback();
 
@@ -75,11 +75,11 @@ Character(const Character &copy, bool copy_bundles) :
 Character::
 Character(const std::string &name) :
   PartBundleNode(name, new CharacterJointBundle(name)),
-  _joints_pcollector(PStatCollector(_animation_pcollector, name), "Joints"),
-  _skinning_pcollector(PStatCollector(_animation_pcollector, name), "Vertices"),
   _last_auto_update(-1.0),
   _view_frame(-1),
-  _view_distance2(0.0f)
+  _view_distance2(0.0f),
+  _joints_pcollector(PStatCollector(_animation_pcollector, name), "Joints"),
+  _skinning_pcollector(PStatCollector(_animation_pcollector, name), "Vertices")
 {
   set_cull_callback();
   clear_lod_animation();

+ 6 - 3
panda/src/cocoadisplay/cocoaGraphicsWindow.mm

@@ -1929,11 +1929,14 @@ handle_wheel_event(double x, double y) {
     _input->button_up(MouseButton::wheel_down());
   }
 
-  // TODO: check if this is correct, I don't own a MacBook
-  if (x > 0.0) {
+  if (x != 0 && cocoa_invert_wheel_x) {
+    x = -x;
+  }
+
+  if (x < 0.0) {
     _input->button_down(MouseButton::wheel_right());
     _input->button_up(MouseButton::wheel_right());
-  } else if (x < 0.0) {
+  } else if (x > 0.0) {
     _input->button_down(MouseButton::wheel_left());
     _input->button_up(MouseButton::wheel_left());
   }

+ 2 - 0
panda/src/cocoadisplay/config_cocoadisplay.h

@@ -20,6 +20,8 @@
 
 NotifyCategoryDecl(cocoadisplay, EXPCL_PANDA_COCOADISPLAY, EXPTP_PANDA_COCOADISPLAY);
 
+extern ConfigVariableBool cocoa_invert_wheel_x;
+
 extern EXPCL_PANDA_COCOADISPLAY void init_libcocoadisplay();
 
 #endif

+ 5 - 0
panda/src/cocoadisplay/config_cocoadisplay.mm

@@ -31,6 +31,11 @@ ConfigureFn(config_cocoadisplay) {
   init_libcocoadisplay();
 }
 
+ConfigVariableBool cocoa_invert_wheel_x
+("cocoa-invert-wheel-x", false,
+ PRC_DESC("Set this to true to swap the wheel_left and wheel_right mouse "
+          "button events, to restore to the pre-1.10.12 behavior."));
+
 /**
  * Initializes the library.  This must be called at least once before any of
  * the functions or classes in this library can be used.  Normally it will be

+ 4 - 36
panda/src/collide/collisionBox.I

@@ -17,11 +17,10 @@
  */
 INLINE CollisionBox::
 CollisionBox(const LPoint3 &center, PN_stdfloat x, PN_stdfloat y, PN_stdfloat z) :
-  _center(center), _x(x), _y(y), _z(z)
+  _center(center)
 {
-  _min = LPoint3(_center.get_x() - _x, _center.get_y() - _y, _center.get_z() - _z);
-  _max = LPoint3(_center.get_x() + _x, _center.get_y() + _y, _center.get_z() + _z);
-  _radius = sqrt(_x*_x + _y*_y + _z*_z);
+  _min = LPoint3(_center.get_x() - x, _center.get_y() - y, _center.get_z() - z);
+  _max = LPoint3(_center.get_x() + x, _center.get_y() + y, _center.get_z() + z);
   for(int v = 0; v < 8; v++)
     _vertex[v] = get_point_aabb(v);
   for(int p = 0; p < 6; p++)
@@ -37,10 +36,6 @@ CollisionBox(const LPoint3 &min, const LPoint3 &max) :
   _min(min), _max(max)
 {
   _center = (_min + _max) / 2;
-  _x = _center.get_x() - _min.get_x();
-  _y = _center.get_y() - _min.get_y();
-  _z = _center.get_z() - _min.get_z();
-  _radius = sqrt(_x*_x + _y*_y + _z*_z);
   for(int v = 0; v < 8; v++)
     _vertex[v] = get_point_aabb(v);
   for(int p = 0; p < 6; p++)
@@ -63,11 +58,7 @@ CollisionBox(const CollisionBox &copy) :
   CollisionSolid(copy),
   _center(copy._center),
   _min(copy._min),
-  _max(copy._max),
-  _x(copy._x ),
-  _y(copy._y ),
-  _z(copy._z ),
-  _radius(copy._radius )
+  _max(copy._max)
 {
   for(int v = 0; v < 8; v++)
     _vertex[v] = copy._vertex[v];
@@ -152,7 +143,6 @@ get_point(int n) const {
   return _vertex[n];
 }
 
-
 /**
  * Returns the nth vertex of the Axis Aligned Bounding Box.
  */
@@ -243,20 +233,6 @@ calc_to_3d_mat(LMatrix4 &to_3d_mat,int plane) const {
   to_3d_mat.set_row(3, get_plane(plane).get_point());
 }
 
-/**
- * Fills the indicated matrix with the appropriate rotation transform to move
- * points from the 2-d plane into the 3-d (X, 0, Z) plane.
- *
- * This is essentially similar to calc_to_3d_mat, except that the matrix is
- * rederived from whatever is stored in _to_2d_mat, guaranteeing that it will
- * match whatever algorithm produced that one, even if it was produced on a
- * different machine with different numerical precision.
- */
-INLINE void CollisionBox::
-rederive_to_3d_mat(LMatrix4 &to_3d_mat, int plane) const {
-  to_3d_mat.invert_from(_to_2d_mat[plane]);
-}
-
 /**
  * Extrude the indicated point in the polygon's 2-d definition space back into
  * 3-d coordinates.
@@ -295,11 +271,3 @@ operator = (const CollisionBox::PointDef &copy) {
   _p = copy._p;
   _v = copy._v;
 }
-
-/**
- * returns the points that form the nth plane
- */
-INLINE CollisionBox::Points CollisionBox::
-get_plane_points(int n) {
-  return _points[n];
-}

+ 54 - 81
panda/src/collide/collisionBox.cxx

@@ -66,14 +66,11 @@ make_copy() {
  * Compute parameters for each of the box's sides
  */
 void CollisionBox::
-setup_box(){
-  for(int plane = 0; plane < 6; plane++) {
-    LPoint3 array[4];
-    array[0] = get_point(plane_def[plane][0]);
-    array[1] = get_point(plane_def[plane][1]);
-    array[2] = get_point(plane_def[plane][2]);
-    array[3] = get_point(plane_def[plane][3]);
-    setup_points(array, array+4, plane);
+setup_box() {
+  assert(sizeof(_points) / sizeof(_points[0]) == 6);
+  assert(sizeof(_points[0]) / sizeof(_points[0][0]) == 4);
+  for (int plane = 0; plane < 6; plane++) {
+    setup_points(plane);
   }
 }
 
@@ -81,11 +78,8 @@ setup_box(){
  * Computes the plane and 2d projection of points that make up this side.
  */
 void CollisionBox::
-setup_points(const LPoint3 *begin, const LPoint3 *end, int plane) {
-  int num_points = end - begin;
-  nassertv(num_points >= 3);
-
-  _points[plane].clear();
+setup_points(int plane) {
+  PointDef *points = _points[plane];
 
   // Construct a matrix that rotates the points from the (X,0,Z) plane into
   // the 3-d plane.
@@ -96,32 +90,15 @@ setup_points(const LPoint3 *begin, const LPoint3 *end, int plane) {
   _to_2d_mat[plane].invert_from(to_3d_mat);
 
   // Now project all of the points onto the 2-d plane.
-
-  const LPoint3 *pi;
-  for (pi = begin; pi != end; ++pi) {
-    LPoint3 point = (*pi) * _to_2d_mat[plane];
-    _points[plane].push_back(PointDef(point[0], point[2]));
+  for (size_t i = 0; i < 4; ++i) {
+    LPoint3 point = get_point(plane_def[plane][i]) * _to_2d_mat[plane];
+    points[i] = PointDef(point[0], point[2]);
   }
 
-  nassertv(_points[plane].size() >= 3);
-
-#ifndef NDEBUG
-  /*
-  // Now make sure the points define a convex polygon.
-  if (is_concave()) {
-  collide_cat.error() << "Invalid concave CollisionPolygon defined:\n";
-  const LPoint3 *pi;
-  for (pi = begin; pi != end; ++pi) {
-  collide_cat.error(false) << "  " << (*pi) << "\n";
-  }
-  collide_cat.error(false)
-  << "  normal " << normal << " with length " << normal.length() << "\n";
-  _points.clear();
-  }
-  */
-#endif
-
-  compute_vectors(_points[plane]);
+  for (size_t i = 0; i < 4; i++) {
+    points[i]._v = points[(i + 1) % 4]._p - points[i]._p;
+    points[i]._v.normalize();
+  }
 }
 
 /**
@@ -146,10 +123,6 @@ xform(const LMatrix4 &mat) {
   for(int p = 0; p < 6 ; p++) {
     _planes[p] = set_plane(p);
   }
-  _x = _vertex[0].get_x() - _center.get_x();
-  _y = _vertex[0].get_y() - _center.get_y();
-  _z = _vertex[0].get_z() - _center.get_z();
-  _radius = sqrt(_x * _x + _y * _y + _z * _z);
   setup_box();
   mark_viz_stale();
   mark_internal_bounds_stale();
@@ -196,7 +169,11 @@ output(std::ostream &out) const {
  */
 PT(BoundingVolume) CollisionBox::
 compute_internal_bounds() const {
-  return new BoundingSphere(_center, _radius);
+  PN_stdfloat x = _vertex[0].get_x() - _center.get_x();
+  PN_stdfloat y = _vertex[0].get_y() - _center.get_y();
+  PN_stdfloat z = _vertex[0].get_z() - _center.get_z();
+  PN_stdfloat radius = sqrt(x * x + y * y + z * z);
+  return new BoundingSphere(_center, radius);
 }
 
 /**
@@ -233,10 +210,8 @@ test_intersection_from_sphere(const CollisionEntry &entry) const {
   LVector3 normal;
 
   for(ip = 0, intersect = false; ip < 6 && !intersect; ip++) {
-    plane = get_plane( ip );
-    if (_points[ip].size() < 3) {
-      continue;
-    }
+    plane = get_plane(ip);
+
     if (wrt_prev_space != wrt_space) {
       // If we have a delta between the previous position and the current
       // position, we use that to determine some more properties of the
@@ -322,17 +297,17 @@ test_intersection_from_sphere(const CollisionEntry &entry) const {
       Points new_points;
       if (apply_clip_plane(new_points, cpa, entry.get_into_node_path().get_net_transform(),ip)) {
         // All points are behind the clip plane; just do the default test.
-        edge_dist = dist_to_polygon(p, _points[ip]);
+        edge_dist = dist_to_polygon(p, _points[ip], 4);
       } else if (new_points.empty()) {
         // The polygon is completely clipped.
         continue;
       } else {
         // Test against the clipped polygon.
-        edge_dist = dist_to_polygon(p, new_points);
+        edge_dist = dist_to_polygon(p, new_points.data(), new_points.size());
       }
     } else {
       // No clip plane is in effect.  Do the default test.
-      edge_dist = dist_to_polygon(p, _points[ip]);
+      edge_dist = dist_to_polygon(p, _points[ip], 4);
     }
 
     max_dist = from_radius;
@@ -506,7 +481,7 @@ test_intersection_from_parabola(const CollisionEntry &entry) const {
   LParabola local_p(parabola->get_parabola());
   local_p.xform(wrt_mat);
 
-  PN_stdfloat t = INT_MAX;
+  PN_stdfloat t = FLT_MAX;
   PN_stdfloat t1, t2;
   int intersecting_face = -1;
   for (int i = 0; i < get_num_planes(); i++) {
@@ -1129,13 +1104,13 @@ apply_clip_plane(CollisionBox::Points &new_points,
       LPlane plane = plane_node->get_plane() * new_transform->get_mat();
       if (first_plane) {
         first_plane = false;
-        if (!clip_polygon(new_points, _points[plane_no], plane, plane_no)) {
+        if (!clip_polygon(new_points, _points[plane_no], 4, plane, plane_no)) {
           all_in = false;
         }
       } else {
         Points last_points;
         last_points.swap(new_points);
-        if (!clip_polygon(new_points, last_points, plane, plane_no)) {
+        if (!clip_polygon(new_points, last_points.data(), last_points.size(), plane, plane_no)) {
           all_in = false;
         }
       }
@@ -1158,10 +1133,10 @@ apply_clip_plane(CollisionBox::Points &new_points,
  */
 bool CollisionBox::
 clip_polygon(CollisionBox::Points &new_points,
-             const CollisionBox::Points &source_points,
+             const PointDef *source_points, size_t num_source_points,
              const LPlane &plane, int plane_no) const {
   new_points.clear();
-  if (source_points.empty()) {
+  if (num_source_points == 0) {
     return true;
   }
 
@@ -1173,7 +1148,7 @@ clip_polygon(CollisionBox::Points &new_points,
     if (plane.dist_to_plane(get_plane(plane_no).get_point()) < 0.0) {
       // A point within the polygon is behind the clipping plane: the polygon
       // is all in.
-      new_points = source_points;
+      new_points.insert(new_points.end(), source_points, source_points + num_source_points);
       return true;
     }
     return false;
@@ -1194,14 +1169,13 @@ clip_polygon(CollisionBox::Points &new_points,
   // We might increase the number of vertices by as many as 1, if the plane
   // clips off exactly one corner.  (We might also decrease the number of
   // vertices, or keep them the same number.)
-  new_points.reserve(source_points.size() + 1);
+  new_points.reserve(num_source_points + 1);
 
-  LPoint2 last_point = source_points.back()._p;
+  LPoint2 last_point = source_points[num_source_points - 1]._p;
   bool last_is_in = !is_right(last_point - from2d, delta2d);
   bool all_in = last_is_in;
-  Points::const_iterator pi;
-  for (pi = source_points.begin(); pi != source_points.end(); ++pi) {
-    const LPoint2 &this_point = (*pi)._p;
+  for (size_t pi = 0; pi < num_source_points; ++pi) {
+    const LPoint2 &this_point = source_points[pi]._p;
     bool this_is_in = !is_right(this_point - from2d, delta2d);
 
     // There appears to be a compiler bug in gcc 4.0: we need to extract this
@@ -1234,15 +1208,13 @@ clip_polygon(CollisionBox::Points &new_points,
   return all_in;
 }
 
-
 /**
  * Returns the linear distance from the 2-d point to the nearest part of the
  * polygon defined by the points vector.  The result is negative if the point
  * is within the polygon.
  */
 PN_stdfloat CollisionBox::
-dist_to_polygon(const LPoint2 &p, const CollisionBox::Points &points) const {
-
+dist_to_polygon(const LPoint2 &p, const PointDef *points, size_t num_points) const {
   // We know that that the polygon is convex and is defined with the points in
   // counterclockwise order.  Therefore, we simply compare the signed distance
   // to each line segment; we ignore any negative values, and take the minimum
@@ -1254,10 +1226,9 @@ dist_to_polygon(const LPoint2 &p, const CollisionBox::Points &points) const {
   bool got_dist = false;
   PN_stdfloat best_dist = -1.0f;
 
-  size_t num_points = points.size();
   for (size_t i = 0; i < num_points - 1; ++i) {
     PN_stdfloat d = dist_to_line_segment(p, points[i]._p, points[i + 1]._p,
-                                   points[i]._v);
+                                         points[i]._v);
     if (d >= 0.0f) {
       if (!got_dist || d < best_dist) {
         best_dist = d;
@@ -1267,7 +1238,7 @@ dist_to_polygon(const LPoint2 &p, const CollisionBox::Points &points) const {
   }
 
   PN_stdfloat d = dist_to_line_segment(p, points[num_points - 1]._p, points[0]._p,
-                                 points[num_points - 1]._v);
+                                       points[num_points - 1]._v);
   if (d >= 0.0f) {
     if (!got_dist || d < best_dist) {
       best_dist = d;
@@ -1450,10 +1421,14 @@ write_datagram(BamWriter *manager, Datagram &me) {
   for(int i=0; i < 8; i++) {
     _vertex[i].write_datagram(me);
   }
-  me.add_stdfloat(_radius);
-  me.add_stdfloat(_x);
-  me.add_stdfloat(_y);
-  me.add_stdfloat(_z);
+  PN_stdfloat x = _vertex[0].get_x() - _center.get_x();
+  PN_stdfloat y = _vertex[0].get_y() - _center.get_y();
+  PN_stdfloat z = _vertex[0].get_z() - _center.get_z();
+  PN_stdfloat radius = sqrt(x * x + y * y + z * z);
+  me.add_stdfloat(radius);
+  me.add_stdfloat(x);
+  me.add_stdfloat(y);
+  me.add_stdfloat(z);
   for(int i=0; i < 6; i++) {
     _planes[i].write_datagram(me);
   }
@@ -1461,8 +1436,8 @@ write_datagram(BamWriter *manager, Datagram &me) {
     _to_2d_mat[i].write_datagram(me);
   }
   for(int i=0; i < 6; i++) {
-    me.add_uint16(_points[i].size());
-    for (size_t j = 0; j < _points[i].size(); j++) {
+    me.add_uint16(4);
+    for (size_t j = 0; j < 4; j++) {
       _points[i][j]._p.write_datagram(me);
       _points[i][j]._v.write_datagram(me);
     }
@@ -1497,10 +1472,10 @@ fillin(DatagramIterator& scan, BamReader* manager) {
   for(int i=0; i < 8; i++) {
     _vertex[i].read_datagram(scan);
   }
-  _radius = scan.get_stdfloat();
-  _x = scan.get_stdfloat();
-  _y = scan.get_stdfloat();
-  _z = scan.get_stdfloat();
+  scan.get_stdfloat();
+  scan.get_stdfloat();
+  scan.get_stdfloat();
+  scan.get_stdfloat();
   for(int i=0; i < 6; i++) {
     _planes[i].read_datagram(scan);
   }
@@ -1509,12 +1484,10 @@ fillin(DatagramIterator& scan, BamReader* manager) {
   }
   for(int i=0; i < 6; i++) {
     size_t size = scan.get_uint16();
+    nassertv(size == 4);
     for (size_t j = 0; j < size; j++) {
-      LPoint2 p;
-      LVector2 v;
-      p.read_datagram(scan);
-      v.read_datagram(scan);
-      _points[i].push_back(PointDef(p, v));
+      _points[i][j]._p.read_datagram(scan);
+      _points[i][j]._v.read_datagram(scan);
     }
   }
 }

+ 7 - 10
panda/src/collide/collisionBox.h

@@ -99,7 +99,6 @@ private:
   LPoint3 _center;
   LPoint3 _min;
   LPoint3 _max;
-  PN_stdfloat _x, _y, _z, _radius;
   LPoint3 _vertex[8]; // Each of the Eight Vertices of the Box
   LPlane _planes[6]; //Points to each of the six sides of the Box
 
@@ -119,6 +118,7 @@ private:
 public:
   class PointDef {
   public:
+    PointDef() = default;
     INLINE PointDef(const LPoint2 &p, const LVector2 &v);
     INLINE PointDef(PN_stdfloat x, PN_stdfloat y);
     INLINE PointDef(const PointDef &copy);
@@ -134,25 +134,22 @@ public:
                     const Points &points) const;
 
   bool point_is_inside(const LPoint2 &p, const Points &points) const;
-  PN_stdfloat dist_to_polygon(const LPoint2 &p, const Points &points) const;
+  PN_stdfloat dist_to_polygon(const LPoint2 &p, const PointDef *points, size_t num_points) const;
 
-  void setup_points(const LPoint3 *begin, const LPoint3 *end, int plane);
+  void setup_points(int plane);
   INLINE LPoint2 to_2d(const LVecBase3 &point3d, int plane) const;
   INLINE void calc_to_3d_mat(LMatrix4 &to_3d_mat, int plane) const;
-  INLINE void rederive_to_3d_mat(LMatrix4 &to_3d_mat, int plane) const;
   INLINE static LPoint3 to_3d(const LVecBase2 &point2d, const LMatrix4 &to_3d_mat);
-  bool clip_polygon(Points &new_points, const Points &source_points,
-                    const LPlane &plane,int plane_no) const;
+  bool clip_polygon(Points &new_points, const PointDef *source_points,
+                    size_t num_source_points, const LPlane &plane,
+                    int plane_no) const;
   bool apply_clip_plane(Points &new_points, const ClipPlaneAttrib *cpa,
                         const TransformState *net_transform, int plane_no) const;
 
 private:
-  Points _points[6]; // one set of points for each of the six planes that make up the box
+  PointDef _points[6][4]; // one set of points for each of the six planes that make up the box
   LMatrix4 _to_2d_mat[6];
 
-public:
-  INLINE Points get_plane_points( int n );
-
 public:
   static void register_with_read_factory();
   virtual void write_datagram(BamWriter *manager, Datagram &me);

+ 0 - 9
panda/src/collide/collisionHandlerEvent.I

@@ -25,15 +25,6 @@ operator () (const PT(CollisionEntry) &a,
   return a->get_into_node_path() < b->get_into_node_path();
 }
 
-/**
- * The assignment operator does absolutely nothing, since this is just a
- * function object class that stores no data.  We define it just to quiet up
- * g++ in -Wall mode.
- */
-INLINE void CollisionHandlerEvent::SortEntries::
-operator = (const CollisionHandlerEvent::SortEntries &) {
-}
-
 /**
  * Removes all of the previously-added in patterns.  See add_in_pattern.
  */

+ 0 - 1
panda/src/collide/collisionHandlerEvent.h

@@ -90,7 +90,6 @@ protected:
     INLINE bool
     operator () (const PT(CollisionEntry) &a,
                  const PT(CollisionEntry) &b) const;
-    INLINE void operator = (const SortEntries &other);
   };
 
   typedef pset<PT(CollisionEntry), SortEntries> Colliding;

+ 0 - 1
panda/src/collide/collisionHandlerPhysical_ext.cxx

@@ -21,7 +21,6 @@
  */
 PyObject *Extension<CollisionHandlerPhysical>::
 __reduce__(PyObject *self) const {
-  extern struct Dtool_PyTypedObject Dtool_Datagram;
   extern struct Dtool_PyTypedObject Dtool_NodePath;
 
   // Create a tuple with all the NodePath pointers.

+ 7 - 7
panda/src/collide/collisionLevelStateBase.I

@@ -31,8 +31,8 @@ CollisionLevelStateBase(const CollisionLevelStateBase &parent, PandaNode *child)
   _node_path(parent._node_path, child),
   _colliders(parent._colliders),
   _include_mask(parent._include_mask),
-  _local_bounds(parent._local_bounds),
-  _node_gbv(child->get_bounds()->as_geometric_bounding_volume())
+  _node_gbv(child->get_bounds()->as_geometric_bounding_volume()),
+  _local_bounds(parent._local_bounds)
 {
 }
 
@@ -44,8 +44,8 @@ CollisionLevelStateBase(const CollisionLevelStateBase &parent, const PandaNode::
   _node_path(parent._node_path, child.get_child()),
   _colliders(parent._colliders),
   _include_mask(parent._include_mask),
-  _local_bounds(parent._local_bounds),
-  _node_gbv(child.get_bounds())
+  _node_gbv(child.get_bounds()),
+  _local_bounds(parent._local_bounds)
 {
 }
 
@@ -57,9 +57,9 @@ CollisionLevelStateBase(const CollisionLevelStateBase &copy) :
   _node_path(copy._node_path),
   _colliders(copy._colliders),
   _include_mask(copy._include_mask),
+  _node_gbv(copy._node_gbv),
   _local_bounds(copy._local_bounds),
-  _parent_bounds(copy._parent_bounds),
-  _node_gbv(copy._node_gbv)
+  _parent_bounds(copy._parent_bounds)
 {
 }
 
@@ -71,9 +71,9 @@ operator = (const CollisionLevelStateBase &copy) {
   _node_path = copy._node_path;
   _colliders = copy._colliders;
   _include_mask = copy._include_mask;
+  _node_gbv = copy._node_gbv;
   _local_bounds = copy._local_bounds;
   _parent_bounds = copy._parent_bounds;
-  _node_gbv = copy._node_gbv;
 }
 
 /**

+ 3 - 3
panda/src/collide/collisionTraverser.cxx

@@ -647,7 +647,7 @@ r_traverse_single(CollisionLevelStateSingle &level_state, size_t pass) {
     // child.
     int index = node->get_visible_child();
     PandaNode::Children children = node->get_children();
-    if (index >= 0 && index < children.get_num_children()) {
+    if (index >= 0 && (size_t)index < children.get_num_children()) {
       const PandaNode::DownConnection &child = children.get_child_connection(index);
       CollisionLevelStateSingle::CurrentMask mask = level_state.get_child_mask(child);
       if (!mask.is_zero()) {
@@ -856,7 +856,7 @@ r_traverse_double(CollisionLevelStateDouble &level_state, size_t pass) {
     // child.
     int index = node->get_visible_child();
     PandaNode::Children children = node->get_children();
-    if (index >= 0 && index < children.get_num_children()) {
+    if (index >= 0 && (size_t)index < children.get_num_children()) {
       const PandaNode::DownConnection &child = children.get_child_connection(index);
       CollisionLevelStateDouble::CurrentMask mask = level_state.get_child_mask(child);
       if (!mask.is_zero()) {
@@ -1065,7 +1065,7 @@ r_traverse_quad(CollisionLevelStateQuad &level_state, size_t pass) {
     // child.
     int index = node->get_visible_child();
     PandaNode::Children children = node->get_children();
-    if (index >= 0 && index < children.get_num_children()) {
+    if (index >= 0 && (size_t)index < children.get_num_children()) {
       const PandaNode::DownConnection &child = children.get_child_connection(index);
       CollisionLevelStateQuad::CurrentMask mask = level_state.get_child_mask(child);
       if (!mask.is_zero()) {

+ 0 - 1
panda/src/collide/collisionTraverser_ext.cxx

@@ -21,7 +21,6 @@
 PyObject *Extension<CollisionTraverser>::
 __getstate__() const {
   extern struct Dtool_PyTypedObject Dtool_CollisionHandler;
-  extern struct Dtool_PyTypedObject Dtool_CollisionTraverser;
   extern struct Dtool_PyTypedObject Dtool_NodePath;
 
   const std::string &name = _this->get_name();

+ 4 - 4
panda/src/device/evdevInputDevice.cxx

@@ -126,8 +126,9 @@ TypeHandle EvdevInputDevice::_type_handle;
 EvdevInputDevice::
 EvdevInputDevice(LinuxInputDeviceManager *manager, size_t index) :
   _manager(manager),
-  _index(index),
   _fd(-1),
+  _quirks(0),
+  _index(index),
   _can_write(false),
   _ff_id(-1),
   _ff_playing(false),
@@ -138,9 +139,8 @@ EvdevInputDevice(LinuxInputDeviceManager *manager, size_t index) :
   _dpad_left_button(-1),
   _dpad_up_button(-1),
   _ltrigger_code(-1),
-  _rtrigger_code(-1),
-  _quirks(0) {
-
+  _rtrigger_code(-1)
+{
   char path[64];
   sprintf(path, "/dev/input/event%zd", index);
 

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