Browse Source

Merge branch 'release/1.10.x'

rdb 3 years ago
parent
commit
c82f95ad6b

+ 1 - 0
BACKERS.md

@@ -26,6 +26,7 @@ This is a list of all the people who are contributing financially to Panda3D.  I
 * Brian Lach
 * Brian Lach
 * Maxwell Dreytser
 * Maxwell Dreytser
 * SureBet
 * SureBet
+* Gyedo Jeon
 
 
 ## Backers
 ## Backers
 
 

+ 17 - 8
direct/src/dist/FreezeTool.py

@@ -787,7 +787,7 @@ class Freezer:
             return 'ModuleDef(%s)' % (', '.join(args))
             return 'ModuleDef(%s)' % (', '.join(args))
 
 
     def __init__(self, previous = None, debugLevel = 0,
     def __init__(self, previous = None, debugLevel = 0,
-                 platform = None, path=None, hiddenImports=None):
+                 platform = None, path=None, hiddenImports=None, optimize=None):
         # Normally, we are freezing for our own platform.  Change this
         # Normally, we are freezing for our own platform.  Change this
         # if untrue.
         # if untrue.
         self.platform = platform or PandaSystem.getPlatform()
         self.platform = platform or PandaSystem.getPlatform()
@@ -917,7 +917,13 @@ class Freezer:
                     ('.so', 'rb', 3),
                     ('.so', 'rb', 3),
                 ]
                 ]
 
 
-        self.mf = PandaModuleFinder(excludes=['doctest'], suffixes=suffixes, path=path)
+        if optimize is None or optimize < 0:
+            self.optimize = sys.flags.optimize
+        else:
+            self.optimize = optimize
+
+        self.mf = PandaModuleFinder(excludes=['doctest'], suffixes=suffixes,
+                                    path=path, optimize=self.optimize)
 
 
     def excludeFrom(self, freezer):
     def excludeFrom(self, freezer):
         """ Excludes all modules that have already been processed by
         """ Excludes all modules that have already been processed by
@@ -1413,7 +1419,7 @@ class Freezer:
                 else:
                 else:
                     filename += '.pyo'
                     filename += '.pyo'
                 if multifile.findSubfile(filename) < 0:
                 if multifile.findSubfile(filename) < 0:
-                    code = compile('', moduleName, 'exec', optimize=2)
+                    code = compile('', moduleName, 'exec', optimize=self.optimize)
                     self.__addPyc(multifile, filename, code, compressionLevel)
                     self.__addPyc(multifile, filename, code, compressionLevel)
 
 
             moduleDirs[str] = True
             moduleDirs[str] = True
@@ -1493,7 +1499,7 @@ class Freezer:
                 source = open(sourceFilename.toOsSpecific(), 'r').read()
                 source = open(sourceFilename.toOsSpecific(), 'r').read()
                 if source and source[-1] != '\n':
                 if source and source[-1] != '\n':
                     source = source + '\n'
                     source = source + '\n'
-                code = compile(source, str(sourceFilename), 'exec', optimize=2)
+                code = compile(source, str(sourceFilename), 'exec', optimize=self.optimize)
 
 
         self.__addPyc(multifile, filename, code, compressionLevel)
         self.__addPyc(multifile, filename, code, compressionLevel)
 
 
@@ -1572,7 +1578,7 @@ class Freezer:
             # trouble importing it as a builtin module.  Synthesize a frozen
             # trouble importing it as a builtin module.  Synthesize a frozen
             # module that loads it as builtin.
             # module that loads it as builtin.
             if '.' in moduleName and self.linkExtensionModules:
             if '.' in moduleName and self.linkExtensionModules:
-                code = compile('import sys;del sys.modules["%s"];import imp;imp.init_builtin("%s")' % (moduleName, moduleName), moduleName, 'exec', optimize=2)
+                code = compile('import sys;del sys.modules["%s"];import imp;imp.init_builtin("%s")' % (moduleName, moduleName), moduleName, 'exec', optimize=self.optimize)
                 code = marshal.dumps(code)
                 code = marshal.dumps(code)
                 mangledName = self.mangleName(moduleName)
                 mangledName = self.mangleName(moduleName)
                 moduleDefs.append(self.makeModuleDef(mangledName, code))
                 moduleDefs.append(self.makeModuleDef(mangledName, code))
@@ -1845,7 +1851,7 @@ class Freezer:
                     code = 'import sys;del sys.modules["%s"];import sys,os,imp;imp.load_dynamic("%s",os.path.join(sys.path[0], "%s%s"))' % (moduleName, moduleName, moduleName, modext)
                     code = 'import sys;del sys.modules["%s"];import sys,os,imp;imp.load_dynamic("%s",os.path.join(sys.path[0], "%s%s"))' % (moduleName, moduleName, moduleName, modext)
                 else:
                 else:
                     code = 'import sys;del sys.modules["%s"];import sys,os,imp;imp.load_dynamic("%s",os.path.join(os.path.dirname(sys.executable), "%s%s"))' % (moduleName, moduleName, moduleName, modext)
                     code = 'import sys;del sys.modules["%s"];import sys,os,imp;imp.load_dynamic("%s",os.path.join(os.path.dirname(sys.executable), "%s%s"))' % (moduleName, moduleName, moduleName, modext)
-                code = compile(code, moduleName, 'exec', optimize=2)
+                code = compile(code, moduleName, 'exec', optimize=self.optimize)
                 code = marshal.dumps(code)
                 code = marshal.dumps(code)
                 moduleList.append((moduleName, len(pool), len(code)))
                 moduleList.append((moduleName, len(pool), len(code)))
                 pool += code
                 pool += code
@@ -1946,6 +1952,8 @@ class Freezer:
                 flags |= 1
                 flags |= 1
             if log_filename_strftime:
             if log_filename_strftime:
                 flags |= 2
                 flags |= 2
+            if self.optimize < 2:
+                flags |= 4 # keep_docstrings
 
 
             # Compose the header we will be writing to the stub, to tell it
             # Compose the header we will be writing to the stub, to tell it
             # where to find the module data blob, as well as other variables.
             # where to find the module data blob, as well as other variables.
@@ -2377,6 +2385,7 @@ class PandaModuleFinder(modulefinder.ModuleFinder):
         """
         """
 
 
         self.suffixes = kw.pop('suffixes', imp.get_suffixes())
         self.suffixes = kw.pop('suffixes', imp.get_suffixes())
+        self.optimize = kw.pop('optimize', -1)
 
 
         modulefinder.ModuleFinder.__init__(self, *args, **kw)
         modulefinder.ModuleFinder.__init__(self, *args, **kw)
 
 
@@ -2530,7 +2539,7 @@ class PandaModuleFinder(modulefinder.ModuleFinder):
 
 
         if type is _PKG_NAMESPACE_DIRECTORY:
         if type is _PKG_NAMESPACE_DIRECTORY:
             m = self.add_module(fqname)
             m = self.add_module(fqname)
-            m.__code__ = compile('', '', 'exec', optimize=2)
+            m.__code__ = compile('', '', 'exec', optimize=self.optimize)
             m.__path__ = pathname
             m.__path__ = pathname
             return m
             return m
 
 
@@ -2542,7 +2551,7 @@ class PandaModuleFinder(modulefinder.ModuleFinder):
                 code = fp.read()
                 code = fp.read()
 
 
             code += b'\n' if isinstance(code, bytes) else '\n'
             code += b'\n' if isinstance(code, bytes) else '\n'
-            co = compile(code, pathname, 'exec', optimize=2)
+            co = compile(code, pathname, 'exec', optimize=self.optimize)
         elif type == imp.PY_COMPILED:
         elif type == imp.PY_COMPILED:
             if sys.version_info >= (3, 7):
             if sys.version_info >= (3, 7):
                 try:
                 try:

+ 59 - 4
direct/src/dist/commands.py

@@ -49,6 +49,39 @@ def _parse_dict(input):
     return d
     return d
 
 
 
 
+def _register_python_loaders():
+    # We need this method so that we don't depend on direct.showbase.Loader.
+    if getattr(_register_python_loaders, 'done', None):
+        return
+
+    _register_python_loaders.done = True
+
+    registry = p3d.LoaderFileTypeRegistry.getGlobalPtr()
+
+    for entry_point in pkg_resources.iter_entry_points('panda3d.loaders'):
+        registry.register_deferred_type(entry_point)
+
+
+def _model_to_bam(_build_cmd, srcpath, dstpath):
+    if dstpath.endswith('.gz') or dstpath.endswith('.pz'):
+        dstpath = dstpath[:-3]
+    dstpath = dstpath + '.bam'
+
+    src_fn = p3d.Filename.from_os_specific(srcpath)
+    dst_fn = p3d.Filename.from_os_specific(dstpath)
+
+    _register_python_loaders()
+
+    loader = p3d.Loader.get_global_ptr()
+    options = p3d.LoaderOptions(p3d.LoaderOptions.LF_report_errors |
+                                p3d.LoaderOptions.LF_no_ram_cache)
+    node = loader.load_sync(src_fn, options)
+    if not node:
+        raise IOError('Failed to load model: %s' % (srcpath))
+
+    if not p3d.NodePath(node).write_bam_file(dst_fn):
+        raise IOError('Failed to write .bam file: %s' % (dstpath))
+
 
 
 def egg2bam(_build_cmd, srcpath, dstpath):
 def egg2bam(_build_cmd, srcpath, dstpath):
     if dstpath.endswith('.gz') or dstpath.endswith('.pz'):
     if dstpath.endswith('.gz') or dstpath.endswith('.pz'):
@@ -277,13 +310,16 @@ class build_apps(setuptools.Command):
         self.log_filename = None
         self.log_filename = None
         self.log_filename_strftime = True
         self.log_filename_strftime = True
         self.log_append = False
         self.log_append = False
+        self.prefer_discrete_gpu = False
         self.requirements_path = os.path.join(os.getcwd(), 'requirements.txt')
         self.requirements_path = os.path.join(os.getcwd(), 'requirements.txt')
+        self.strip_docstrings = True
         self.use_optimized_wheels = True
         self.use_optimized_wheels = True
         self.optimized_wheel_index = ''
         self.optimized_wheel_index = ''
         self.pypi_extra_indexes = [
         self.pypi_extra_indexes = [
             'https://archive.panda3d.org/thirdparty',
             'https://archive.panda3d.org/thirdparty',
         ]
         ]
         self.file_handlers = {}
         self.file_handlers = {}
+        self.bam_model_extensions = []
         self.exclude_dependencies = [
         self.exclude_dependencies = [
             # Windows
             # Windows
             'kernel32.dll', 'user32.dll', 'wsock32.dll', 'ws2_32.dll',
             'kernel32.dll', 'user32.dll', 'wsock32.dll', 'ws2_32.dll',
@@ -444,6 +480,15 @@ class build_apps(setuptools.Command):
         for glob in self.exclude_dependencies:
         for glob in self.exclude_dependencies:
             glob.case_sensitive = False
             glob.case_sensitive = False
 
 
+        # bam_model_extensions registers a 2bam handler for each given extension.
+        # They can override a default handler, but not a custom handler.
+        if self.bam_model_extensions:
+            for ext in self.bam_model_extensions:
+                ext = '.' + ext.lstrip('.')
+                assert ext not in self.file_handlers, \
+                    'Extension {} occurs in both file_handlers and bam_model_extensions!'.format(ext)
+                self.file_handlers[ext] = _model_to_bam
+
         tmp = self.default_file_handlers.copy()
         tmp = self.default_file_handlers.copy()
         tmp.update(self.file_handlers)
         tmp.update(self.file_handlers)
         self.file_handlers = tmp
         self.file_handlers = tmp
@@ -625,11 +670,16 @@ class build_apps(setuptools.Command):
             self.icon_objects.get('*', None),
             self.icon_objects.get('*', None),
         )
         )
 
 
-        if icon is not None:
+        if icon is not None or self.prefer_discrete_gpu:
             pef = pefile.PEFile()
             pef = pefile.PEFile()
             pef.open(runtime, 'r+')
             pef.open(runtime, 'r+')
-            pef.add_icon(icon)
-            pef.add_resource_section()
+            if icon is not None:
+                pef.add_icon(icon)
+                pef.add_resource_section()
+            if self.prefer_discrete_gpu:
+                if not pef.rename_export("SymbolPlaceholder___________________", "AmdPowerXpressRequestHighPerformance") or \
+                   not pef.rename_export("SymbolPlaceholder__", "NvOptimusEnablement"):
+                    self.warn("Failed to apply prefer_discrete_gpu, newer target Panda3D version may be required")
             pef.write_changes()
             pef.write_changes()
             pef.close()
             pef.close()
 
 
@@ -944,7 +994,8 @@ class build_apps(setuptools.Command):
             freezer = FreezeTool.Freezer(
             freezer = FreezeTool.Freezer(
                 platform=platform,
                 platform=platform,
                 path=path,
                 path=path,
-                hiddenImports=self.hidden_imports
+                hiddenImports=self.hidden_imports,
+                optimize=2 if self.strip_docstrings else 1
             )
             )
             freezer.addModule('__main__', filename=mainscript)
             freezer.addModule('__main__', filename=mainscript)
             if platform.startswith('android'):
             if platform.startswith('android'):
@@ -1617,6 +1668,10 @@ class bdist_apps(setuptools.Command):
         'manylinux_2_24_ppc64': ['gztar'],
         'manylinux_2_24_ppc64': ['gztar'],
         'manylinux_2_24_ppc64le': ['gztar'],
         'manylinux_2_24_ppc64le': ['gztar'],
         'manylinux_2_24_s390x': ['gztar'],
         'manylinux_2_24_s390x': ['gztar'],
+        'manylinux_2_28_x86_64': ['gztar'],
+        'manylinux_2_28_aarch64': ['gztar'],
+        'manylinux_2_28_ppc64le': ['gztar'],
+        'manylinux_2_28_s390x': ['gztar'],
         'android': ['aab'],
         'android': ['aab'],
         # Everything else defaults to ['zip']
         # Everything else defaults to ['zip']
     }
     }

+ 30 - 0
direct/src/dist/pefile.py

@@ -600,6 +600,36 @@ class PEFile(object):
         if self.res_rva.addr and self.res_rva.size:
         if self.res_rva.addr and self.res_rva.size:
             self.resources.unpack_from(self.vmem, self.res_rva.addr)
             self.resources.unpack_from(self.vmem, self.res_rva.addr)
 
 
+    def _mark_address_modified(self, rva):
+        for section in self.sections:
+            if rva >= section.vaddr and rva - section.vaddr <= section.size:
+                section.modified = True
+
+    def rename_export(self, old_name, new_name):
+        """ Renames a symbol in the export table. """
+
+        assert len(new_name) <= len(old_name)
+
+        new_name = new_name.ljust(len(old_name) + 1, '\0').encode('ascii')
+
+        start = self.exp_rva.addr
+        expdir = expdirtab(*unpack('<IIHHIIIIIII', self.vmem[start:start+40]))
+        if expdir.nnames == 0 or expdir.ordinals == 0 or expdir.names == 0:
+            return False
+
+        nptr = expdir.names
+        for i in range(expdir.nnames):
+            name_rva, = unpack('<I', self.vmem[nptr:nptr+4])
+            if name_rva != 0:
+                name = _unpack_zstring(self.vmem, name_rva)
+                if name == old_name:
+                    self.vmem[name_rva:name_rva+len(new_name)] = new_name
+                    self._mark_address_modified(name_rva)
+                    return True
+            nptr += 4
+
+        return False
+
     def get_export_address(self, symbol_name):
     def get_export_address(self, symbol_name):
         """ Finds the virtual address for a named export symbol. """
         """ Finds the virtual address for a named export symbol. """
 
 

+ 3 - 0
direct/src/showbase/EventManager.py

@@ -180,3 +180,6 @@ class EventManager:
         # since the task removal itself might also fire off an event.
         # since the task removal itself might also fire off an event.
         if self.eventQueue is not None:
         if self.eventQueue is not None:
             self.eventQueue.clear()
             self.eventQueue.clear()
+
+    do_events = doEvents
+    process_event = processEvent

+ 11 - 0
dtool/src/prc/configVariableFilename.I

@@ -226,6 +226,17 @@ __bool__() const {
   return !get_value().empty();
   return !get_value().empty();
 }
 }
 
 
+/**
+ * Allows a ConfigVariableFilename object to be passed to any Python function
+ * that accepts an os.PathLike object.
+ *
+ * @since 1.10.13
+ */
+INLINE std::wstring ConfigVariableFilename::
+__fspath__() const {
+  return get_ref_value().to_os_specific_w();
+}
+
 /**
 /**
  * Returns the variable's value, as a reference into the config variable
  * Returns the variable's value, as a reference into the config variable
  * itself.  This is the internal method that implements get_value(), which
  * itself.  This is the internal method that implements get_value(), which

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

@@ -61,6 +61,7 @@ PUBLISHED:
   INLINE void set_word(size_t n, const Filename &value);
   INLINE void set_word(size_t n, const Filename &value);
 
 
   INLINE bool __bool__() const;
   INLINE bool __bool__() const;
+  INLINE std::wstring __fspath__() const;
 
 
 private:
 private:
   void reload_cache();
   void reload_cache();

+ 20 - 12
makepanda/makepanda.py

@@ -382,8 +382,9 @@ MAJOR_VERSION = '.'.join(VERSION.split('.')[:2])
 
 
 # Now determine the distutils-style platform tag for the target system.
 # Now determine the distutils-style platform tag for the target system.
 target = GetTarget()
 target = GetTarget()
+target_arch = GetTargetArch()
 if target == 'windows':
 if target == 'windows':
-    if GetTargetArch() == 'x64':
+    if target_arch == 'x64':
         PLATFORM = 'win-amd64'
         PLATFORM = 'win-amd64'
     else:
     else:
         PLATFORM = 'win32'
         PLATFORM = 'win32'
@@ -391,7 +392,7 @@ if target == 'windows':
 elif target == 'darwin':
 elif target == 'darwin':
     arch_tag = None
     arch_tag = None
     if not OSX_ARCHS:
     if not OSX_ARCHS:
-        arch_tag = GetTargetArch()
+        arch_tag = target_arch
     elif len(OSX_ARCHS) == 1:
     elif len(OSX_ARCHS) == 1:
         arch_tag = OSX_ARCHS[0]
         arch_tag = OSX_ARCHS[0]
     elif frozenset(OSX_ARCHS) == frozenset(('i386', 'ppc')):
     elif frozenset(OSX_ARCHS) == frozenset(('i386', 'ppc')):
@@ -416,45 +417,53 @@ elif target == 'darwin':
 
 
 elif target == 'linux' and (os.path.isfile("/lib/libc-2.5.so") or os.path.isfile("/lib64/libc-2.5.so")) and os.path.isdir("/opt/python"):
 elif target == 'linux' and (os.path.isfile("/lib/libc-2.5.so") or os.path.isfile("/lib64/libc-2.5.so")) and os.path.isdir("/opt/python"):
     # This is manylinux1.  A bit of a sloppy check, though.
     # This is manylinux1.  A bit of a sloppy check, though.
-    if GetTargetArch() in ('x86_64', 'amd64'):
+    if target_arch in ('x86_64', 'amd64'):
         PLATFORM = 'manylinux1-x86_64'
         PLATFORM = 'manylinux1-x86_64'
-    elif GetTargetArch() in ('arm64', 'aarch64'):
+    elif target_arch in ('arm64', 'aarch64'):
         PLATFORM = 'manylinux1-aarch64'
         PLATFORM = 'manylinux1-aarch64'
     else:
     else:
         PLATFORM = 'manylinux1-i686'
         PLATFORM = 'manylinux1-i686'
 
 
 elif target == 'linux' and (os.path.isfile("/lib/libc-2.12.so") or os.path.isfile("/lib64/libc-2.12.so")) and os.path.isdir("/opt/python"):
 elif target == 'linux' and (os.path.isfile("/lib/libc-2.12.so") or os.path.isfile("/lib64/libc-2.12.so")) and os.path.isdir("/opt/python"):
     # Same sloppy check for manylinux2010.
     # Same sloppy check for manylinux2010.
-    if GetTargetArch() in ('x86_64', 'amd64'):
+    if target_arch in ('x86_64', 'amd64'):
         PLATFORM = 'manylinux2010-x86_64'
         PLATFORM = 'manylinux2010-x86_64'
-    elif GetTargetArch() in ('arm64', 'aarch64'):
+    elif target_arch in ('arm64', 'aarch64'):
         PLATFORM = 'manylinux2010-aarch64'
         PLATFORM = 'manylinux2010-aarch64'
     else:
     else:
         PLATFORM = 'manylinux2010-i686'
         PLATFORM = 'manylinux2010-i686'
 
 
 elif target == 'linux' and (os.path.isfile("/lib/libc-2.17.so") or os.path.isfile("/lib64/libc-2.17.so")) and os.path.isdir("/opt/python"):
 elif target == 'linux' and (os.path.isfile("/lib/libc-2.17.so") or os.path.isfile("/lib64/libc-2.17.so")) and os.path.isdir("/opt/python"):
     # Same sloppy check for manylinux2014.
     # Same sloppy check for manylinux2014.
-    if GetTargetArch() in ('x86_64', 'amd64'):
+    if target_arch in ('x86_64', 'amd64'):
         PLATFORM = 'manylinux2014-x86_64'
         PLATFORM = 'manylinux2014-x86_64'
-    elif GetTargetArch() in ('arm64', 'aarch64'):
+    elif target_arch in ('arm64', 'aarch64'):
         PLATFORM = 'manylinux2014-aarch64'
         PLATFORM = 'manylinux2014-aarch64'
     else:
     else:
         PLATFORM = 'manylinux2014-i686'
         PLATFORM = 'manylinux2014-i686'
 
 
 elif target == 'linux' and (os.path.isfile("/lib/i386-linux-gnu/libc-2.24.so") or os.path.isfile("/lib/x86_64-linux-gnu/libc-2.24.so")) and os.path.isdir("/opt/python"):
 elif target == 'linux' and (os.path.isfile("/lib/i386-linux-gnu/libc-2.24.so") or os.path.isfile("/lib/x86_64-linux-gnu/libc-2.24.so")) and os.path.isdir("/opt/python"):
     # Same sloppy check for manylinux_2_24.
     # Same sloppy check for manylinux_2_24.
-    if GetTargetArch() in ('x86_64', 'amd64'):
+    if target_arch in ('x86_64', 'amd64'):
         PLATFORM = 'manylinux_2_24-x86_64'
         PLATFORM = 'manylinux_2_24-x86_64'
-    elif GetTargetArch() in ('arm64', 'aarch64'):
+    elif target_arch in ('arm64', 'aarch64'):
         PLATFORM = 'manylinux_2_24-aarch64'
         PLATFORM = 'manylinux_2_24-aarch64'
     else:
     else:
         PLATFORM = 'manylinux_2_24-i686'
         PLATFORM = 'manylinux_2_24-i686'
 
 
+elif target == 'linux' and os.path.isfile("/lib64/libc-2.28.so") and os.path.isfile('/etc/almalinux-release') and os.path.isdir("/opt/python"):
+    # Same sloppy check for manylinux_2_28.
+    if target_arch in ('x86_64', 'amd64'):
+        PLATFORM = 'manylinux_2_28-x86_64'
+    elif target_arch in ('arm64', 'aarch64'):
+        PLATFORM = 'manylinux_2_28-aarch64'
+    else:
+        raise RuntimeError('Unhandled arch %s, please file a bug report!' % (target_arch))
+
 elif not CrossCompiling():
 elif not CrossCompiling():
     if HasTargetArch():
     if HasTargetArch():
         # Replace the architecture in the platform string.
         # Replace the architecture in the platform string.
         platform_parts = get_platform().rsplit('-', 1)
         platform_parts = get_platform().rsplit('-', 1)
-        target_arch = GetTargetArch()
         if target_arch == 'amd64':
         if target_arch == 'amd64':
             target_arch = 'x86_64'
             target_arch = 'x86_64'
         PLATFORM = platform_parts[0] + '-' + target_arch
         PLATFORM = platform_parts[0] + '-' + target_arch
@@ -463,7 +472,6 @@ elif not CrossCompiling():
         PLATFORM = get_platform()
         PLATFORM = get_platform()
 
 
 else:
 else:
-    target_arch = GetTargetArch()
     if target_arch == 'amd64':
     if target_arch == 'amd64':
         target_arch = 'x86_64'
         target_arch = 'x86_64'
     PLATFORM = '{0}-{1}'.format(target, target_arch)
     PLATFORM = '{0}-{1}'.format(target, target_arch)

+ 5 - 1
makepanda/makepandacore.py

@@ -2203,6 +2203,10 @@ def SdkLocatePython(prefer_thirdparty_python=False):
             # Fall back to looking on the system.
             # Fall back to looking on the system.
             py_fwx = "/Library/Frameworks/Python.framework/Versions/" + version
             py_fwx = "/Library/Frameworks/Python.framework/Versions/" + version
 
 
+        if not os.path.exists(py_fwx):
+            # Newer macOS versions use this scheme.
+            py_fwx = "/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/" + version
+
         if not os.path.exists(py_fwx):
         if not os.path.exists(py_fwx):
             exit("Could not locate Python installation at %s" % (py_fwx))
             exit("Could not locate Python installation at %s" % (py_fwx))
 
 
@@ -2439,7 +2443,7 @@ def SdkLocateMacOSX(archs = []):
         # Prefer pre-10.14 for now so that we can keep building FMOD.
         # Prefer pre-10.14 for now so that we can keep building FMOD.
         sdk_versions += ["10.13", "10.12", "10.11", "10.10", "10.9"]
         sdk_versions += ["10.13", "10.12", "10.11", "10.10", "10.9"]
 
 
-    sdk_versions += ["11.3", "11.1", "11.0"]
+    sdk_versions += ["13.0", "12.3", "11.3", "11.1", "11.0"]
 
 
     if 'arm64' not in archs:
     if 'arm64' not in archs:
         sdk_versions += ["10.15", "10.14"]
         sdk_versions += ["10.15", "10.14"]

+ 2 - 0
makepanda/makewheel.py

@@ -656,6 +656,8 @@ def makewheel(version, output_dir, platform=None):
                     platform = platform.replace("linux", "manylinux2014")
                     platform = platform.replace("linux", "manylinux2014")
                 elif os.path.isfile("/lib/i386-linux-gnu/libc-2.24.so") or os.path.isfile("/lib/x86_64-linux-gnu/libc-2.24.so"):
                 elif os.path.isfile("/lib/i386-linux-gnu/libc-2.24.so") or os.path.isfile("/lib/x86_64-linux-gnu/libc-2.24.so"):
                     platform = platform.replace("linux", "manylinux_2_24")
                     platform = platform.replace("linux", "manylinux_2_24")
+                elif os.path.isfile("/lib64/libc-2.28.so") and os.path.isfile('/etc/almalinux-release'):
+                    platform = platform.replace("linux", "manylinux_2_28")
 
 
     platform = platform.replace('-', '_').replace('.', '_')
     platform = platform.replace('-', '_').replace('.', '_')
 
 

+ 36 - 4
panda/src/cocoadisplay/cocoaGraphicsWindow.mm

@@ -43,12 +43,35 @@
 #import <OpenGL/OpenGL.h>
 #import <OpenGL/OpenGL.h>
 #import <Carbon/Carbon.h>
 #import <Carbon/Carbon.h>
 
 
+#include <sys/sysctl.h>
+
 TypeHandle CocoaGraphicsWindow::_type_handle;
 TypeHandle CocoaGraphicsWindow::_type_handle;
 
 
 #ifndef MAC_OS_X_VERSION_10_15
 #ifndef MAC_OS_X_VERSION_10_15
 #define NSAppKitVersionNumber10_14 1671
 #define NSAppKitVersionNumber10_14 1671
 #endif
 #endif
 
 
+/**
+ * Returns true if this is an arm64-based mac.
+ */
+static int is_arm64_mac() {
+#ifdef __aarch64__
+  return 1;
+#elif defined(__x86_64__)
+  // Running in Rosetta 2?
+  static int ret = -1;
+  if (ret < 0) {
+    size_t size = sizeof(ret);
+    if (sysctlbyname("sysctl.proc_translated", &ret, &size, nullptr, 0) == -1) {
+      ret = 0;
+    }
+  }
+  return ret;
+#else
+  return 0;
+#endif
+}
+
 /**
 /**
  *
  *
  */
  */
@@ -187,8 +210,17 @@ begin_frame(FrameMode mode, Thread *current_thread) {
 
 
   // Set the drawable.
   // Set the drawable.
   if (_properties.get_fullscreen()) {
   if (_properties.get_fullscreen()) {
-    // Fullscreen.
-    CGLSetFullScreenOnDisplay((CGLContextObj) [cocoagsg->_context CGLContextObj], CGDisplayIDToOpenGLDisplayMask(_display));
+    // Fullscreen.  Note that this call doesn't work with the newer
+    // Metal-based OpenGL drivers.
+    if (!is_arm64_mac()) {
+      CGLError err = CGLSetFullScreenOnDisplay((CGLContextObj) [cocoagsg->_context CGLContextObj], CGDisplayIDToOpenGLDisplayMask(_display));
+      if (err != kCGLNoError) {
+        cocoadisplay_cat.error()
+          << "Failed call to CGLSetFullScreenOnDisplay with display mask "
+          << CGDisplayIDToOpenGLDisplayMask(_display) << ": " << CGLErrorString(err) << "\n";
+        return false;
+      }
+    }
   } else {
   } else {
     // Although not recommended, it is technically possible to use the same
     // Although not recommended, it is technically possible to use the same
     // context with multiple different-sized windows.  If that happens, the
     // context with multiple different-sized windows.  If that happens, the
@@ -626,7 +658,7 @@ open_window() {
     }
     }
 
 
     if (_properties.get_fullscreen()) {
     if (_properties.get_fullscreen()) {
-      [_window setLevel: NSMainMenuWindowLevel + 1];
+      [_window setLevel: CGShieldingWindowLevel()];
     } else {
     } else {
       switch (_properties.get_z_order()) {
       switch (_properties.get_z_order()) {
       case WindowProperties::Z_bottom:
       case WindowProperties::Z_bottom:
@@ -824,7 +856,7 @@ set_properties_now(WindowProperties &properties) {
                 [_window setStyleMask:NSBorderlessWindowMask];
                 [_window setStyleMask:NSBorderlessWindowMask];
               }
               }
               [_window makeFirstResponder:_view];
               [_window makeFirstResponder:_view];
-              [_window setLevel:NSMainMenuWindowLevel+1];
+              [_window setLevel:CGShieldingWindowLevel()];
               [_window makeKeyAndOrderFront:nil];
               [_window makeKeyAndOrderFront:nil];
             }
             }
 
 

+ 14 - 0
panda/src/glstuff/glGraphicsStateGuardian_src.cxx

@@ -12078,7 +12078,21 @@ set_state_and_transform(const RenderState *target,
   if (_target_rs->get_attrib(texture_slot) != _state_rs->get_attrib(texture_slot) ||
   if (_target_rs->get_attrib(texture_slot) != _state_rs->get_attrib(texture_slot) ||
       !_state_mask.get_bit(texture_slot)) {
       !_state_mask.get_bit(texture_slot)) {
     //PStatGPUTimer timer(this, _draw_set_state_texture_pcollector);
     //PStatGPUTimer timer(this, _draw_set_state_texture_pcollector);
+#ifdef OPENGLES_1
     determine_target_texture();
     determine_target_texture();
+#else
+    if (has_fixed_function_pipeline() ||
+        _current_shader == nullptr ||
+        _current_shader == _default_shader) {
+      determine_target_texture();
+    } else {
+      // If we have a custom shader, don't filter down the list of textures.
+      _target_texture = (const TextureAttrib *)
+        _target_rs->get_attrib_def(TextureAttrib::get_class_slot());
+      _target_tex_gen = (const TexGenAttrib *)
+        _target_rs->get_attrib_def(TexGenAttrib::get_class_slot());
+    }
+#endif
     do_issue_texture();
     do_issue_texture();
 
 
     // Since the TexGen and TexMatrix states depend partly on the particular
     // Since the TexGen and TexMatrix states depend partly on the particular

+ 2 - 7
panda/src/tform/mouseWatcher.cxx

@@ -724,19 +724,14 @@ clear_current_regions() {
     while (old_ri != _current_regions.end()) {
     while (old_ri != _current_regions.end()) {
       // Here's a region we don't have any more.
       // Here's a region we don't have any more.
       MouseWatcherRegion *old_region = (*old_ri);
       MouseWatcherRegion *old_region = (*old_ri);
-      old_region->exit_region(param);
-      throw_event_pattern(_leave_pattern, old_region, ButtonHandle::none());
-      if (_preferred_region == old_region) {
-        _preferred_region = nullptr;
-      }
+      without_region(old_region, param);
       ++old_ri;
       ++old_ri;
     }
     }
 
 
     _current_regions.clear();
     _current_regions.clear();
 
 
     if (_preferred_region != nullptr) {
     if (_preferred_region != nullptr) {
-      _preferred_region->exit_region(param);
-      throw_event_pattern(_leave_pattern, _preferred_region, ButtonHandle::none());
+      exit_region(_preferred_region, param);
       _preferred_region = nullptr;
       _preferred_region = nullptr;
     }
     }
   }
   }

+ 12 - 4
panda/src/windisplay/winGraphicsWindow.cxx

@@ -1739,7 +1739,9 @@ window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
     if (_lost_keypresses) {
     if (_lost_keypresses) {
       resend_lost_keypresses();
       resend_lost_keypresses();
     }
     }
-    ReleaseCapture();
+    if (wparam == 0) {
+      ReleaseCapture();
+    }
     _input->button_up(MouseButton::button(0), get_message_time());
     _input->button_up(MouseButton::button(0), get_message_time());
     return 0;
     return 0;
 
 
@@ -1747,7 +1749,9 @@ window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
     if (_lost_keypresses) {
     if (_lost_keypresses) {
       resend_lost_keypresses();
       resend_lost_keypresses();
     }
     }
-    ReleaseCapture();
+    if (wparam == 0) {
+      ReleaseCapture();
+    }
     _input->button_up(MouseButton::button(1), get_message_time());
     _input->button_up(MouseButton::button(1), get_message_time());
     return 0;
     return 0;
 
 
@@ -1755,7 +1759,9 @@ window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
     if (_lost_keypresses) {
     if (_lost_keypresses) {
       resend_lost_keypresses();
       resend_lost_keypresses();
     }
     }
-    ReleaseCapture();
+    if (wparam == 0) {
+      ReleaseCapture();
+    }
     _input->button_up(MouseButton::button(2), get_message_time());
     _input->button_up(MouseButton::button(2), get_message_time());
     return 0;
     return 0;
 
 
@@ -1764,7 +1770,9 @@ window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
       if (_lost_keypresses) {
       if (_lost_keypresses) {
         resend_lost_keypresses();
         resend_lost_keypresses();
       }
       }
-      ReleaseCapture();
+      if (wparam == 0) {
+        ReleaseCapture();
+      }
       int whichButton = GET_XBUTTON_WPARAM(wparam);
       int whichButton = GET_XBUTTON_WPARAM(wparam);
       if (whichButton == XBUTTON1) {
       if (whichButton == XBUTTON1) {
         _input->button_up(MouseButton::button(3), get_message_time());
         _input->button_up(MouseButton::button(3), get_message_time());

+ 13 - 1
pandatool/src/deploy-stub/deploy-stub.c

@@ -34,6 +34,7 @@
 enum Flags {
 enum Flags {
   F_log_append = 1,
   F_log_append = 1,
   F_log_filename_strftime = 2,
   F_log_filename_strftime = 2,
+  F_keep_docstrings = 4,
 };
 };
 
 
 /* Define an exposed symbol where we store the offset to the module data. */
 /* Define an exposed symbol where we store the offset to the module data. */
@@ -56,6 +57,13 @@ volatile struct {
   // end up putting it in the .bss section for zero-initialized data.
   // end up putting it in the .bss section for zero-initialized data.
 } blobinfo = {(uint64_t)-1};
 } blobinfo = {(uint64_t)-1};
 
 
+
+#ifdef _WIN32
+// These placeholders can have their names changed by deploy-stub.
+__declspec(dllexport) DWORD SymbolPlaceholder___________________ = 0x00000001;
+__declspec(dllexport) DWORD SymbolPlaceholder__ = 0x00000001;
+#endif
+
 #ifdef MS_WINDOWS
 #ifdef MS_WINDOWS
 #  define WIN32_LEAN_AND_MEAN
 #  define WIN32_LEAN_AND_MEAN
 #  include <windows.h>
 #  include <windows.h>
@@ -389,7 +397,11 @@ int Py_FrozenMain(int argc, char **argv)
     Py_NoUserSiteDirectory = 1;
     Py_NoUserSiteDirectory = 1;
 
 
 #if PY_VERSION_HEX >= 0x03020000
 #if PY_VERSION_HEX >= 0x03020000
-    Py_OptimizeFlag = 2;
+    if (blobinfo.flags & F_keep_docstrings) {
+      Py_OptimizeFlag = 1;
+    } else {
+      Py_OptimizeFlag = 2;
+    }
 #endif
 #endif
 
 
 #ifndef NDEBUG
 #ifndef NDEBUG