Преглед изворни кода

Merge branch 'master' into ios

Donny Lawrence пре 5 година
родитељ
комит
7db1b5b574
55 измењених фајлова са 1186 додато и 509 уклоњено
  1. 1 1
      .editorconfig
  2. 1 1
      .github/codecov.yml
  3. 2 0
      direct/src/dcparser/dcClass.cxx
  4. 3 2
      direct/src/dcparser/dcClass.h
  5. 3 0
      direct/src/dcparser/dcLexer.cxx.prebuilt
  6. 3 1
      direct/src/dcparser/dcLexer.lxx
  7. 2 0
      direct/src/dcparser/dcbase.h
  8. 134 15
      direct/src/dist/FreezeTool.py
  9. 0 7
      direct/src/dist/commands.py
  10. 5 5
      direct/src/showbase/DirectObject.py
  11. 0 1
      direct/src/showbase/ProfileSession.py
  12. 1 1
      dtool/Package.cmake
  13. 432 353
      dtool/src/cppparser/cppBison.cxx.prebuilt
  14. 6 2
      dtool/src/cppparser/cppBison.h.prebuilt
  15. 13 37
      dtool/src/cppparser/cppBison.yxx
  16. 47 9
      dtool/src/cppparser/cppExpression.cxx
  17. 3 1
      dtool/src/cppparser/cppExpression.h
  18. 11 1
      dtool/src/cppparser/cppInstance.cxx
  19. 1 1
      dtool/src/cppparser/cppInstanceIdentifier.cxx
  20. 2 2
      dtool/src/cppparser/cppInstanceIdentifier.h
  21. 15 11
      dtool/src/parser-inc/map
  22. 2 0
      dtool/src/parser-inc/ostream
  23. 9 8
      dtool/src/parser-inc/string
  24. 10 6
      dtool/src/parser-inc/vector
  25. 1 1
      makepanda/makepanda.py
  26. 1 0
      panda/src/cocoadisplay/cocoaGraphicsWindow.h
  27. 52 2
      panda/src/cocoadisplay/cocoaGraphicsWindow.mm
  28. 2 1
      panda/src/collide/collisionPolygon.h
  29. 7 0
      panda/src/display/config_display.cxx
  30. 1 0
      panda/src/display/config_display.h
  31. 1 0
      panda/src/display/graphicsWindow.cxx
  32. 39 0
      panda/src/display/windowProperties.I
  33. 7 0
      panda/src/display/windowProperties.cxx
  34. 9 0
      panda/src/display/windowProperties.h
  35. 3 3
      panda/src/downloader/httpClient.I
  36. 4 4
      panda/src/grutil/meshDrawer2D.I
  37. 2 2
      panda/src/grutil/meshDrawer2D.h
  38. 12 4
      panda/src/nativenet/buffered_datagramconnection.h
  39. 7 5
      panda/src/pgraph/geomNode.cxx
  40. 6 0
      panda/src/pgraph/pandaNode_ext.cxx
  41. 9 0
      panda/src/pgui/pgItem.h
  42. 5 5
      panda/src/putil/bitArray.cxx
  43. 60 14
      panda/src/windisplay/winGraphicsWindow.cxx
  44. 2 0
      panda/src/x11display/x11GraphicsPipe.cxx
  45. 2 0
      panda/src/x11display/x11GraphicsPipe.h
  46. 75 1
      panda/src/x11display/x11GraphicsWindow.cxx
  47. 3 0
      pandatool/src/deploy-stub/deploy-stub.c
  48. 0 1
      samples/fireflies/light.sha
  49. 20 0
      tests/display/test_winprops.py
  50. 18 0
      tests/pgraph/test_cullfaceattrib.py
  51. 34 0
      tests/pgraph/test_lightattrib.py
  52. 29 0
      tests/pgraph/test_nodepath.py
  53. 5 1
      tests/pgraph/test_shaderattrib.py
  54. 47 0
      tests/pgraph/test_textureattrib.py
  55. 17 0
      tests/putil/test_bitarray.py

+ 1 - 1
.editorconfig

@@ -10,7 +10,7 @@ insert_final_newline = true
 indent_style = space
 indent_size = 4
 
-[*.{h,c,cxx,cpp,I}]
+[*.{h,c,cxx,cpp,I,T}]
 indent_style = space
 indent_size = 2
 

+ 1 - 1
.github/codecov.yml

@@ -5,7 +5,7 @@ coverage:
         threshold: 0.1
     patch:
       default:
-        threshold: 0.1
+        target: 0%
 codecov:
   require_ci_to_pass: true
   notify:

+ 2 - 0
direct/src/dcparser/dcClass.cxx

@@ -77,7 +77,9 @@ DCClass(DCFile *dc_file, const string &name, bool is_struct, bool bogus_class) :
   _number = -1;
   _constructor = nullptr;
 
+#ifdef WITHIN_PANDA
   _python_class_defs = nullptr;
+#endif
 }
 
 /**

+ 3 - 2
direct/src/dcparser/dcClass.h

@@ -172,6 +172,9 @@ private:
   typedef pmap<int, DCField *> FieldsByIndex;
   FieldsByIndex _fields_by_index;
 
+  friend class DCField;
+
+#ifdef WITHIN_PANDA
   // See pandaNode.h for an explanation of this trick
   class PythonClassDefs : public ReferenceCount {
   public:
@@ -179,8 +182,6 @@ private:
   };
   PT(PythonClassDefs) _python_class_defs;
 
-  friend class DCField;
-#ifdef WITHIN_PANDA
   friend class Extension<DCClass>;
 #endif
 };

+ 3 - 0
direct/src/dcparser/dcLexer.cxx.prebuilt

@@ -599,7 +599,10 @@ char *dcyytext;
 #include "dcParser.h"
 #include "dcFile.h"
 #include "dcindent.h"
+
+#ifdef WITHIN_PANDA
 #include "pstrtod.h"
+#endif
 
 
 static int yyinput(void);        // declared by flex.

+ 3 - 1
direct/src/dcparser/dcLexer.lxx

@@ -11,8 +11,10 @@
 #include "dcParser.h"
 #include "dcFile.h"
 #include "dcindent.h"
-#include "pstrtod.h"
 
+#ifdef WITHIN_PANDA
+#include "pstrtod.h"
+#endif
 
 static int yyinput(void);        // declared by flex.
 extern "C" int dcyywrap();

+ 2 - 0
direct/src/dcparser/dcbase.h

@@ -70,6 +70,7 @@
 #define BEGIN_PUBLISH
 #define END_PUBLISH
 #define BLOCKING
+#define EXTENSION(x)
 
 // These control the declspec(dllexport/dllimport) on Windows.  When compiling
 // outside of Panda, we assume we aren't part of a DLL.
@@ -100,6 +101,7 @@ typedef std::string Filename;
 #define pmap std::map
 #define pset std::set
 #define vector_uchar std::vector<unsigned char>
+#define patof(x) atof(x)
 
 #include <stdint.h>
 #include <string.h>

+ 134 - 15
direct/src/dist/FreezeTool.py

@@ -639,6 +639,51 @@ okMissing = [
     'direct.extensions_native.extensions_darwin',
     ]
 
+# Since around macOS 10.15, Apple's codesigning process has become more strict.
+# Appending data to the end of a Mach-O binary is now explicitly forbidden. The
+# solution is to embed our own segment into the binary so it can be properly
+# signed.
+mach_header_64_layout = '<IIIIIIII'
+
+# Each load command is guaranteed to start with the command identifier and
+# command size. We'll call this the "lc header".
+lc_header_layout = '<II'
+
+# Each Mach-O segment is made up of sections. We need to change both the segment
+# and section information, so we'll need to know the layout of a section as
+# well.
+section64_header_layout = '<16s16sQQIIIIIIII'
+
+# These are all of the load commands we'll need to modify parts of.
+LC_SEGMENT_64 = 0x19
+LC_DYLD_INFO_ONLY = 0x80000022
+LC_SYMTAB = 0x02
+LC_DYSYMTAB = 0x0B
+LC_FUNCTION_STARTS = 0x26
+LC_DATA_IN_CODE = 0x29
+
+lc_layouts = {
+    LC_SEGMENT_64: '<II16sQQQQIIII',
+    LC_DYLD_INFO_ONLY: '<IIIIIIIIIIII',
+    LC_SYMTAB: '<IIIIII',
+    LC_DYSYMTAB: '<IIIIIIIIIIIIIIIIIIII',
+    LC_FUNCTION_STARTS: '<IIII',
+    LC_DATA_IN_CODE: '<IIII',
+}
+
+# All of our modifications involve sliding some offsets, since we need to insert
+# our data in the middle of the binary (we can't just put the data at the end
+# since __LINKEDIT must be the last segment).
+lc_indices_to_slide = {
+    b'__PANDA': [4, 6],
+    b'__LINKEDIT': [3, 5],
+    LC_DYLD_INFO_ONLY: [2, 4, 8, 10],
+    LC_SYMTAB: [2, 4],
+    LC_DYSYMTAB: [14],
+    LC_FUNCTION_STARTS: [2],
+    LC_DATA_IN_CODE: [2],
+}
+
 class Freezer:
     class ModuleDef:
         def __init__(self, moduleName, filename = None,
@@ -1799,21 +1844,36 @@ class Freezer:
             # Align to page size, so that it can be mmapped.
             blob_align = 4096
 
-        # Add padding before the blob if necessary.
-        blob_offset = len(stub_data)
-        if (blob_offset & (blob_align - 1)) != 0:
-            pad = (blob_align - (blob_offset & (blob_align - 1)))
-            stub_data += (b'\0' * pad)
-            blob_offset += pad
-        assert (blob_offset % blob_align) == 0
-        assert blob_offset == len(stub_data)
-
         # Also determine the total blob size now.  Add padding to the end.
         blob_size = pool_offset + len(pool)
-        if blob_size & 31 != 0:
-            pad = (32 - (blob_size & 31))
+        if blob_size & (blob_align - 1) != 0:
+            pad = (blob_align - (blob_size & (blob_align - 1)))
             blob_size += pad
 
+        # TODO: Support creating custom sections in universal binaries.
+        append_blob = True
+        if self.platform.startswith('macosx') and len(bitnesses) == 1:
+            # If our deploy-stub has a __PANDA segment, we know we're meant to
+            # put our blob there rather than attach it to the end.
+            load_commands = self._parse_macho_load_commands(stub_data)
+            if b'__PANDA' in load_commands.keys():
+                append_blob = False
+
+        if self.platform.startswith("macosx") and not append_blob:
+            # Take this time to shift any Mach-O structures around to fit our
+            # blob. We don't need to worry about aligning the offset since the
+            # compiler already took care of that when creating the segment.
+            blob_offset = self._shift_macho_structures(stub_data, load_commands, blob_size)
+        else:
+            # Add padding before the blob if necessary.
+            blob_offset = len(stub_data)
+            if (blob_offset & (blob_align - 1)) != 0:
+                pad = (blob_align - (blob_offset & (blob_align - 1)))
+                stub_data += (b'\0' * pad)
+                blob_offset += pad
+            assert (blob_offset % blob_align) == 0
+            assert blob_offset == len(stub_data)
+
         # Calculate the offsets for the variables.  These are pointers,
         # relative to the beginning of the blob.
         field_offsets = {}
@@ -1893,9 +1953,13 @@ class Freezer:
             blob += struct.pack('<Q', blob_offset)
 
         with open(target, 'wb') as f:
-            f.write(stub_data)
-            assert f.tell() == blob_offset
-            f.write(blob)
+            if append_blob:
+                f.write(stub_data)
+                assert f.tell() == blob_offset
+                f.write(blob)
+            else:
+                stub_data[blob_offset:blob_offset + blob_size] = blob
+                f.write(stub_data)
 
         os.chmod(target, 0o755)
         return target
@@ -2153,7 +2217,9 @@ class Freezer:
                     symoff += nlist_size
                     name = strings[strx : strings.find(b'\0', strx)]
 
-                    if name == b'_' + symbol_name:
+                    # If the entry's type has any bits at 0xe0 set, it's a debug
+                    # symbol, and will point us to the wrong place.
+                    if name == b'_' + symbol_name and type & 0xe0 == 0:
                         # Find out in which segment this is.
                         for vmaddr, vmsize, fileoff in segments:
                             # Is it defined in this segment?
@@ -2163,6 +2229,59 @@ class Freezer:
                                 return fileoff + rel
                         print("Could not find memory address for symbol %s" % (symbol_name))
 
+    def _parse_macho_load_commands(self, macho_data):
+        """Returns the list of load commands from macho_data."""
+        mach_header_64 = list(
+            struct.unpack_from(mach_header_64_layout, macho_data, 0))
+
+        num_load_commands = mach_header_64[4]
+
+        load_commands = {}
+
+        curr_lc_offset = struct.calcsize(mach_header_64_layout)
+        for i in range(num_load_commands):
+            lc = struct.unpack_from(lc_header_layout, macho_data, curr_lc_offset)
+            layout = lc_layouts.get(lc[0])
+            if layout:
+                # Make it a list since we want to mutate it.
+                lc = list(struct.unpack_from(layout, macho_data, curr_lc_offset))
+
+                if lc[0] == LC_SEGMENT_64:
+                    stripped_name = lc[2].rstrip(b'\0')
+                    if stripped_name in [b'__PANDA', b'__LINKEDIT']:
+                        load_commands[stripped_name] = (curr_lc_offset, lc)
+                else:
+                    load_commands[lc[0]] = (curr_lc_offset, lc)
+
+            curr_lc_offset += lc[1]
+
+        return load_commands
+
+    def _shift_macho_structures(self, macho_data, load_commands, blob_size):
+        """Given the stub and the size of our blob, make room for it and edit
+        all of the necessary structures to keep the binary valid. Returns the
+        offset where the blob should be placed."""
+
+        for lc_key in load_commands.keys():
+            for index in lc_indices_to_slide[lc_key]:
+                load_commands[lc_key][1][index] += blob_size
+
+            if lc_key == b'__PANDA':
+                section_header_offset = load_commands[lc_key][0] + struct.calcsize(lc_layouts[LC_SEGMENT_64])
+                section_header = list(struct.unpack_from(section64_header_layout, macho_data, section_header_offset))
+                section_header[3] = blob_size
+                struct.pack_into(section64_header_layout, macho_data, section_header_offset, *section_header)
+
+            layout = LC_SEGMENT_64 if lc_key in [b'__PANDA', b'__LINKEDIT'] else lc_key
+            struct.pack_into(lc_layouts[layout], macho_data, load_commands[lc_key][0], *load_commands[lc_key][1])
+
+        blob_offset = load_commands[b'__PANDA'][1][5]
+
+        # Write in some null bytes until we write in the actual blob.
+        macho_data[blob_offset:blob_offset] = b'\0' * blob_size
+
+        return blob_offset
+
     def makeModuleDef(self, mangledName, code):
         result = ''
         result += 'static unsigned char %s[] = {' % (mangledName)

+ 0 - 7
direct/src/dist/commands.py

@@ -4,10 +4,6 @@ See the :ref:`distribution` section of the programming manual for information
 on how to use these commands.
 """
 
-from __future__ import print_function
-
-from pathlib import Path
-from modulefinder import ModuleFinder
 import collections
 import os
 import plistlib
@@ -35,9 +31,6 @@ import panda3d.core as p3d
 
 
 if sys.version_info < (3, 0):
-    # Python 3 defines these subtypes of IOError, but Python 2 doesn't.
-    FileNotFoundError = IOError
-
     # Warn the user.  They might be using Python 2 by accident.
     print("=================================================================")
     print("WARNING: You are using Python 2, which has reached the end of its")

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

@@ -94,12 +94,12 @@ class DirectObject:
         if hasattr(self, '_taskList'):
             tasks = [task.name for task in self._taskList.values()]
         if len(events) or len(tasks):
-            estr = choice(len(events), 'listening to events: %s' % events, '')
-            andStr = choice(len(events) and len(tasks), ' and ', '')
-            tstr = choice(len(tasks), '%srunning tasks: %s' % (andStr, tasks), '')
+            estr = ('listening to events: %s' % events if len(events) else '')
+            andStr = (' and ' if len(events) and len(tasks) else '')
+            tstr = ('%srunning tasks: %s' % (andStr, tasks) if len(tasks) else '')
             notify = directNotify.newCategory('LeakDetect')
-            func = choice(getRepository()._crashOnProactiveLeakDetect,
-                          self.notify.error, self.notify.warning)
+            crash = getattr(getRepository(), '_crashOnProactiveLeakDetect', False)
+            func = (self.notify.error if crash else self.notify.warning)
             func('destroyed %s instance is still %s%s' % (self.__class__.__name__, estr, tstr))
 
     #snake_case alias:

+ 0 - 1
direct/src/showbase/ProfileSession.py

@@ -1,4 +1,3 @@
-from __future__ import print_function
 from panda3d.core import TrueClock
 from direct.directnotify.DirectNotifyGlobal import directNotify
 from direct.showbase.PythonUtil import (

+ 1 - 1
dtool/Package.cmake

@@ -408,7 +408,7 @@ package_status(SQUISH "libsquish")
 #
 
 # Assimp
-find_package(Assimp QUIET)
+find_package(Assimp QUIET MODULE)
 
 package_option(Assimp
   "Build pandatool with support for loading 3D assets supported by Assimp.")

Разлика између датотеке није приказан због своје велике величине
+ 432 - 353
dtool/src/cppparser/cppBison.cxx.prebuilt


+ 6 - 2
dtool/src/cppparser/cppBison.h.prebuilt

@@ -1,8 +1,9 @@
-/* A Bison parser, made by GNU Bison 3.0.5.  */
+/* A Bison parser, made by GNU Bison 3.5.3.  */
 
 /* Bison interface for Yacc-like parsers in C
 
-   Copyright (C) 1984, 1989-1990, 2000-2015, 2018 Free Software Foundation, Inc.
+   Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2020 Free Software Foundation,
+   Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -30,6 +31,9 @@
    This special exception was added by the Free Software Foundation in
    version 2.2 of Bison.  */
 
+/* Undocumented macros, especially those whose name start with YY_,
+   are private implementation details.  Do not rely on them.  */
+
 #ifndef YY_CPPYY_BUILT_TMP_CPPBISON_YXX_H_INCLUDED
 # define YY_CPPYY_BUILT_TMP_CPPBISON_YXX_H_INCLUDED
 /* Debug traces.  */

+ 13 - 37
dtool/src/cppparser/cppBison.yxx

@@ -1891,7 +1891,7 @@ instance_identifier_and_maybe_trailing_return_type:
   }
   $$ = $1;
 }
-        | instance_identifier ':' INTEGER
+        | instance_identifier ':' const_expr
 {
   // Bitfield definition.
   $1->_bit_width = $3;
@@ -3278,17 +3278,9 @@ no_angle_bracket_const_expr:
 {
   $$ = new CPPExpression(CPPExpression::sizeof_func($3));
 }
-        | KW_SIZEOF '(' IDENTIFIER ')' %prec UNARY
+        | KW_SIZEOF no_angle_bracket_const_expr %prec UNARY
 {
-  CPPDeclaration *arg = $3->find_symbol(current_scope, global_scope, current_lexer);
-  if (arg == nullptr) {
-    yyerror("undefined sizeof argument: " + $3->get_fully_scoped_name(), @3);
-  } else if (arg->get_subtype() == CPPDeclaration::ST_instance) {
-    CPPInstance *inst = arg->as_instance();
-    $$ = new CPPExpression(CPPExpression::sizeof_func(inst->_type));
-  } else {
-    $$ = new CPPExpression(CPPExpression::sizeof_func(arg->as_type()));
-  }
+  $$ = new CPPExpression(CPPExpression::sizeof_func($2));
 }
         | KW_SIZEOF ELLIPSIS '(' name ')' %prec UNARY
 {
@@ -3402,9 +3394,9 @@ no_angle_bracket_const_expr:
 {
   $$ = new CPPExpression('f', $1);
 }
-        | no_angle_bracket_const_expr '.' no_angle_bracket_const_expr
+        | no_angle_bracket_const_expr '.' name
 {
-  $$ = new CPPExpression('.', $1, $3);
+  $$ = new CPPExpression('.', $1, new CPPExpression($3, current_scope, global_scope, current_lexer));
 }
         | no_angle_bracket_const_expr POINTSAT no_angle_bracket_const_expr
 {
@@ -3542,17 +3534,9 @@ const_expr:
 {
   $$ = new CPPExpression(CPPExpression::sizeof_func($3));
 }
-        | KW_SIZEOF '(' IDENTIFIER ')' %prec UNARY
+        | KW_SIZEOF const_expr %prec UNARY
 {
-  CPPDeclaration *arg = $3->find_symbol(current_scope, global_scope, current_lexer);
-  if (arg == nullptr) {
-    yyerror("undefined sizeof argument: " + $3->get_fully_scoped_name(), @3);
-  } else if (arg->get_subtype() == CPPDeclaration::ST_instance) {
-    CPPInstance *inst = arg->as_instance();
-    $$ = new CPPExpression(CPPExpression::sizeof_func(inst->_type));
-  } else {
-    $$ = new CPPExpression(CPPExpression::sizeof_func(arg->as_type()));
-  }
+  $$ = new CPPExpression(CPPExpression::sizeof_func($2));
 }
         | KW_SIZEOF ELLIPSIS '(' name ')' %prec UNARY
 {
@@ -3704,9 +3688,9 @@ const_expr:
 {
   $$ = new CPPExpression('f', $1);
 }
-        | const_expr '.' const_expr
+        | const_expr '.' name
 {
-  $$ = new CPPExpression('.', $1, $3);
+  $$ = new CPPExpression('.', $1, new CPPExpression($3, current_scope, global_scope, current_lexer));
 }
         | const_expr POINTSAT const_expr
 {
@@ -3886,17 +3870,9 @@ formal_const_expr:
 {
   $$ = new CPPExpression(CPPExpression::sizeof_func($3));
 }
-        | KW_SIZEOF '(' IDENTIFIER ')' %prec UNARY
+        | KW_SIZEOF formal_const_expr %prec UNARY
 {
-  CPPDeclaration *arg = $3->find_symbol(current_scope, global_scope, current_lexer);
-  if (arg == nullptr) {
-    yyerror("undefined sizeof argument: " + $3->get_fully_scoped_name(), @3);
-  } else if (arg->get_subtype() == CPPDeclaration::ST_instance) {
-    CPPInstance *inst = arg->as_instance();
-    $$ = new CPPExpression(CPPExpression::sizeof_func(inst->_type));
-  } else {
-    $$ = new CPPExpression(CPPExpression::sizeof_func(arg->as_type()));
-  }
+  $$ = new CPPExpression(CPPExpression::sizeof_func($2));
 }
         | KW_SIZEOF ELLIPSIS '(' name ')' %prec UNARY
 {
@@ -4044,9 +4020,9 @@ formal_const_expr:
 {
   $$ = new CPPExpression('f', $1);
 }
-        | formal_const_expr '.' const_expr
+        | formal_const_expr '.' name
 {
-  $$ = new CPPExpression('.', $1, $3);
+  $$ = new CPPExpression('.', $1, new CPPExpression($3, current_scope, global_scope, current_lexer));
 }
         | formal_const_expr POINTSAT const_expr
 {

+ 47 - 9
dtool/src/cppparser/cppExpression.cxx

@@ -429,12 +429,24 @@ type_trait(int trait, CPPType *type, CPPType *arg) {
 CPPExpression CPPExpression::
 sizeof_func(CPPType *type) {
   CPPExpression expr(0);
-  expr._type = T_sizeof;
+  expr._type = T_sizeof_type;
   expr._u._typecast._to = type;
   expr._u._typecast._op1 = nullptr;
   return expr;
 }
 
+/**
+ *
+ */
+CPPExpression CPPExpression::
+sizeof_func(CPPExpression *op1) {
+  CPPExpression expr(0);
+  expr._type = T_sizeof_expr;
+  expr._u._typecast._to = nullptr;
+  expr._u._typecast._op1 = op1;
+  return expr;
+}
+
 /**
  *
  */
@@ -629,7 +641,8 @@ evaluate() const {
   case T_empty_aggregate_init:
   case T_new:
   case T_default_new:
-  case T_sizeof:
+  case T_sizeof_type:
+  case T_sizeof_expr:
   case T_sizeof_ellipsis:
     return Result();
 
@@ -1058,7 +1071,8 @@ determine_type() const {
   case T_default_new:
     return CPPType::new_type(new CPPPointerType(_u._typecast._to));
 
-  case T_sizeof:
+  case T_sizeof_type:
+  case T_sizeof_expr:
   case T_sizeof_ellipsis:
   case T_alignof:
     // Note: this should actually be size_t, but that is defined as a typedef
@@ -1334,10 +1348,13 @@ is_fully_specified() const {
   case T_default_construct:
   case T_empty_aggregate_init:
   case T_default_new:
-  case T_sizeof:
+  case T_sizeof_type:
   case T_alignof:
     return _u._typecast._to->is_fully_specified();
 
+  case T_sizeof_expr:
+    return _u._typecast._op1->is_fully_specified();
+
   case T_sizeof_ellipsis:
     return _u._ident->is_fully_specified();
 
@@ -1469,7 +1486,7 @@ substitute_decl(CPPDeclaration::SubstDecl &subst,
   case T_default_construct:
   case T_empty_aggregate_init:
   case T_default_new:
-  case T_sizeof:
+  case T_sizeof_type:
   case T_alignof:
     rep->_u._typecast._to =
       _u._typecast._to->substitute_decl(subst, current_scope, global_scope)
@@ -1477,6 +1494,13 @@ substitute_decl(CPPDeclaration::SubstDecl &subst,
     any_changed = any_changed || (rep->_u._typecast._to != _u._typecast._to);
     break;
 
+  case T_sizeof_expr:
+    rep->_u._typecast._op1 =
+      _u._typecast._op1->substitute_decl(subst, current_scope, global_scope)
+      ->as_expression();
+    any_changed = any_changed || (rep->_u._typecast._op1 != _u._typecast._op1);
+    break;
+
   case T_trinary_operation:
     rep->_u._op._op3 =
       _u._op._op3->substitute_decl(subst, current_scope, global_scope)
@@ -1567,10 +1591,13 @@ is_tbd() const {
   case T_new:
   case T_default_construct:
   case T_default_new:
-  case T_sizeof:
+  case T_sizeof_type:
   case T_alignof:
     return _u._typecast._to->is_tbd();
 
+  case T_sizeof_expr:
+    return _u._typecast._op1->is_tbd();
+
   case T_trinary_operation:
     if (_u._op._op3->is_tbd()) {
       return true;
@@ -1807,12 +1834,17 @@ output(std::ostream &out, int indent_level, CPPScope *scope, bool) const {
     out << "())";
     break;
 
-  case T_sizeof:
+  case T_sizeof_type:
     out << "sizeof(";
     _u._typecast._to->output(out, indent_level, scope, false);
     out << ")";
     break;
 
+  case T_sizeof_expr:
+    out << "sizeof ";
+    _u._typecast._op1->output(out, indent_level, scope, false);
+    break;
+
   case T_sizeof_ellipsis:
     out << "sizeof...(";
     _u._ident->output(out, scope);
@@ -2222,10 +2254,13 @@ is_equal(const CPPDeclaration *other) const {
   case T_default_construct:
   case T_empty_aggregate_init:
   case T_default_new:
-  case T_sizeof:
+  case T_sizeof_type:
   case T_alignof:
     return _u._typecast._to == ot->_u._typecast._to;
 
+  case T_sizeof_expr:
+    return _u._typecast._op1 == ot->_u._typecast._op1;
+
   case T_unary_operation:
     return *_u._op._op1 == *ot->_u._op._op1;
 
@@ -2324,10 +2359,13 @@ is_less(const CPPDeclaration *other) const {
   case T_default_construct:
   case T_empty_aggregate_init:
   case T_default_new:
-  case T_sizeof:
+  case T_sizeof_type:
   case T_alignof:
     return _u._typecast._to < ot->_u._typecast._to;
 
+  case T_sizeof_expr:
+    return _u._typecast._op1 < ot->_u._typecast._op1;
+
   case T_trinary_operation:
     if (*_u._op._op3 != *ot->_u._op._op3) {
       return *_u._op._op3 < *ot->_u._op._op3;

+ 3 - 1
dtool/src/cppparser/cppExpression.h

@@ -52,7 +52,8 @@ public:
     T_empty_aggregate_init,
     T_new,
     T_default_new,
-    T_sizeof,
+    T_sizeof_type,
+    T_sizeof_expr,
     T_sizeof_ellipsis,
     T_alignof,
     T_unary_operation,
@@ -89,6 +90,7 @@ public:
   static CPPExpression typeid_op(CPPExpression *op1, CPPType *std_type_info);
   static CPPExpression type_trait(int trait, CPPType *type, CPPType *arg = nullptr);
   static CPPExpression sizeof_func(CPPType *type);
+  static CPPExpression sizeof_func(CPPExpression *op1);
   static CPPExpression sizeof_ellipsis_func(CPPIdentifier *ident);
   static CPPExpression alignof_func(CPPType *type);
   static CPPExpression lambda(CPPClosureType *type);

+ 11 - 1
dtool/src/cppparser/cppInstance.cxx

@@ -74,7 +74,17 @@ CPPInstance(CPPType *type, CPPInstanceIdentifier *ii, int storage_class,
   ii->_ident = nullptr;
   _storage_class = storage_class;
   _initializer = nullptr;
-  _bit_width = ii->_bit_width;
+
+  if (ii->_bit_width != nullptr) {
+    CPPExpression::Result result = ii->_bit_width->evaluate();
+    if (result._type != CPPExpression::RT_error) {
+      _bit_width = ii->_bit_width->evaluate().as_integer();
+    } else {
+      _bit_width = -1;
+    }
+  } else {
+    _bit_width = -1;
+  }
 
   CPPParameterList *params = ii->get_initializer();
   if (params != nullptr) {

+ 1 - 1
dtool/src/cppparser/cppInstanceIdentifier.cxx

@@ -82,7 +82,7 @@ initializer_type(CPPParameterList *params) {
 CPPInstanceIdentifier::
 CPPInstanceIdentifier(CPPIdentifier *ident) :
   _ident(ident),
-  _bit_width(-1),
+  _bit_width(nullptr),
   _packed(false) {
 }
 

+ 2 - 2
dtool/src/cppparser/cppInstanceIdentifier.h

@@ -86,8 +86,8 @@ public:
   typedef std::vector<Modifier> Modifiers;
   Modifiers _modifiers;
 
-  // If not -1, indicates a bitfield
-  int _bit_width;
+  // If not null, indicates a bitfield
+  CPPExpression *_bit_width;
 
   // Indicates a parameter pack
   bool _packed;

+ 15 - 11
dtool/src/parser-inc/map

@@ -24,19 +24,23 @@
 #include <stdcompare.h>
 #include <pair>
 
-template<class key, class element, class compare = less<key> >
+namespace std {
+  template<class T> class allocator;
+}
+
+template<class Key, class T, class Compare = less<Key>, class Allocator = std::allocator<pair<const Key, T> > >
 class map {
 public:
-  typedef key key_type;
-  typedef element data_type;
-  typedef element mapped_type;
-  typedef pair<const key, element> value_type;
-  typedef compare key_compare;
-
-  typedef element *pointer;
-  typedef const element *const_pointer;
-  typedef element &reference;
-  typedef const element &const_reference;
+  typedef Key key_type;
+  typedef T data_type;
+  typedef T mapped_type;
+  typedef pair<const Key, T> value_type;
+  typedef Compare key_compare;
+
+  typedef T *pointer;
+  typedef const T *const_pointer;
+  typedef T &reference;
+  typedef const T &const_reference;
 
   class iterator;
   class const_iterator;

+ 2 - 0
dtool/src/parser-inc/ostream

@@ -1,5 +1,7 @@
 #pragma once
 
+#include <iosfwd>
+
 namespace std {
   template<class CharT, class Traits>
   std::basic_ostream<CharT, Traits> &ends(std::basic_ostream<CharT, Traits> &os);

+ 9 - 8
dtool/src/parser-inc/string

@@ -26,6 +26,7 @@
 
 namespace std {
   template<class charT> struct char_traits;
+  template<class T> class allocator;
 
   template<> struct char_traits<char> {
     using char_type = char;
@@ -51,7 +52,7 @@ namespace std {
     using state_type = mbstate_t;
   };
 
-  template<class ctype>
+  template<class CharT, class Traits = std::char_traits<CharT>, class Allocator = std::allocator<CharT> >
   class basic_string {
   public:
     struct iterator;
@@ -63,17 +64,17 @@ namespace std {
     static const size_t npos = -1;
 
     basic_string();
-    basic_string(const basic_string<ctype> &copy);
-    void operator = (const basic_string<ctype> &copy);
-    basic_string(const ctype *string);
+    basic_string(const basic_string<CharT> &copy);
+    void operator = (const basic_string<CharT> &copy);
+    basic_string(const CharT *string);
     ~basic_string();
 
-    const ctype *c_str() const;
+    const CharT *c_str() const;
     size_t length() const;
 
-    ctype at(size_t pos) const;
-    ctype operator[](size_t pos) const;
-    ctype &operator[](size_t pos);
+    CharT at(size_t pos) const;
+    CharT operator[](size_t pos) const;
+    CharT &operator[](size_t pos);
   };
 
   typedef basic_string<char> string;

+ 10 - 6
dtool/src/parser-inc/vector

@@ -22,17 +22,21 @@
 
 #include <stdtypedefs.h>
 
+namespace std {
+  template<class T> class allocator;
+}
+
 inline namespace std {
 
-template<class element>
+template<class T, class Allocator = std::allocator<T> >
 class vector {
 public:
-  typedef element value_type;
+  typedef T value_type;
 
-  typedef element *pointer;
-  typedef const element *const_pointer;
-  typedef element &reference;
-  typedef const element &const_reference;
+  typedef T *pointer;
+  typedef const T *const_pointer;
+  typedef T &reference;
+  typedef const T &const_reference;
 
   typedef pointer iterator;
   typedef const_pointer const_iterator;

+ 1 - 1
makepanda/makepanda.py

@@ -168,7 +168,7 @@ def parseopts(args):
     # Options for which to display a deprecation warning.
     removedopts = [
         "use-touchinput", "no-touchinput", "no-awesomium", "no-directscripts",
-        "no-carbon", "universal", "no-physx", "no-rocket"
+        "no-carbon", "universal", "no-physx", "no-rocket", "host"
         ]
 
     # All recognized options.

+ 1 - 0
panda/src/cocoadisplay/cocoaGraphicsWindow.h

@@ -51,6 +51,7 @@ public:
   void handle_move_event();
   void handle_resize_event();
   void handle_minimize_event(bool minimized);
+  void handle_maximize_event(bool maximized);
   void handle_foreground_event(bool foreground);
   bool handle_close_request();
   void handle_close_event();

+ 52 - 2
panda/src/cocoadisplay/cocoaGraphicsWindow.mm

@@ -405,6 +405,9 @@ open_window() {
   if (!_properties.has_minimized()) {
     _properties.set_minimized(false);
   }
+  if (!_properties.has_maximized()) {
+    _properties.set_maximized(false);
+  }
   if (!_properties.has_z_order()) {
     _properties.set_z_order(WindowProperties::Z_normal);
   }
@@ -713,7 +716,7 @@ close_window() {
 
   if (_window != nil) {
     [_window close];
-    
+
     // Process events once more so any pending NSEvents are cleared. Not doing
     // this causes the window to stick around after calling [_window close].
     process_events();
@@ -925,6 +928,14 @@ set_properties_now(WindowProperties &properties) {
     properties.clear_origin();
   }
 
+  if (properties.has_maximized() && _window != nil) {
+    _properties.set_maximized(properties.get_maximized());
+    if (properties.get_maximized() != !![_window isZoomed]) {
+      [_window zoom:nil];
+    }
+    properties.clear_maximized();
+  }
+
   if (properties.has_title() && _window != nil) {
     _properties.set_title(properties.get_title());
     [_window setTitle:[NSString stringWithUTF8String:properties.get_title().c_str()]];
@@ -1404,10 +1415,12 @@ handle_resize_event() {
 
   NSRect frame = [_view convertRect:[_view bounds] toView:nil];
 
+  WindowProperties properties;
+  bool changed = false;
+
   if (frame.size.width != _properties.get_x_size() ||
       frame.size.height != _properties.get_y_size()) {
 
-    WindowProperties properties;
     properties.set_size(frame.size.width, frame.size.height);
 
     if (cocoadisplay_cat.is_spam()) {
@@ -1415,6 +1428,18 @@ handle_resize_event() {
         << "Window changed size to (" << frame.size.width
        << ", " << frame.size.height << ")\n";
     }
+    changed = true;
+  }
+
+  if (_window != nil) {
+    bool is_maximized = [_window isZoomed];
+    if (is_maximized != _properties.get_maximized()) {
+      properties.set_maximized(is_maximized);
+      changed = true;
+    }
+  }
+
+  if (changed) {
     system_changed_properties(properties);
   }
 
@@ -1444,6 +1469,31 @@ handle_minimize_event(bool minimized) {
   system_changed_properties(properties);
 }
 
+/**
+ * Called by the window delegate when the window is maximized or
+ * demaximized.
+ */
+void CocoaGraphicsWindow::
+handle_maximize_event(bool maximized) {
+  if (maximized == _properties.get_maximized()) {
+    return;
+  }
+
+  if (cocoadisplay_cat.is_debug()) {
+    if (maximized) {
+      cocoadisplay_cat.debug() << "Window was maximized\n";
+    } else {
+      cocoadisplay_cat.debug() << "Window was demaximized\n";
+    }
+  }
+
+  WindowProperties properties;
+  properties.set_maximized(maximized);
+  system_changed_properties(properties);
+}
+
+
+
 /**
  * Called by the window delegate when the window has become the key window or
  * resigned that status.

+ 2 - 1
panda/src/collide/collisionPolygon.h

@@ -42,6 +42,8 @@ public:
 
   virtual CollisionSolid *make_copy();
 
+  static bool verify_points(const LPoint3 *begin, const LPoint3 *end);
+
 PUBLISHED:
   virtual LPoint3 get_collision_origin() const;
 
@@ -53,7 +55,6 @@ PUBLISHED:
   INLINE static bool verify_points(const LPoint3 &a, const LPoint3 &b,
                                    const LPoint3 &c, const LPoint3 &d);
   static bool verify_points(const LPoint3 &a, const LPoint3 &b, const LPoint3 &c);
-  static bool verify_points(const LPoint3 *begin, const LPoint3 *end);
 
   bool is_valid() const;
   bool is_concave() const;

+ 7 - 0
panda/src/display/config_display.cxx

@@ -328,6 +328,13 @@ ConfigVariableInt win_origin
 ConfigVariableBool fullscreen
 ("fullscreen", false);
 
+ConfigVariableBool maximized
+("maximized", false,
+ PRC_DESC("Start the window in a maximized state as handled by the window"
+          "manager.  In comparison to the fullscreen setting, this will"
+          "usually not remove the window decoration and not occupy the"
+          "whole screen space."));
+
 ConfigVariableBool undecorated
 ("undecorated", false,
  PRC_DESC("This specifies the default value of the 'undecorated' window "

+ 1 - 0
panda/src/display/config_display.h

@@ -73,6 +73,7 @@ extern EXPCL_PANDA_DISPLAY ConfigVariableBool old_alpha_blend;
 extern EXPCL_PANDA_DISPLAY ConfigVariableInt win_size;
 extern EXPCL_PANDA_DISPLAY ConfigVariableInt win_origin;
 extern EXPCL_PANDA_DISPLAY ConfigVariableBool fullscreen;
+extern EXPCL_PANDA_DISPLAY ConfigVariableBool maximized;
 extern EXPCL_PANDA_DISPLAY ConfigVariableBool undecorated;
 extern EXPCL_PANDA_DISPLAY ConfigVariableBool win_fixed_size;
 extern EXPCL_PANDA_DISPLAY ConfigVariableBool cursor_hidden;

+ 1 - 0
panda/src/display/graphicsWindow.cxx

@@ -54,6 +54,7 @@ GraphicsWindow(GraphicsEngine *engine, GraphicsPipe *pipe,
   _properties.set_undecorated(false);
   _properties.set_fullscreen(false);
   _properties.set_minimized(false);
+  _properties.set_maximized(false);
   _properties.set_cursor_hidden(false);
 
   request_properties(WindowProperties::get_default());

+ 39 - 0
panda/src/display/windowProperties.I

@@ -407,6 +407,45 @@ clear_minimized() {
   _flags &= ~F_minimized;
 }
 
+/**
+ * Specifies whether the window should be created maximized (true), or normal
+ * (false).
+ */
+INLINE void WindowProperties::
+set_maximized(bool maximized) {
+  if (maximized) {
+    _flags |= F_maximized;
+  } else {
+    _flags &= ~F_maximized;
+  }
+  _specified |= S_maximized;
+}
+
+/**
+ * Returns true if the window is maximized.
+ */
+INLINE bool WindowProperties::
+get_maximized() const {
+  return (_flags & F_maximized) != 0;
+}
+
+/**
+ * Returns true if set_maximized() has been specified.
+ */
+INLINE bool WindowProperties::
+has_maximized() const {
+  return ((_specified & S_maximized) != 0);
+}
+
+/**
+ * Removes the maximized specification from the properties.
+ */
+INLINE void WindowProperties::
+clear_maximized() {
+  _specified &= ~S_maximized;
+  _flags &= ~F_maximized;
+}
+
 /**
  * Specifies whether the window should read the raw mouse devices.
  */

+ 7 - 0
panda/src/display/windowProperties.cxx

@@ -69,6 +69,7 @@ get_config_properties() {
   props.set_fullscreen(fullscreen);
   props.set_undecorated(undecorated);
   props.set_fixed_size(win_fixed_size);
+  props.set_maximized(maximized);
   props.set_cursor_hidden(cursor_hidden);
   if (!icon_filename.empty()) {
     props.set_icon_filename(icon_filename);
@@ -240,6 +241,9 @@ add_properties(const WindowProperties &other) {
   if (other.has_minimized()) {
     set_minimized(other.get_minimized());
   }
+  if (other.has_maximized()) {
+    set_maximized(other.get_maximized());
+  }
   if (other.has_raw_mice()) {
     set_raw_mice(other.get_raw_mice());
   }
@@ -296,6 +300,9 @@ output(ostream &out) const {
   if (has_minimized()) {
     out << (get_minimized() ? "minimized " : "!minimized ");
   }
+  if (has_maximized()) {
+    out << (get_maximized() ? "maximized " : "!maximized ");
+  }
   if (has_raw_mice()) {
     out << (get_raw_mice() ? "raw_mice " : "!raw_mice ");
   }

+ 9 - 0
panda/src/display/windowProperties.h

@@ -132,6 +132,13 @@ PUBLISHED:
   MAKE_PROPERTY2(minimized, has_minimized, get_minimized,
                             set_minimized, clear_minimized);
 
+  INLINE void set_maximized(bool maximized);
+  INLINE bool get_maximized() const;
+  INLINE bool has_maximized() const;
+  INLINE void clear_maximized();
+  MAKE_PROPERTY2(maximized, has_maximized, get_maximized,
+                            set_maximized, clear_maximized);
+
   INLINE void set_raw_mice(bool raw_mice);
   INLINE bool get_raw_mice() const;
   INLINE bool has_raw_mice() const;
@@ -202,6 +209,7 @@ private:
     S_mouse_mode           = 0x02000,
     S_parent_window        = 0x04000,
     S_raw_mice             = 0x08000,
+    S_maximized            = 0x10000,
   };
 
   // This bitmask represents the truefalse settings for various boolean flags
@@ -211,6 +219,7 @@ private:
     F_fullscreen     = S_fullscreen,
     F_foreground     = S_foreground,
     F_minimized      = S_minimized,
+    F_maximized      = S_maximized,
     F_open           = S_open,
     F_cursor_hidden  = S_cursor_hidden,
     F_fixed_size     = S_fixed_size,

+ 3 - 3
panda/src/downloader/httpClient.I

@@ -111,9 +111,9 @@ get_verify_ssl() const {
  * Specifies the set of ciphers that are to be made available for SSL
  * connections.  This is a string as described in the ciphers(1) man page of
  * the OpenSSL documentation (or see
- * https://www.openssl.org/docs/apps/ciphers.html ).  If this isn't specified,
- * the default is provided by the Config file.  You may also specify "DEFAULT"
- * to use the built-in OpenSSL default value.
+ * https://www.openssl.org/docs/man1.1.1/man1/ciphers.html ).  If this isn't
+ * specified, the default is provided by the Config file.  You may also specify
+ * "DEFAULT" to use the built-in OpenSSL default value.
  */
 INLINE void HTTPClient::
 set_cipher_list(const std::string &cipher_list) {

+ 4 - 4
panda/src/grutil/meshDrawer2D.I

@@ -60,7 +60,7 @@ set_budget(int total_budget) {
 }
 
 /**
- * Gets the total triangle budget of the drawer
+ * Gets the total triangle budget of the drawer.
  */
 INLINE int MeshDrawer2D::
 get_budget() {
@@ -68,7 +68,7 @@ get_budget() {
 }
 
 /**
- * Sets clipping rectangle
+ * Sets the clipping rectangle.
  */
 INLINE void MeshDrawer2D::
 set_clip(PN_stdfloat x, PN_stdfloat y, PN_stdfloat w, PN_stdfloat h) {
@@ -79,7 +79,7 @@ set_clip(PN_stdfloat x, PN_stdfloat y, PN_stdfloat w, PN_stdfloat h) {
 }
 
 /**
- * Draws a 2d rectangle.  Ignores the cliping rectangle
+ * Draws a 2D rectangle.  Ignores the clipping rectangle.
  */
 INLINE void MeshDrawer2D::
 quad_raw(const LVector3 &v1, const LVector4 &c1, const LVector2 &uv1,
@@ -125,7 +125,7 @@ rectangle_raw(PN_stdfloat x, PN_stdfloat y, PN_stdfloat w, PN_stdfloat h,
 }
 
 /**
- * Draws a 2d rectangle, that can be cliped
+ * Draws a 2D rectangle which can be clipped.
  */
 INLINE void MeshDrawer2D::
 rectangle(PN_stdfloat x, PN_stdfloat y, PN_stdfloat w, PN_stdfloat h,

+ 2 - 2
panda/src/grutil/meshDrawer2D.h

@@ -39,8 +39,8 @@
 #include "nodePath.h"
 
 /**
- * This class allows the drawing of 2d objects - mainly based on quads and
- * rectangles.  Allows clipping and serverl high level UI theme functions.
+ * This class allows the drawing of 2D objects - mainly based on quads and
+ * rectangles.  It allows clipping and several high level UI theme functions.
  */
 class EXPCL_PANDA_GRUTIL MeshDrawer2D : public TypedObject {
 PUBLISHED:

+ 12 - 4
panda/src/nativenet/buffered_datagramconnection.h

@@ -136,7 +136,10 @@ private:
  * used to do a full reset of buffers
  */
 inline void Buffered_DatagramConnection::ClearAll(void) {
-  nativenet_cat.error() << "Buffered_DatagramConnection::ClearAll Starting Auto Reset\n";
+  if (nativenet_cat.is_debug()) {
+    nativenet_cat.debug()
+      << "Buffered_DatagramConnection::ClearAll Starting Auto Reset\n";
+  }
   Close();
   _Writer.ReSet();
   _Reader.ReSet();
@@ -215,8 +218,11 @@ inline Buffered_DatagramConnection::~Buffered_DatagramConnection(void)
 inline Buffered_DatagramConnection::Buffered_DatagramConnection(int rbufsize, int wbufsize, int write_flush_point)
     :  _Writer(wbufsize,write_flush_point) , _Reader(rbufsize)
 {
-  nativenet_cat.error() << "Buffered_DatagramConnection Constructor rbufsize = " << rbufsize
-                        << " wbufsize = " << wbufsize << " write_flush_point = " << write_flush_point << "\n";
+  if (nativenet_cat.is_debug()) {
+    nativenet_cat.debug()
+      << "Buffered_DatagramConnection Constructor rbufsize = " << rbufsize
+      << " wbufsize = " << wbufsize << " write_flush_point = " << write_flush_point << "\n";
+  }
 }
 
 inline bool  Buffered_DatagramConnection::SendMessageBufferOnly(Datagram &msg)
@@ -289,7 +295,9 @@ bool Buffered_DatagramConnection::Flush(void)
  * Reset
  */
 inline void Buffered_DatagramConnection::Reset() {
-  nativenet_cat.error() << "Buffered_DatagramConnection::Reset()\n";
+  if (nativenet_cat.is_debug()) {
+    nativenet_cat.debug() << "Buffered_DatagramConnection::Reset()\n";
+  }
   ClearAll();
 }
 

+ 7 - 5
panda/src/pgraph/geomNode.cxx

@@ -507,8 +507,10 @@ add_for_draw(CullTraverser *trav, CullTraverserData &data) {
       << " draw_mask = " << data._draw_mask << "\n";
   }
 
+  Thread *current_thread = trav->get_current_thread();
+
   // Get all the Geoms, with no decalling.
-  Geoms geoms = get_geoms(trav->get_current_thread());
+  Geoms geoms = get_geoms(current_thread);
   int num_geoms = geoms.get_num_geoms();
   trav->_geoms_pcollector.add_level(num_geoms);
   CPT(TransformState) internal_transform = data.get_internal_transform(trav);
@@ -532,9 +534,9 @@ add_for_draw(CullTraverser *trav, CullTraverserData &data) {
     if (num_geoms > 1) {
       if (data._view_frustum != nullptr) {
         // Cull the individual Geom against the view frustum.
-        CPT(BoundingVolume) geom_volume = geom->get_bounds();
+        CPT(BoundingVolume) geom_volume = geom->get_bounds(current_thread);
         const GeometricBoundingVolume *geom_gbv =
-          DCAST(GeometricBoundingVolume, geom_volume);
+          geom_volume->as_geometric_bounding_volume();
 
         int result = data._view_frustum->contains(geom_gbv);
         if (result == BoundingVolume::IF_no_intersection) {
@@ -544,9 +546,9 @@ add_for_draw(CullTraverser *trav, CullTraverserData &data) {
       }
       if (!data._cull_planes->is_empty()) {
         // Also cull the Geom against the cull planes.
-        CPT(BoundingVolume) geom_volume = geom->get_bounds();
+        CPT(BoundingVolume) geom_volume = geom->get_bounds(current_thread);
         const GeometricBoundingVolume *geom_gbv =
-          DCAST(GeometricBoundingVolume, geom_volume);
+          geom_volume->as_geometric_bounding_volume();
         int result;
         data._cull_planes->do_cull(result, state, geom_gbv);
         if (result == BoundingVolume::IF_no_intersection) {

+ 6 - 0
panda/src/pgraph/pandaNode_ext.cxx

@@ -165,6 +165,12 @@ clear_python_tag(PyObject *key) {
   if (PyDict_GetItem(dict, key) != nullptr) {
     PyDict_DelItem(dict, key);
   }
+
+  if (PyDict_Size(dict) == 0 && Py_REFCNT(dict) == 1) {
+    // This was the last tag, and do_get_python_tags() made sure we have a
+    // unique reference to the tags, so clear the tag object.
+    _this->_python_tag_data.clear();
+  }
 }
 
 /**

+ 9 - 0
panda/src/pgui/pgItem.h

@@ -141,6 +141,15 @@ PUBLISHED:
   INLINE const std::string &get_id() const;
   INLINE void set_id(const std::string &id);
 
+  MAKE_PROPERTY(name, get_name, set_name);
+  MAKE_PROPERTY2(frame, has_frame, get_frame, set_frame, clear_frame);
+  MAKE_PROPERTY(state, get_state, set_state);
+  MAKE_PROPERTY(active, get_active, set_active);
+  MAKE_PROPERTY(focus, get_focus, set_focus);
+  MAKE_PROPERTY(background_focus, get_background_focus, set_background_focus);
+  MAKE_PROPERTY(suppress_flags, get_suppress_flags, set_suppress_flags);
+  MAKE_PROPERTY(id, get_id, set_id);
+
   INLINE static std::string get_enter_prefix();
   INLINE static std::string get_exit_prefix();
   INLINE static std::string get_within_prefix();

+ 5 - 5
panda/src/putil/bitArray.cxx

@@ -114,6 +114,11 @@ has_any_of(int low_bit, int size) const {
   ++w;
 
   while (size > 0) {
+    if ((size_t)w >= get_num_words()) {
+      // Now we're up to the highest bits.
+      return (_highest_bits != 0);
+    }
+
     if (size <= num_bits_per_word) {
       // The remainder fits within one word of the array.
       return _array[w].has_any_of(0, size);
@@ -125,11 +130,6 @@ has_any_of(int low_bit, int size) const {
     }
     size -= num_bits_per_word;
     ++w;
-
-    if (w >= (int)get_num_words()) {
-      // Now we're up to the highest bits.
-      return (_highest_bits != 0);
-    }
   }
 
   return false;

+ 60 - 14
panda/src/windisplay/winGraphicsWindow.cxx

@@ -306,22 +306,29 @@ set_properties_now(WindowProperties &properties) {
     LPoint2i bottom_right = top_left + _properties.get_size();
 
     DWORD window_style = make_style(_properties);
+    DWORD current_style = GetWindowLong(_hWnd, GWL_STYLE);
     SetWindowLong(_hWnd, GWL_STYLE, window_style);
 
-    // Now calculate the proper size and origin with the new window style.
-    RECT view_rect;
-    SetRect(&view_rect, top_left[0], top_left[1],
-            bottom_right[0], bottom_right[1]);
-    WINDOWINFO wi;
-    GetWindowInfo(_hWnd, &wi);
-    AdjustWindowRectEx(&view_rect, wi.dwStyle, FALSE, wi.dwExStyle);
-
-    // We need to call this to ensure that the style change takes effect.
-    SetWindowPos(_hWnd, HWND_NOTOPMOST, view_rect.left, view_rect.top,
-                 view_rect.right - view_rect.left,
-                 view_rect.bottom - view_rect.top,
-                 SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED |
-                 SWP_NOSENDCHANGING | SWP_SHOWWINDOW);
+    // If we switched to/from undecorated, calculate the new size.
+    if (((window_style ^ current_style) & WS_CAPTION) != 0) {
+      RECT view_rect;
+      SetRect(&view_rect, top_left[0], top_left[1],
+              bottom_right[0], bottom_right[1]);
+      WINDOWINFO wi;
+      GetWindowInfo(_hWnd, &wi);
+      AdjustWindowRectEx(&view_rect, wi.dwStyle, FALSE, wi.dwExStyle);
+
+      SetWindowPos(_hWnd, HWND_NOTOPMOST, view_rect.left, view_rect.top,
+                   view_rect.right - view_rect.left,
+                   view_rect.bottom - view_rect.top,
+                   SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED |
+                   SWP_NOSENDCHANGING | SWP_SHOWWINDOW);
+    } else {
+      // We need to call this to ensure that the style change takes effect.
+      SetWindowPos(_hWnd, HWND_NOTOPMOST, 0, 0, 0, 0,
+                   SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE |
+                   SWP_FRAMECHANGED | SWP_NOSENDCHANGING | SWP_SHOWWINDOW);
+    }
   }
 
   if (properties.has_title()) {
@@ -402,6 +409,23 @@ set_properties_now(WindowProperties &properties) {
     properties.clear_minimized();
   }
 
+  if (properties.has_maximized()) {
+    if (_properties.get_maximized() != properties.get_maximized()) {
+      if (properties.get_maximized()) {
+        ShowWindow(_hWnd, SW_MAXIMIZE);
+      } else {
+        ShowWindow(_hWnd, SW_RESTORE);
+      }
+      _properties.set_maximized(properties.get_maximized());
+
+      if (_properties.get_minimized()) {
+        // Immediately minimize it again
+        ShowWindow(_hWnd, SW_MINIMIZE);
+      }
+    }
+    properties.clear_maximized();
+  }
+
   if (properties.has_fullscreen()) {
     if (properties.get_fullscreen() && !is_fullscreen()) {
       if (do_fullscreen_switch()){
@@ -507,6 +531,7 @@ open_window() {
   }
   bool want_foreground = (!_properties.has_foreground() || _properties.get_foreground());
   bool want_minimized = (_properties.has_minimized() && _properties.get_minimized()) && !want_foreground;
+  bool want_maximized = (_properties.has_maximized() && _properties.get_maximized()) && want_foreground;
 
   HWND old_foreground_window = GetForegroundWindow();
 
@@ -533,6 +558,9 @@ open_window() {
   if (want_minimized) {
     ShowWindow(_hWnd, SW_MINIMIZE);
     ShowWindow(_hWnd, SW_MINIMIZE);
+  } else if (want_maximized) {
+    ShowWindow(_hWnd, SW_MAXIMIZE);
+    ShowWindow(_hWnd, SW_MAXIMIZE);
   } else {
     ShowWindow(_hWnd, SW_SHOWNORMAL);
     ShowWindow(_hWnd, SW_SHOWNORMAL);
@@ -854,6 +882,21 @@ handle_reshape() {
       << "," << properties.get_y_size() << ")\n";
   }
 
+  // Check whether the window has been maximized or unmaximized.
+  WINDOWPLACEMENT pl;
+  pl.length = sizeof(WINDOWPLACEMENT);
+  if (GetWindowPlacement(_hWnd, &pl)) {
+    if (pl.showCmd == SW_SHOWMAXIMIZED || (pl.flags & WPF_RESTORETOMAXIMIZED) != 0) {
+      properties.set_maximized(true);
+    } else {
+      properties.set_maximized(false);
+    }
+  }
+  else if (windisplay_cat.is_debug()) {
+    windisplay_cat.debug()
+      << "GetWindowPlacement() failed in handle_reshape.  Ignoring.\n";
+  }
+
   adjust_z_order();
   system_changed_properties(properties);
 }
@@ -1083,6 +1126,9 @@ calculate_metrics(bool fullscreen, DWORD window_style, WINDOW_METRICS &metrics,
 bool WinGraphicsWindow::
 open_graphic_window() {
   DWORD window_style = make_style(_properties);
+  if (_properties.get_maximized()) {
+    window_style |= WS_MAXIMIZE;
+  }
 
   wstring title;
   if (_properties.has_title()) {

+ 2 - 0
panda/src/x11display/x11GraphicsPipe.cxx

@@ -363,6 +363,8 @@ x11GraphicsPipe(const std::string &display) :
   _net_wm_state_add = XInternAtom(_display, "_NET_WM_STATE_ADD", false);
   _net_wm_state_remove = XInternAtom(_display, "_NET_WM_STATE_REMOVE", false);
   _net_wm_bypass_compositor = XInternAtom(_display, "_NET_WM_BYPASS_COMPOSITOR", false);
+  _net_wm_state_maximized_vert = XInternAtom(_display, "_NET_WM_STATE_MAXIMIZED_VERT", false);
+  _net_wm_state_maximized_horz = XInternAtom(_display, "_NET_WM_STATE_MAXIMIZED_HORZ", false);
 }
 
 /**

+ 2 - 0
panda/src/x11display/x11GraphicsPipe.h

@@ -177,6 +177,8 @@ public:
   Atom _net_wm_state_add;
   Atom _net_wm_state_remove;
   Atom _net_wm_bypass_compositor;
+  Atom _net_wm_state_maximized_vert;
+  Atom _net_wm_state_maximized_horz;
 
   // Extension functions.
   typedef int (*pfn_XcursorGetDefaultSize)(X11_Display *);

+ 75 - 1
panda/src/x11display/x11GraphicsWindow.cxx

@@ -309,6 +309,9 @@ process_events() {
   WindowProperties properties;
   bool changed_properties = false;
 
+  XPropertyEvent property_event;
+  bool got_net_wm_state_change = false;
+
   while (XCheckIfEvent(_display, &event, check_event, (char *)this)) {
     if (got_keyrelease_event) {
       // If a keyrelease event is immediately followed by a matching keypress
@@ -362,6 +365,19 @@ process_events() {
     case ReparentNotify:
       break;
 
+    case PropertyNotify:
+      //std::cout << "PropertyNotify event: atom = " << event.xproperty.atom << std::endl;
+      x11GraphicsPipe *x11_pipe;
+      DCAST_INTO_V(x11_pipe, _pipe);
+      if (event.xproperty.atom == x11_pipe->_net_wm_state) {
+        // currently we're only interested in the net_wm_state type of
+        // changes and only need to gather property informations once at
+        // the end after the while loop
+        property_event = event.xproperty;
+        got_net_wm_state_change = true;
+      }
+      break;
+
     case ConfigureNotify:
       // When resizing or moving the window, multiple ConfigureNotify events
       // may be sent in rapid succession.  We only respond to the last one.
@@ -585,6 +601,45 @@ process_events() {
       }
   }
 
+  if (got_net_wm_state_change) {
+    // some wm state properties have been changed, check their values
+    // once in this part instead of multiple times in the while loop
+
+    // Check if this window is maximized or not
+    bool is_maximized = false;
+    Atom wmState = property_event.atom;
+    Atom type;
+    int format;
+    unsigned long nItem, bytesAfter;
+    unsigned char *new_window_properties = NULL;
+    // gather all properties from the active dispplay and window
+    XGetWindowProperty(_display, _xwindow, wmState, 0, LONG_MAX, false, AnyPropertyType, &type, &format, &nItem, &bytesAfter, &new_window_properties);
+    if (nItem > 0) {
+      x11GraphicsPipe *x11_pipe;
+      DCAST_INTO_V(x11_pipe, _pipe);
+      // run through all found items
+      for (unsigned long iItem = 0; iItem < nItem; ++iItem) {
+        unsigned long item = reinterpret_cast<unsigned long *>(new_window_properties)[iItem];
+        // check if the item is one of the maximized states
+        if (item == x11_pipe->_net_wm_state_maximized_horz ||
+            item == x11_pipe->_net_wm_state_maximized_vert) {
+          // The window was maximized
+          is_maximized = true;
+        }
+      }
+    }
+
+    // Debug entry
+    if (x11display_cat.is_debug()) {
+      x11display_cat.debug()
+        << "set maximized to: " << is_maximized << "\n";
+    }
+
+    // Now make sure the property will get stored correctly
+    properties.set_maximized(is_maximized);
+    changed_properties = true;
+  }
+
   if (changed_properties) {
     system_changed_properties(properties);
   }
@@ -791,6 +846,12 @@ set_properties_now(WindowProperties &properties) {
     properties.clear_fullscreen();
   }
 
+  // Same for maximized.
+  if (properties.has_maximized()) {
+    _properties.set_maximized(properties.get_maximized());
+    properties.clear_maximized();
+  }
+
   // The size and position of an already-open window are changed via explicit
   // X calls.  These may still get intercepted by the window manager.  Rather
   // than changing _properties immediately, we'll wait for the ConfigureNotify
@@ -1101,7 +1162,8 @@ open_window() {
     KeyPressMask | KeyReleaseMask |
     EnterWindowMask | LeaveWindowMask |
     PointerMotionMask |
-    FocusChangeMask | StructureNotifyMask;
+    FocusChangeMask | StructureNotifyMask |
+    PropertyChangeMask;
 
   // Initialize window attributes
   XSetWindowAttributes wa;
@@ -1269,6 +1331,18 @@ set_wm_properties(const WindowProperties &properties, bool already_mapped) {
   SetAction set_data[max_set_data];
   int next_set_data = 0;
 
+  if (properties.has_maximized()) {
+    if (properties.get_maximized()) {
+      state_data[next_state_data++] = x11_pipe->_net_wm_state_maximized_vert;
+      set_data[next_set_data++] = SetAction(x11_pipe->_net_wm_state_maximized_vert, 1);
+      state_data[next_state_data++] = x11_pipe->_net_wm_state_maximized_horz;
+      set_data[next_set_data++] = SetAction(x11_pipe->_net_wm_state_maximized_horz, 1);
+    } else {
+      set_data[next_set_data++] = SetAction(x11_pipe->_net_wm_state_maximized_vert, 0);
+      set_data[next_set_data++] = SetAction(x11_pipe->_net_wm_state_maximized_horz, 0);
+    }
+  }
+
   if (properties.has_fullscreen()) {
     if (properties.get_fullscreen()) {
       // For a "fullscreen" request, we pass this through, hoping the window

+ 3 - 0
pandatool/src/deploy-stub/deploy-stub.c

@@ -655,6 +655,9 @@ int main(int argc, char *argv[]) {
   PyImport_FrozenModules = blobinfo.pointers[0];
   retval = Py_FrozenMain(argc, argv);
 
+  fflush(stdout);
+  fflush(stderr);
+
   unmap_blob(blob);
   return retval;
 }

+ 0 - 1
samples/fireflies/light.sha

@@ -14,7 +14,6 @@ void vshader(float4 vtx_position : POSITION,
 }
 
 void fshader(float4 l_pos: TEXCOORD0,
-             float4 l_scale: TEXCOORD1,
              uniform sampler2D k_texnormal : TEXUNIT0,
              uniform sampler2D k_texalbedo : TEXUNIT1,
              uniform sampler2D k_texdepth  : TEXUNIT2,

+ 20 - 0
tests/display/test_winprops.py

@@ -66,3 +66,23 @@ def test_winprops_size_property():
     # Test clear
     props.size = None
     assert not props.has_size()
+
+
+def test_winprops_maximized_property():
+    props = WindowProperties()
+
+    # Test get
+    props.set_maximized(True)
+    assert props.maximized == True
+
+    # Test has
+    props.clear_maximized()
+    assert props.maximized is None
+
+    # Test set
+    props.maximized = True
+    assert props.get_maximized() == True
+
+    # Test clear
+    props.maximized = None
+    assert not props.has_maximized()

+ 18 - 0
tests/pgraph/test_cullfaceattrib.py

@@ -0,0 +1,18 @@
+from panda3d.core import CullFaceAttrib
+
+
+def test_cullfaceattrib_compare():
+    clockwise1 = CullFaceAttrib.make()
+    clockwise2 = CullFaceAttrib.make()
+    reverse1 = CullFaceAttrib.make_reverse()
+    reverse2 = CullFaceAttrib.make_reverse()
+
+    assert clockwise1.compare_to(clockwise2) == 0
+    assert clockwise2.compare_to(clockwise1) == 0
+
+    assert reverse1.compare_to(reverse2) == 0
+    assert reverse2.compare_to(reverse1) == 0
+
+    assert reverse1.compare_to(clockwise1) != 0
+    assert clockwise1.compare_to(reverse1) != 0
+    assert reverse1.compare_to(clockwise1) == -clockwise1.compare_to(reverse1)

+ 34 - 0
tests/pgraph/test_lightattrib.py

@@ -107,3 +107,37 @@ def test_lightattrib_compare():
     assert lattr1.compare_to(lattr2) != 0
     assert lattr2.compare_to(lattr1) != 0
     assert lattr2.compare_to(lattr1) == -lattr1.compare_to(lattr2)
+
+    # An on light is not the same as an off light
+    lattr1 = core.LightAttrib.make().add_on_light(spot)
+    lattr2 = core.LightAttrib.make().add_off_light(spot)
+    assert lattr1.compare_to(lattr2) != 0
+    assert lattr2.compare_to(lattr1) != 0
+    assert lattr2.compare_to(lattr1) == -lattr1.compare_to(lattr2)
+
+    # If both have the same off light, they are equal
+    lattr1 = core.LightAttrib.make().add_off_light(spot)
+    lattr2 = core.LightAttrib.make().add_off_light(spot)
+    assert lattr1.compare_to(lattr2) == 0
+    assert lattr2.compare_to(lattr1) == 0
+
+    # Off light should not be equal to empty
+    lattr1 = core.LightAttrib.make().add_off_light(spot)
+    lattr2 = core.LightAttrib.make_all_off()
+    assert lattr1.compare_to(lattr2) != 0
+    assert lattr2.compare_to(lattr1) != 0
+    assert lattr2.compare_to(lattr1) == -lattr1.compare_to(lattr2)
+
+    # Off light should not be equal to all-off
+    lattr1 = core.LightAttrib.make().add_off_light(spot)
+    lattr2 = core.LightAttrib.make_all_off()
+    assert lattr1.compare_to(lattr2) != 0
+    assert lattr2.compare_to(lattr1) != 0
+    assert lattr2.compare_to(lattr1) == -lattr1.compare_to(lattr2)
+
+    # Different off lights shouldn't be equal either, of course
+    lattr1 = core.LightAttrib.make().add_off_light(spot)
+    lattr2 = core.LightAttrib.make().add_off_light(point)
+    assert lattr1.compare_to(lattr2) != 0
+    assert lattr2.compare_to(lattr1) != 0
+    assert lattr2.compare_to(lattr1) == -lattr1.compare_to(lattr2)

+ 29 - 0
tests/pgraph/test_nodepath.py

@@ -194,6 +194,35 @@ def test_nodepath_python_tags():
     assert rc1 == rc2
 
 
+def test_nodepath_clear_python_tag():
+    from panda3d.core import NodePath
+
+    path = NodePath("node")
+    assert not path.has_python_tag("a")
+    assert not path.has_python_tag("b")
+    assert not path.node().has_tags()
+
+    path.set_python_tag("a", "value")
+    assert path.has_python_tag("a")
+    assert not path.has_python_tag("b")
+    assert path.node().has_tags()
+
+    path.set_python_tag("b", "value")
+    assert path.has_python_tag("a")
+    assert path.has_python_tag("b")
+    assert path.node().has_tags()
+
+    path.clear_python_tag("a")
+    assert not path.has_python_tag("a")
+    assert path.has_python_tag("b")
+    assert path.node().has_tags()
+
+    path.clear_python_tag("b")
+    assert not path.has_python_tag("a")
+    assert not path.has_python_tag("b")
+    assert not path.node().has_tags()
+
+
 def test_nodepath_replace_texture():
     from panda3d.core import NodePath, Texture
 

+ 5 - 1
tests/pgraph/test_shaderattrib.py

@@ -48,10 +48,14 @@ def test_shaderattrib_compare():
     assert shattr1.compare_to(shattr2) == 0
     assert shattr2.compare_to(shattr1) == 0
 
-    shattr2 = core.ShaderAttrib.make().set_flag(core.ShaderAttrib.F_subsume_alpha_test, True)
+    shattr2 = core.ShaderAttrib.make().set_flag(core.ShaderAttrib.F_subsume_alpha_test, False)
     assert shattr1.compare_to(shattr2) != 0
     assert shattr2.compare_to(shattr1) != 0
 
     shattr1 = core.ShaderAttrib.make().set_flag(core.ShaderAttrib.F_subsume_alpha_test, False)
+    assert shattr1.compare_to(shattr2) == 0
+    assert shattr2.compare_to(shattr1) == 0
+
+    shattr2 = core.ShaderAttrib.make().set_flag(core.ShaderAttrib.F_subsume_alpha_test, True)
     assert shattr1.compare_to(shattr2) != 0
     assert shattr2.compare_to(shattr1) != 0

+ 47 - 0
tests/pgraph/test_textureattrib.py

@@ -9,6 +9,19 @@ tex2 = core.Texture("tex2")
 tex3 = core.Texture("tex3")
 
 
+def test_textureattrib_compose_empty():
+    # Tests a case in which a child node does not alter the original.
+    tattr1 = core.TextureAttrib.make()
+    tattr1 = tattr1.add_on_stage(stage1, tex1)
+
+    tattr2 = core.TextureAttrib.make()
+
+    tattr3 = tattr1.compose(tattr2)
+    assert tattr3.get_num_on_stages() == 1
+
+    assert stage1 in tattr3.on_stages
+
+
 def test_textureattrib_compose_add():
     # Tests a case in which a child node adds another texture.
     tattr1 = core.TextureAttrib.make()
@@ -24,6 +37,21 @@ def test_textureattrib_compose_add():
     assert stage2 in tattr3.on_stages
 
 
+def test_textureattrib_compose_override():
+    # Tests a case in which a child node overrides a texture.
+    tattr1 = core.TextureAttrib.make()
+    tattr1 = tattr1.add_on_stage(stage1, tex1)
+
+    tattr2 = core.TextureAttrib.make()
+    tattr2 = tattr2.add_on_stage(stage1, tex2)
+
+    tattr3 = tattr1.compose(tattr2)
+    assert tattr3.get_num_on_stages() == 1
+
+    assert stage1 in tattr3.on_stages
+    assert tattr3.get_on_texture(stage1) == tex2
+
+
 def test_textureattrib_compose_subtract():
     # Tests a case in which a child node disables a texture.
     tattr1 = core.TextureAttrib.make()
@@ -61,6 +89,25 @@ def test_textureattrib_compose_both():
     assert stage3 in tattr3.on_stages
 
 
+def test_textureattrib_implicit_sort():
+    # Tests that two TextureStages with same sort retain insertion order.
+    tattr1 = core.TextureAttrib.make()
+    tattr1 = tattr1.add_on_stage(stage1, tex1)
+    tattr1 = tattr1.add_on_stage(stage2, tex2)
+
+    assert tattr1.get_on_stage(0) == stage1
+    assert tattr1.get_on_stage(1) == stage2
+
+    tattr2 = core.TextureAttrib.make()
+    tattr2 = tattr1.add_on_stage(stage2, tex2)
+    tattr2 = tattr1.add_on_stage(stage1, tex1)
+
+    assert tattr2.get_on_stage(0) == stage2
+    assert tattr2.get_on_stage(1) == stage1
+
+    assert tattr1.compare_to(tattr2) == -tattr2.compare_to(tattr1)
+
+
 def test_textureattrib_compose_alloff():
     # Tests a case in which a child node disables all textures.
     tattr1 = core.TextureAttrib.make()

+ 17 - 0
tests/putil/test_bitarray.py

@@ -123,3 +123,20 @@ def test_bitarray_pickle():
 
     ba = ~BitArray(94187049178237918273981729127381723)
     assert ba == pickle.loads(pickle.dumps(ba, -1))
+
+
+def test_bitarray_has_any_of():
+    ba = BitArray()
+    assert not ba.has_any_of(100, 200)
+
+    ba = BitArray()
+    ba.set_range(0, 53)
+    assert ba.has_any_of(52, 1)
+    assert ba.has_any_of(52, 100)
+    assert not ba.has_any_of(53, 45)
+
+    ba = BitArray()
+    ba.invert_in_place()
+    assert ba.has_any_of(0, 1)
+    assert ba.has_any_of(53, 45)
+    assert ba.has_any_of(0, 100)

Неке датотеке нису приказане због велике количине промена