Browse Source

Merge branch 'release/1.10.x'

rdb 2 years ago
parent
commit
cdc95acf81
33 changed files with 352 additions and 76 deletions
  1. 3 3
      contrib/src/panda3dtoolsgui/Panda3DToolsGUI.py
  2. 16 0
      direct/src/dist/FreezeTool.py
  3. 14 1
      direct/src/dist/commands.py
  4. 17 8
      dtool/src/dtoolutil/filename.cxx
  5. 5 0
      dtool/src/dtoolutil/filename.h
  6. 15 5
      dtool/src/interrogate/functionRemap.cxx
  7. 1 0
      dtool/src/interrogate/functionRemap.h
  8. 8 1
      dtool/src/interrogate/interfaceMakerPythonNative.cxx
  9. 5 7
      panda/src/display/graphicsOutput.cxx
  10. 28 3
      panda/src/egldisplay/eglGraphicsBuffer.cxx
  11. 2 0
      panda/src/egldisplay/eglGraphicsBuffer.h
  12. 0 1
      panda/src/egldisplay/eglGraphicsPipe.cxx
  13. 6 5
      panda/src/express/virtualFileSystem.I
  14. 3 3
      panda/src/iphone/ipfreeze.py
  15. 1 1
      panda/src/iphone/provision.py
  16. 2 0
      panda/src/pgraph/CMakeLists.txt
  17. 4 0
      panda/src/pgraph/bamFile.h
  18. 28 0
      panda/src/pgraph/bamFile_ext.cxx
  19. 37 0
      panda/src/pgraph/bamFile_ext.h
  20. 1 0
      panda/src/pgraph/p3pgraph_ext_composite.cxx
  21. 2 0
      panda/src/putil/CMakeLists.txt
  22. 1 1
      panda/src/putil/bamReader.cxx
  23. 4 0
      panda/src/putil/bamWriter.h
  24. 28 0
      panda/src/putil/bamWriter_ext.cxx
  25. 37 0
      panda/src/putil/bamWriter_ext.h
  26. 16 0
      panda/src/putil/datagramOutputFile.cxx
  27. 1 0
      panda/src/putil/datagramOutputFile.h
  28. 1 0
      panda/src/putil/p3putil_ext_composite.cxx
  29. 6 6
      panda/src/testbed/test_native_net1.py
  30. 3 3
      panda/src/testbed/test_native_net2.py
  31. 2 2
      panda/src/testbed/test_native_net3.py
  32. 53 26
      panda/src/windisplay/winGraphicsWindow.cxx
  33. 2 0
      panda/src/windisplay/winGraphicsWindow.h

+ 3 - 3
contrib/src/panda3dtoolsgui/Panda3DToolsGUI.py

@@ -1905,7 +1905,7 @@ class main(wx.Frame):
                             self.txaExtraLines.append(line)
                             self.txaExtraLines.append(line)
                 txafile.close()
                 txafile.close()
             except:
             except:
-                print "Error opening .txa file!"
+                print("Error opening .txa file!")
             self.palettize_saveTxaTxt.SetValue(os.path.join(dirname + os.sep , filename))
             self.palettize_saveTxaTxt.SetValue(os.path.join(dirname + os.sep , filename))
         dlg.Destroy()
         dlg.Destroy()
 
 
@@ -2705,7 +2705,7 @@ class main(wx.Frame):
             selectedItemIndex = int(self.batchTree.GetItemText(selectedItemId).split()[0])-1
             selectedItemIndex = int(self.batchTree.GetItemText(selectedItemId).split()[0])-1
             batchItem = self.batchList[selectedItemIndex]
             batchItem = self.batchList[selectedItemIndex]
 
 
-            print '\n'+self.BuildCommand(batchItem)
+            print('\n'+self.BuildCommand(batchItem))
 
 
             if (batchItem['cmd'].count('maya2egg')):
             if (batchItem['cmd'].count('maya2egg')):
                 # Display Maya2Egg Tool Panel
                 # Display Maya2Egg Tool Panel
@@ -2840,7 +2840,7 @@ class main(wx.Frame):
                                 self.txaExtraLines.append(line)
                                 self.txaExtraLines.append(line)
                     txafile.close()
                     txafile.close()
                 except:
                 except:
-                    print "Error opening .txa file!"
+                    print("Error opening .txa file!")
 
 
             self.batchItemNameTxt.SetValue(batchItem['label'])
             self.batchItemNameTxt.SetValue(batchItem['label'])
 
 

+ 16 - 0
direct/src/dist/FreezeTool.py

@@ -90,6 +90,7 @@ defaultHiddenImports = {
     'scipy.special._ufuncs': ['scipy.special._ufuncs_cxx'],
     'scipy.special._ufuncs': ['scipy.special._ufuncs_cxx'],
     'scipy.stats._stats': ['scipy.special.cython_special'],
     'scipy.stats._stats': ['scipy.special.cython_special'],
     'setuptools.monkey': ['setuptools.msvc'],
     'setuptools.monkey': ['setuptools.msvc'],
+    'shapely._geometry_helpers': ['shapely._geos'],
 }
 }
 
 
 
 
@@ -2583,6 +2584,21 @@ class PandaModuleFinder(modulefinder.ModuleFinder):
             else:
             else:
                 code = fp.read()
                 code = fp.read()
 
 
+            # Strip out delvewheel patch (see GitHub issue #1492)
+            if isinstance(code, bytes):
+                # Don't look for \n at the end, it may also be \r\n
+                start_marker = b'# start delvewheel patch'
+                end_marker = b'# end delvewheel patch'
+            else:
+                start_marker = '# start delvewheel patch'
+                end_marker = '# end delvewheel patch'
+
+            start = code.find(start_marker)
+            while start >= 0:
+                end = code.find(end_marker, start) + len(end_marker)
+                code = code[:start] + code[end:]
+                start = code.find(start_marker)
+
             code += b'\n' if isinstance(code, bytes) else '\n'
             code += b'\n' if isinstance(code, bytes) else '\n'
             co = compile(code, pathname, 'exec', optimize=self.optimize)
             co = compile(code, pathname, 'exec', optimize=self.optimize)
         elif type == imp.PY_COMPILED:
         elif type == imp.PY_COMPILED:

+ 14 - 1
direct/src/dist/commands.py

@@ -634,7 +634,20 @@ class build_apps(setuptools.Command):
         for index in self.pypi_extra_indexes:
         for index in self.pypi_extra_indexes:
             pip_args += ['--extra-index-url', index]
             pip_args += ['--extra-index-url', index]
 
 
-        subprocess.check_call([sys.executable, '-m', 'pip'] + pip_args)
+        try:
+            subprocess.check_call([sys.executable, '-m', 'pip'] + pip_args)
+        except:
+            # Display a more helpful message for these common issues.
+            if platform.startswith('manylinux2010_') and sys.version_info >= (3, 11):
+                new_platform = platform.replace('manylinux2010_', 'manylinux2014_')
+                self.announce('This error likely occurs because {} is not a supported target as of Python 3.11.\nChange the target platform to {} instead.'.format(platform, new_platform), distutils.log.ERROR)
+            elif platform.startswith('manylinux1_') and sys.version_info >= (3, 10):
+                new_platform = platform.replace('manylinux1_', 'manylinux2014_')
+                self.announce('This error likely occurs because {} is not a supported target as of Python 3.10.\nChange the target platform to {} instead.'.format(platform, new_platform), distutils.log.ERROR)
+            elif platform.startswith('macosx_10_6_') and sys.version_info >= (3, 8):
+                new_platform = platform.replace('macosx_10_6_', 'macosx_10_9_')
+                self.announce('This error likely occurs because {} is not a supported target as of Python 3.8.\nChange the target platform to {} instead.'.format(platform, new_platform), distutils.log.ERROR)
+            raise
 
 
         # Return a list of paths to the downloaded whls
         # Return a list of paths to the downloaded whls
         return [
         return [

+ 17 - 8
dtool/src/dtoolutil/filename.cxx

@@ -1289,9 +1289,12 @@ to_os_long_name() const {
 }
 }
 
 
 /**
 /**
- * Returns true if the filename exists on the disk, false otherwise.  If the
- * type is indicated to be executable, this also tests that the file has
+ * Returns true if the filename exists on the physical disk, false otherwise.
+ * If the type is indicated to be executable, this also tests that the file has
  * execute permission.
  * execute permission.
+ *
+ * @see VirtualFileSystem::exists() for checking whether the filename exists in
+ * the virtual file system.
  */
  */
 bool Filename::
 bool Filename::
 exists() const {
 exists() const {
@@ -1320,8 +1323,11 @@ exists() const {
 }
 }
 
 
 /**
 /**
- * Returns true if the filename exists and is the name of a regular file (i.e.
- * not a directory or device), false otherwise.
+ * Returns true if the filename exists on the physical disk and is the name of
+ * a regular file (i.e. not a directory or device), false otherwise.
+ *
+ * @see VirtualFileSystem::is_regular_file() for checking whether the filename
+ * exists and is a regular file in the virtual file system.
  */
  */
 bool Filename::
 bool Filename::
 is_regular_file() const {
 is_regular_file() const {
@@ -1350,8 +1356,8 @@ is_regular_file() const {
 }
 }
 
 
 /**
 /**
- * Returns true if the filename exists and is either a directory or a regular
- * file that can be written to, or false otherwise.
+ * Returns true if the filename exists on the physical disk and is either a
+ * directory or a regular file that can be written to, or false otherwise.
  */
  */
 bool Filename::
 bool Filename::
 is_writable() const {
 is_writable() const {
@@ -1382,8 +1388,11 @@ is_writable() const {
 }
 }
 
 
 /**
 /**
- * Returns true if the filename exists and is a directory name, false
- * otherwise.
+ * Returns true if the filename exists on the physical disk and is a directory
+ * name, false otherwise.
+ *
+ * @see VirtualFileSystem::is_directory() for checking whether the filename
+ * exists as a directory in the virtual file system.
  */
  */
 bool Filename::
 bool Filename::
 is_directory() const {
 is_directory() const {

+ 5 - 0
dtool/src/dtoolutil/filename.h

@@ -36,6 +36,11 @@ class DSearchPath;
  * convention, and it knows how to perform basic OS-specific I/O, like testing
  * convention, and it knows how to perform basic OS-specific I/O, like testing
  * for file existence and searching a searchpath, as well as the best way to
  * for file existence and searching a searchpath, as well as the best way to
  * open an fstream for reading or writing.
  * open an fstream for reading or writing.
+ *
+ * Note that the methods of Filename that interact with the filesystem (such
+ * as exists(), open_read(), etc.) directly interface with the operating system
+ * and are not aware of Panda's virtual file system.  To interact with the VFS,
+ * use the methods on VirtualFileSystem instead.
  */
  */
 class EXPCL_DTOOL_DTOOLUTIL Filename {
 class EXPCL_DTOOL_DTOOLUTIL Filename {
 PUBLISHED:
 PUBLISHED:

+ 15 - 5
dtool/src/interrogate/functionRemap.cxx

@@ -494,6 +494,10 @@ get_call_str(const string &container, const vector_string &pexprs) const {
       call << separator << "self";
       call << separator << "self";
       separator = ", ";
       separator = ", ";
     }
     }
+    if (_flags & F_explicit_cls) {
+      call << separator << "cls";
+      separator = ", ";
+    }
 
 
     size_t pn;
     size_t pn;
     size_t num_parameters = pexprs.size();
     size_t num_parameters = pexprs.size();
@@ -781,14 +785,20 @@ setup_properties(const InterrogateFunction &ifunc, InterfaceMaker *interface_mak
     first_param = 1;
     first_param = 1;
   }
   }
 
 
-  if (_parameters.size() > first_param && _parameters[first_param]._name == "self" &&
+  if (_parameters.size() > first_param &&
       TypeManager::is_pointer_to_PyObject(_parameters[first_param]._remap->get_orig_type())) {
       TypeManager::is_pointer_to_PyObject(_parameters[first_param]._remap->get_orig_type())) {
     // Here's a special case.  If the first parameter of a nonstatic method
     // Here's a special case.  If the first parameter of a nonstatic method
     // is a PyObject * called "self", then we will automatically fill it in
     // is a PyObject * called "self", then we will automatically fill it in
-    // from the this pointer, and remove it from the generated parameter
-    // list.
-    _parameters.erase(_parameters.begin() + first_param);
-    _flags |= F_explicit_self;
+    // from the this pointer, and remove it from the generated parameter list.
+    // For static methods, we offer "cls" instead, containing the type object.
+    if (_parameters[first_param]._name == "self") {
+      _parameters.erase(_parameters.begin() + first_param);
+      _flags |= F_explicit_self;
+    }
+    else if (!_has_this && _parameters[first_param]._name == "cls") {
+      _parameters.erase(_parameters.begin() + first_param);
+      _flags |= F_explicit_cls;
+    }
   }
   }
 
 
   if (_parameters.size() == first_param) {
   if (_parameters.size() == first_param) {

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

@@ -101,6 +101,7 @@ public:
     F_divide_integer     = 0x2000,
     F_divide_integer     = 0x2000,
     F_hash               = 0x4000,
     F_hash               = 0x4000,
     F_explicit_args      = 0x8000,
     F_explicit_args      = 0x8000,
+    F_explicit_cls       =0x10000,
   };
   };
 
 
   typedef std::vector<Parameter> Parameters;
   typedef std::vector<Parameter> Parameters;

+ 8 - 1
dtool/src/interrogate/interfaceMakerPythonNative.cxx

@@ -1706,7 +1706,11 @@ write_module_class(ostream &out, Object *obj) {
     }
     }
 
 
     if (!func->_has_this) {
     if (!func->_has_this) {
-      flags += " | METH_STATIC";
+      if (func->_flags & FunctionRemap::F_explicit_cls) {
+        flags += " | METH_CLASS";
+      } else {
+        flags += " | METH_STATIC";
+      }
 
 
       // Skip adding this entry if we also have a property with the same name.
       // Skip adding this entry if we also have a property with the same name.
       // In that case, we will use a Dtool_StaticProperty to disambiguate
       // In that case, we will use a Dtool_StaticProperty to disambiguate
@@ -3652,6 +3656,9 @@ write_function_for_top(ostream &out, InterfaceMaker::Object *obj, InterfaceMaker
   if (func->_has_this) {
   if (func->_has_this) {
     prototype += "self";
     prototype += "self";
   }
   }
+  else if (func->_flags & FunctionRemap::F_explicit_cls) {
+    prototype += "cls";
+  }
 
 
   switch (func->_args_type) {
   switch (func->_args_type) {
   case AT_keyword_args:
   case AT_keyword_args:

+ 5 - 7
panda/src/display/graphicsOutput.cxx

@@ -1150,8 +1150,9 @@ clear_pipe() {
 
 
 /**
 /**
  * Changes the x_size and y_size, then recalculates structures that depend on
  * Changes the x_size and y_size, then recalculates structures that depend on
- * size.  The recalculation currently includes: - compute_pixels on all the
- * graphics regions.  - updating the texture card, if one is present.
+ * size.  The recalculation currently includes:
+ *  - compute_pixels on all the graphics regions.
+ *  - updating the texture card, if one is present.
  */
  */
 void GraphicsOutput::
 void GraphicsOutput::
 set_size_and_recalc(int x, int y) {
 set_size_and_recalc(int x, int y) {
@@ -1163,11 +1164,8 @@ set_size_and_recalc(int x, int y) {
   int fb_x_size = get_fb_x_size();
   int fb_x_size = get_fb_x_size();
   int fb_y_size = get_fb_y_size();
   int fb_y_size = get_fb_y_size();
 
 
-  TotalDisplayRegions::iterator dri;
-  for (dri = _total_display_regions.begin();
-       dri != _total_display_regions.end();
-       ++dri) {
-    (*dri)->compute_pixels_all_stages(fb_x_size, fb_y_size);
+  for (DisplayRegion *dr : _total_display_regions) {
+    dr->compute_pixels_all_stages(fb_x_size, fb_y_size);
   }
   }
 
 
   if (_texture_card != nullptr && _texture_card->get_num_geoms() > 0) {
   if (_texture_card != nullptr && _texture_card->get_num_geoms() > 0) {

+ 28 - 3
panda/src/egldisplay/eglGraphicsBuffer.cxx

@@ -38,9 +38,10 @@ eglGraphicsBuffer(GraphicsEngine *engine, GraphicsPipe *pipe,
   DCAST_INTO_V(egl_pipe, _pipe);
   DCAST_INTO_V(egl_pipe, _pipe);
   _pbuffer = EGL_NO_SURFACE;
   _pbuffer = EGL_NO_SURFACE;
 
 
-  // Since the pbuffer never gets flipped, we get screenshots from the same
-  // buffer we draw into.
-  _screenshot_buffer_type = _draw_buffer_type;
+  // EGL pbuffers only have a back buffer (see 2.2.2 in spec), and it is never
+  // flipped (eglSwapBuffers is a no-op).
+  _draw_buffer_type = RenderBuffer::T_back;
+  _screenshot_buffer_type = RenderBuffer::T_back;
 }
 }
 
 
 /**
 /**
@@ -119,6 +120,30 @@ end_frame(FrameMode mode, Thread *current_thread) {
   }
   }
 }
 }
 
 
+/**
+ *
+ */
+void eglGraphicsBuffer::
+set_size(int x, int y) {
+  nassertv_always(_gsg != nullptr);
+
+  if (_size.get_x() != x || _size.get_y() != y) {
+    eglDestroySurface(_egl_display, _pbuffer);
+
+    int attrib_list[] = {
+      EGL_WIDTH, x,
+      EGL_HEIGHT, y,
+      EGL_NONE
+    };
+
+    eglGraphicsStateGuardian *eglgsg;
+    DCAST_INTO_V(eglgsg, _gsg);
+    _pbuffer = eglCreatePbufferSurface(eglgsg->_egl_display, eglgsg->_fbconfig, attrib_list);
+  }
+
+  set_size_and_recalc(x, y);
+}
+
 /**
 /**
  * Closes the buffer right now.  Called from the window thread.
  * Closes the buffer right now.  Called from the window thread.
  */
  */

+ 2 - 0
panda/src/egldisplay/eglGraphicsBuffer.h

@@ -36,6 +36,8 @@ public:
   virtual bool begin_frame(FrameMode mode, Thread *current_thread);
   virtual bool begin_frame(FrameMode mode, Thread *current_thread);
   virtual void end_frame(FrameMode mode, Thread *current_thread);
   virtual void end_frame(FrameMode mode, Thread *current_thread);
 
 
+  virtual void set_size(int x, int y);
+
 protected:
 protected:
   virtual void close_buffer();
   virtual void close_buffer();
   virtual bool open_buffer();
   virtual bool open_buffer();

+ 0 - 1
panda/src/egldisplay/eglGraphicsPipe.cxx

@@ -323,7 +323,6 @@ make_output(const std::string &name,
   if (retry == 2) {
   if (retry == 2) {
     if (((flags&BF_require_parasite)!=0)||
     if (((flags&BF_require_parasite)!=0)||
         ((flags&BF_require_window)!=0)||
         ((flags&BF_require_window)!=0)||
-        ((flags&BF_resizeable)!=0)||
         ((flags&BF_size_track_host)!=0)) {
         ((flags&BF_size_track_host)!=0)) {
       return nullptr;
       return nullptr;
     }
     }

+ 6 - 5
panda/src/express/virtualFileSystem.I

@@ -12,7 +12,8 @@
  */
  */
 
 
 /**
 /**
- * Convenience function; returns true if the named file exists.
+ * Convenience function; returns true if the named file exists in the virtual
+ * file system hierarchy.
  */
  */
 INLINE bool VirtualFileSystem::
 INLINE bool VirtualFileSystem::
 exists(const Filename &filename) const {
 exists(const Filename &filename) const {
@@ -20,8 +21,8 @@ exists(const Filename &filename) const {
 }
 }
 
 
 /**
 /**
- * Convenience function; returns true if the named file exists and is a
- * directory.
+ * Convenience function; returns true if the named file exists as a directory in
+ * the virtual file system hierarchy.
  */
  */
 INLINE bool VirtualFileSystem::
 INLINE bool VirtualFileSystem::
 is_directory(const Filename &filename) const {
 is_directory(const Filename &filename) const {
@@ -30,8 +31,8 @@ is_directory(const Filename &filename) const {
 }
 }
 
 
 /**
 /**
- * Convenience function; returns true if the named file exists and is a
- * regular file.
+ * Convenience function; returns true if the named file exists as a regular file
+ * in the virtual file system hierarchy.
  */
  */
 INLINE bool VirtualFileSystem::
 INLINE bool VirtualFileSystem::
 is_regular_file(const Filename &filename) const {
 is_regular_file(const Filename &filename) const {

+ 3 - 3
panda/src/iphone/ipfreeze.py

@@ -17,8 +17,8 @@ import os
 from direct.dist import FreezeTool
 from direct.dist import FreezeTool
 
 
 def usage(code, msg = ''):
 def usage(code, msg = ''):
-    print >> sys.stderr, __doc__
-    print >> sys.stderr, msg
+    print(__doc__, file=sys.stderr)
+    print(msg, file=sys.stderr)
     sys.exit(code)
     sys.exit(code)
 
 
 if __name__ == '__main__':
 if __name__ == '__main__':
@@ -30,7 +30,7 @@ if __name__ == '__main__':
 
 
     try:
     try:
         opts, args = getopt.getopt(sys.argv[1:], 'h')
         opts, args = getopt.getopt(sys.argv[1:], 'h')
-    except getopt.error, msg:
+    except getopt.error as msg:
         usage(1, msg)
         usage(1, msg)
 
 
     for opt, arg in opts:
     for opt, arg in opts:

+ 1 - 1
panda/src/iphone/provision.py

@@ -34,7 +34,7 @@ command = 'env CODESIGN_ALLOCATE="/Developer/Platforms/iPhoneOS.platform/Develop
     'xcent' : xcent,
     'xcent' : xcent,
     }
     }
 
 
-print command
+print(command)
 result = os.system(command)
 result = os.system(command)
 if result != 0:
 if result != 0:
     raise StandardError
     raise StandardError

+ 2 - 0
panda/src/pgraph/CMakeLists.txt

@@ -204,6 +204,8 @@ set(P3PGRAPH_SOURCES
 )
 )
 
 
 set(P3PGRAPH_IGATEEXT
 set(P3PGRAPH_IGATEEXT
+  bamFile_ext.cxx
+  bamFile_ext.h
   loaderFileTypeRegistry_ext.cxx
   loaderFileTypeRegistry_ext.cxx
   loaderFileTypeRegistry_ext.h
   loaderFileTypeRegistry_ext.h
   nodePathCollection_ext.cxx
   nodePathCollection_ext.cxx

+ 4 - 0
panda/src/pgraph/bamFile.h

@@ -74,7 +74,11 @@ PUBLISHED:
   BamReader *get_reader();
   BamReader *get_reader();
   BamWriter *get_writer();
   BamWriter *get_writer();
 
 
+public:
+  EXTENSION(PyObject *get_file_version() const);
+
 PUBLISHED:
 PUBLISHED:
+  MAKE_PROPERTY(file_version, get_file_version);
   MAKE_PROPERTY(file_endian, get_file_endian);
   MAKE_PROPERTY(file_endian, get_file_endian);
   MAKE_PROPERTY(file_stdfloat_double, get_file_stdfloat_double);
   MAKE_PROPERTY(file_stdfloat_double, get_file_stdfloat_double);
 
 

+ 28 - 0
panda/src/pgraph/bamFile_ext.cxx

@@ -0,0 +1,28 @@
+/**
+ * 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 bamFile_ext.cxx
+ * @author rdb
+ * @date 2023-05-03
+ */
+
+#include "bamFile_ext.h"
+#include "bamWriter_ext.h"
+
+#ifdef HAVE_PYTHON
+
+/**
+ * Returns the version number of the Bam file currently being written.
+ */
+PyObject *Extension<BamFile>::
+get_file_version() const {
+  return Py_BuildValue("(ii)", _this->get_file_major_ver(),
+                               _this->get_file_minor_ver());
+}
+
+#endif

+ 37 - 0
panda/src/pgraph/bamFile_ext.h

@@ -0,0 +1,37 @@
+/**
+ * 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 bamFile_ext.h
+ * @author rdb
+ * @date 2023-05-03
+ */
+
+#ifndef BAMFILE_EXT_H
+#define BAMFILE_EXT_H
+
+#include "dtoolbase.h"
+
+#ifdef HAVE_PYTHON
+
+#include "extension.h"
+#include "bamFile.h"
+#include "py_panda.h"
+
+/**
+ * This class defines the extension methods for BamFile, which are called
+ * instead of any C++ methods with the same prototype.
+ */
+template<>
+class Extension<BamFile> : public ExtensionBase<BamFile> {
+public:
+  PyObject *get_file_version() const;
+};
+
+#endif  // HAVE_PYTHON
+
+#endif  // BAMFILE_EXT_H

+ 1 - 0
panda/src/pgraph/p3pgraph_ext_composite.cxx

@@ -1,3 +1,4 @@
+#include "bamFile_ext.cxx"
 #include "loaderFileTypeRegistry_ext.cxx"
 #include "loaderFileTypeRegistry_ext.cxx"
 #include "nodePath_ext.cxx"
 #include "nodePath_ext.cxx"
 #include "nodePathCollection_ext.cxx"
 #include "nodePathCollection_ext.cxx"

+ 2 - 0
panda/src/putil/CMakeLists.txt

@@ -125,6 +125,8 @@ set(P3PUTIL_SOURCES
 set(P3PUTIL_IGATEEXT
 set(P3PUTIL_IGATEEXT
   bamReader_ext.cxx
   bamReader_ext.cxx
   bamReader_ext.h
   bamReader_ext.h
+  bamWriter_ext.cxx
+  bamWriter_ext.h
   bitArray_ext.cxx
   bitArray_ext.cxx
   bitArray_ext.h
   bitArray_ext.h
   bitArray_ext.I
   bitArray_ext.I

+ 1 - 1
panda/src/putil/bamReader.cxx

@@ -315,7 +315,7 @@ read_object(TypedWritable *&ptr, ReferenceCount *&ref_ptr) {
  * time to call it.
  * time to call it.
  *
  *
  * This must be called at least once after reading a particular object via
  * This must be called at least once after reading a particular object via
- * get_object() in order to validate that object.
+ * read_object() in order to validate that object.
  *
  *
  * The return value is true if all objects have been resolved, or false if
  * The return value is true if all objects have been resolved, or false if
  * some objects are still outstanding (in which case you will need to call
  * some objects are still outstanding (in which case you will need to call

+ 4 - 0
panda/src/putil/bamWriter.h

@@ -87,9 +87,13 @@ PUBLISHED:
   INLINE TypedWritable *get_root_node() const;
   INLINE TypedWritable *get_root_node() const;
   INLINE void set_root_node(TypedWritable *root_node);
   INLINE void set_root_node(TypedWritable *root_node);
 
 
+public:
+  EXTENSION(PyObject *get_file_version() const);
+
 PUBLISHED:
 PUBLISHED:
   MAKE_PROPERTY(target, get_target, set_target);
   MAKE_PROPERTY(target, get_target, set_target);
   MAKE_PROPERTY(filename, get_filename);
   MAKE_PROPERTY(filename, get_filename);
+  MAKE_PROPERTY(file_version, get_file_version);
   MAKE_PROPERTY(file_endian, get_file_endian);
   MAKE_PROPERTY(file_endian, get_file_endian);
   MAKE_PROPERTY(file_stdfloat_double, get_file_stdfloat_double);
   MAKE_PROPERTY(file_stdfloat_double, get_file_stdfloat_double);
   MAKE_PROPERTY(file_texture_mode, get_file_texture_mode);
   MAKE_PROPERTY(file_texture_mode, get_file_texture_mode);

+ 28 - 0
panda/src/putil/bamWriter_ext.cxx

@@ -0,0 +1,28 @@
+/**
+ * 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 bamWriter_ext.cxx
+ * @author rdb
+ * @date 2023-05-03
+ */
+
+#include "bamWriter_ext.h"
+#include "config_putil.h"
+
+#ifdef HAVE_PYTHON
+
+/**
+ * Returns the version number of the Bam file currently being written.
+ */
+PyObject *Extension<BamWriter>::
+get_file_version() const {
+  return Py_BuildValue("(ii)", _this->get_file_major_ver(),
+                               _this->get_file_minor_ver());
+}
+
+#endif

+ 37 - 0
panda/src/putil/bamWriter_ext.h

@@ -0,0 +1,37 @@
+/**
+ * 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 bamWriter_ext.h
+ * @author rdb
+ * @date 2023-05-01
+ */
+
+#ifndef BAMWRITER_EXT_H
+#define BAMWRITER_EXT_H
+
+#include "dtoolbase.h"
+
+#ifdef HAVE_PYTHON
+
+#include "extension.h"
+#include "bamWriter.h"
+#include "py_panda.h"
+
+/**
+ * This class defines the extension methods for BamWriter, which are called
+ * instead of any C++ methods with the same prototype.
+ */
+template<>
+class Extension<BamWriter> : public ExtensionBase<BamWriter> {
+public:
+  PyObject *get_file_version() const;
+};
+
+#endif  // HAVE_PYTHON
+
+#endif  // BAMWRITER_EXT_H

+ 16 - 0
panda/src/putil/datagramOutputFile.cxx

@@ -86,6 +86,22 @@ close() {
   _error = false;
   _error = false;
 }
 }
 
 
+/**
+ * Writes a sequence of bytes to the beginning of the datagram file.  This may
+ * be called any number of times after the file has been opened and before the
+ * first datagram is written.  It may not be called once the first datagram is
+ * written.
+ */
+bool DatagramOutputFile::
+write_header(const vector_uchar &header) {
+  nassertr(_out != nullptr, false);
+  nassertr(!_wrote_first_datagram, false);
+
+  _out->write((const char *)&header[0], header.size());
+  thread_consider_yield();
+  return !_out->fail();
+}
+
 /**
 /**
  * Writes a sequence of bytes to the beginning of the datagram file.  This may
  * Writes a sequence of bytes to the beginning of the datagram file.  This may
  * be called any number of times after the file has been opened and before the
  * be called any number of times after the file has been opened and before the

+ 1 - 0
panda/src/putil/datagramOutputFile.h

@@ -38,6 +38,7 @@ PUBLISHED:
 
 
   void close();
   void close();
 
 
+  bool write_header(const vector_uchar &header);
   bool write_header(const std::string &header);
   bool write_header(const std::string &header);
   virtual bool put_datagram(const Datagram &data);
   virtual bool put_datagram(const Datagram &data);
   virtual bool copy_datagram(SubfileInfo &result, const Filename &filename);
   virtual bool copy_datagram(SubfileInfo &result, const Filename &filename);

+ 1 - 0
panda/src/putil/p3putil_ext_composite.cxx

@@ -1,4 +1,5 @@
 #include "bamReader_ext.cxx"
 #include "bamReader_ext.cxx"
+#include "bamWriter_ext.cxx"
 #include "bitArray_ext.cxx"
 #include "bitArray_ext.cxx"
 #include "paramPyObject.cxx"
 #include "paramPyObject.cxx"
 #include "pythonCallbackObject.cxx"
 #include "pythonCallbackObject.cxx"

+ 6 - 6
panda/src/testbed/test_native_net1.py

@@ -11,7 +11,7 @@ SocketIP.InitNetworkDriver();
 
 
 addr = SocketAddress()
 addr = SocketAddress()
 addr.setHost("127.0.0.1",8080)
 addr.setHost("127.0.0.1",8080)
-print addr.getIpPort()
+print(addr.getIpPort())
 
 
 
 
 inbound = SocketTCPListen()
 inbound = SocketTCPListen()
@@ -23,16 +23,16 @@ while 1 == 1:
     source = SocketAddress()
     source = SocketAddress()
     if inbound.GetIncomingConnection(newsession,source) :
     if inbound.GetIncomingConnection(newsession,source) :
         #newsession.SetNonBlocking();
         #newsession.SetNonBlocking();
-        print source.getIpPort()
+        print(source.getIpPort())
         newsession.SendData("Hello From the Listener\n\r");
         newsession.SendData("Hello From the Listener\n\r");
 
 
         s = newsession.RecvData(10);
         s = newsession.RecvData(10);
-        print s
-        print newsession.GetLastError()
+        print(s)
+        print(newsession.GetLastError())
         if newsession.ErrorIsWouldBlocking(newsession.GetLastError()) :
         if newsession.ErrorIsWouldBlocking(newsession.GetLastError()) :
-            print  "Reading Would Block"
+            print("Reading Would Block")
         else:
         else:
-            print "Not A Blocking Error"
+            print("Not A Blocking Error")
 
 
         newsession.SendData("GoodBy From the Listener\n\r");
         newsession.SendData("GoodBy From the Listener\n\r");
         newsession.Close();
         newsession.Close();

+ 3 - 3
panda/src/testbed/test_native_net2.py

@@ -15,7 +15,7 @@ SocketIP.InitNetworkDriver();
 
 
 addr = SocketAddress()
 addr = SocketAddress()
 addr.setHost("127.0.0.1",6666)
 addr.setHost("127.0.0.1",6666)
-print addr.getIpPort()
+print(addr.getIpPort())
 
 
 MyConection = BufferedDatagramConnection(0,4096000,4096000,102400);
 MyConection = BufferedDatagramConnection(0,4096000,4096000,102400);
 #help(BufferedDatagramConnection)
 #help(BufferedDatagramConnection)
@@ -42,8 +42,8 @@ dg1.addUint16(54321)
 while 1==1:
 while 1==1:
     for x in range(200000):
     for x in range(200000):
         if  not MyConection.SendMessage(dg1):
         if  not MyConection.SendMessage(dg1):
-            print "Error Sending Message"
+            print("Error Sending Message")
 
 
     MyConection.Flush();
     MyConection.Flush();
     time.sleep(1)
     time.sleep(1)
-    print "loop"
+    print("loop")

+ 2 - 2
panda/src/testbed/test_native_net3.py

@@ -15,7 +15,7 @@ SocketIP.InitNetworkDriver();
 
 
 addr = SocketAddress()
 addr = SocketAddress()
 addr.setHost("127.0.0.1",6666)
 addr.setHost("127.0.0.1",6666)
-print addr.getIpPort()
+print(addr.getIpPort())
 
 
 MyConection = BufferedDatagramConnection(0,4096000,4096000,102400);
 MyConection = BufferedDatagramConnection(0,4096000,4096000,102400);
 #help(BufferedDatagramConnection)
 #help(BufferedDatagramConnection)
@@ -47,4 +47,4 @@ while 1==1:
 
 
     MyConection.Flush();
     MyConection.Flush();
     time.sleep(1)
     time.sleep(1)
-    print "loop"
+    print("loop")

+ 53 - 26
panda/src/windisplay/winGraphicsWindow.cxx

@@ -272,15 +272,34 @@ process_events() {
  */
  */
 void WinGraphicsWindow::
 void WinGraphicsWindow::
 set_properties_now(WindowProperties &properties) {
 set_properties_now(WindowProperties &properties) {
-  if (properties.has_fullscreen() && !properties.get_fullscreen() &&
-      is_fullscreen()) {
-    if (do_windowed_switch()) {
-      _properties.set_fullscreen(false);
-      properties.clear_fullscreen();
-    } else {
-      windisplay_cat.warning()
-        << "Switching to windowed mode failed!\n";
+  if (properties.has_fullscreen()) {
+    if (!properties.get_fullscreen() && is_fullscreen()) {
+      if (do_windowed_switch()) {
+        _properties.set_fullscreen(false);
+      } else {
+        windisplay_cat.warning()
+          << "Switching to windowed mode failed!\n";
+      }
     }
     }
+    else if (properties.get_fullscreen() && !is_fullscreen()) {
+      int x_size;
+      int y_size;
+      if (properties.has_size()) {
+        x_size = properties.get_x_size();
+        y_size = properties.get_y_size();
+      } else {
+        x_size = _properties.get_x_size();
+        y_size = _properties.get_y_size();
+      }
+      if (do_fullscreen_switch(x_size, y_size)) {
+        _properties.set_fullscreen(true);
+        properties.clear_size();
+      } else {
+        windisplay_cat.warning()
+          << "Switching to fullscreen mode failed!\n";
+      }
+    }
+    properties.clear_fullscreen();
   }
   }
 
 
   GraphicsWindow::set_properties_now(properties);
   GraphicsWindow::set_properties_now(properties);
@@ -974,8 +993,8 @@ do_fullscreen_resize(int x_size, int y_size) {
  * Called in the set_properties_now function to switch to fullscreen.
  * Called in the set_properties_now function to switch to fullscreen.
  */
  */
 bool WinGraphicsWindow::
 bool WinGraphicsWindow::
-do_fullscreen_switch() {
-  if (!do_fullscreen_enable()) {
+do_fullscreen_switch(int x_size, int y_size) {
+  if (!do_fullscreen_enable(x_size, y_size)) {
     // Couldn't get fullscreen.
     // Couldn't get fullscreen.
     return false;
     return false;
   }
   }
@@ -985,17 +1004,21 @@ do_fullscreen_switch() {
   DWORD window_style = make_style(props);
   DWORD window_style = make_style(props);
   SetWindowLong(_hWnd, GWL_STYLE, window_style);
   SetWindowLong(_hWnd, GWL_STYLE, window_style);
 
 
-  WINDOW_METRICS metrics;
-  bool has_origin;
-  if (!calculate_metrics(true, window_style, metrics, has_origin)){
-    return false;
-  }
-
-  SetWindowPos(_hWnd, HWND_NOTOPMOST, 0, 0, metrics.width, metrics.height,
+  SetWindowPos(_hWnd, HWND_NOTOPMOST, 0, 0, x_size, y_size,
     SWP_FRAMECHANGED | SWP_SHOWWINDOW);
     SWP_FRAMECHANGED | SWP_SHOWWINDOW);
+
+  handle_reshape();
   return true;
   return true;
 }
 }
 
 
+/**
+ * Called in the set_properties_now function to switch to fullscreen.
+ */
+bool WinGraphicsWindow::
+do_fullscreen_switch() {
+  return do_fullscreen_switch(_properties.get_x_size(), _properties.get_y_size());
+}
+
 /**
 /**
  * Called in the set_properties_now function to switch to windowed mode.
  * Called in the set_properties_now function to switch to windowed mode.
  */
  */
@@ -1248,8 +1271,8 @@ open_graphic_window() {
   // somehow, but I need the window's black background to cover up the desktop
   // somehow, but I need the window's black background to cover up the desktop
   // during the mode change.
   // during the mode change.
 
 
-  if (fullscreen){
-    if (!do_fullscreen_enable()){
+  if (fullscreen) {
+    if (!do_fullscreen_enable()) {
       return false;
       return false;
     }
     }
   }
   }
@@ -1262,7 +1285,7 @@ open_graphic_window() {
  * Not to confuse with do_fullscreen_switch().
  * Not to confuse with do_fullscreen_switch().
  */
  */
 bool WinGraphicsWindow::
 bool WinGraphicsWindow::
-do_fullscreen_enable() {
+do_fullscreen_enable(int x_size, int y_size) {
 
 
   HWND hDesktopWindow = GetDesktopWindow();
   HWND hDesktopWindow = GetDesktopWindow();
   HDC scrnDC = GetDC(hDesktopWindow);
   HDC scrnDC = GetDC(hDesktopWindow);
@@ -1272,8 +1295,8 @@ do_fullscreen_enable() {
   // GetDeviceCaps(scrnDC, VERTRES);
   // GetDeviceCaps(scrnDC, VERTRES);
   ReleaseDC(hDesktopWindow, scrnDC);
   ReleaseDC(hDesktopWindow, scrnDC);
 
 
-  DWORD dwWidth = _properties.get_x_size();
-  DWORD dwHeight = _properties.get_y_size();
+  DWORD dwWidth = x_size;
+  DWORD dwHeight = y_size;
   DWORD dwFullScreenBitDepth = cur_bitdepth;
   DWORD dwFullScreenBitDepth = cur_bitdepth;
 
 
   DEVMODE dm;
   DEVMODE dm;
@@ -1301,12 +1324,16 @@ do_fullscreen_enable() {
   }
   }
 
 
   _fullscreen_display_mode = dm;
   _fullscreen_display_mode = dm;
-
-  _properties.set_origin(0, 0);
-  _properties.set_size(dwWidth, dwHeight);
-
   return true;
   return true;
+}
 
 
+/**
+ * This is a low-level function that just puts Windows in fullscreen mode.
+ * Not to confuse with do_fullscreen_switch().
+ */
+bool WinGraphicsWindow::
+do_fullscreen_enable() {
+  return do_fullscreen_enable(_properties.get_x_size(), _properties.get_y_size());
 }
 }
 
 
 /**
 /**

+ 2 - 0
panda/src/windisplay/winGraphicsWindow.h

@@ -109,8 +109,10 @@ protected:
   virtual void handle_reshape();
   virtual void handle_reshape();
   virtual bool do_fullscreen_resize(int x_size, int y_size);
   virtual bool do_fullscreen_resize(int x_size, int y_size);
 
 
+  bool do_fullscreen_switch(int x_size, int y_size);
   virtual bool do_fullscreen_switch();
   virtual bool do_fullscreen_switch();
   virtual bool do_windowed_switch();
   virtual bool do_windowed_switch();
+  bool do_fullscreen_enable(int x_size, int y_size);
   virtual bool do_fullscreen_enable();
   virtual bool do_fullscreen_enable();
   virtual bool do_fullscreen_disable();
   virtual bool do_fullscreen_disable();