Sfoglia il codice sorgente

Merge branch 'master' into vulkan

rdb 7 anni fa
parent
commit
76a3aba82a

+ 1 - 0
.gitignore

@@ -59,3 +59,4 @@ __pycache__/
 # Test tool cache directories
 .tox/
 .cache/
+.pytest_cache/

+ 7 - 0
dtool/src/cppparser/cppStructType.cxx

@@ -545,6 +545,13 @@ is_copy_constructible(CPPVisibility min_vis) const {
     return true;
   }
 
+  if (get_move_constructor() != nullptr ||
+      get_move_assignment_operator() != nullptr) {
+    // A user-declared move constructor or move assignment operator means that
+    // the implicitly-declared copy constructor is deleted.
+    return false;
+  }
+
   CPPInstance *destructor = get_destructor();
   if (destructor != nullptr) {
     if (destructor->_vis > min_vis) {

+ 147 - 158
dtool/src/interrogate/interfaceMakerPythonNative.cxx

@@ -673,12 +673,7 @@ get_valid_child_classes(std::map<std::string, CastDetails> &answer, CPPStructTyp
     return;
   }
 
-  CPPStructType::Derivation::const_iterator bi;
-  for (bi = inclass->_derivation.begin();
-      bi != inclass->_derivation.end();
-      ++bi) {
-
-    const CPPStructType::Base &base = (*bi);
+  for (const CPPStructType::Base &base : inclass->_derivation) {
 // if (base._vis <= V_public) can_downcast = false;
     CPPStructType *base_type = TypeManager::resolve_type(base._base)->as_struct_type();
     if (base_type != nullptr) {
@@ -801,11 +796,10 @@ write_prototypes(ostream &out_code, ostream *out_h) {
   }
 
   /*
-  for (fi = _functions.begin(); fi != _functions.end(); ++fi)
-  {
-      Function *func = (*fi);
-      if (!func->_itype.is_global() && is_function_legal(func))
-        write_prototype_for (out_code, func);
+  for (Function *func : _functions) {
+    if (!func->_itype.is_global() && is_function_legal(func)) {
+      write_prototype_for(out_code, func);
+    }
   }
   */
 
@@ -821,6 +815,11 @@ write_prototypes(ostream &out_code, ostream *out_h) {
           // _external_imports.insert(object->_itype._cpptype);
         }
       }
+    } else if (object->_itype.is_scoped_enum() && isExportThisRun(object->_itype._cpptype)) {
+      // Forward declare where we will put the scoped enum type.
+      string class_name = object->_itype._cpptype->get_local_name(&parser);
+      string safe_name = make_safe_name(class_name);
+      out_code << "static PyTypeObject *Dtool_Ptr_" << safe_name << " = nullptr;\n";
     }
   }
 
@@ -828,8 +827,7 @@ write_prototypes(ostream &out_code, ostream *out_h) {
   out_code << " * Extern declarations for imported classes\n";
   out_code << " */\n";
 
-  for (std::set<CPPType *>::iterator ii = _external_imports.begin(); ii != _external_imports.end(); ii++) {
-    CPPType *type = (*ii);
+  for (CPPType *type : _external_imports) {
     string class_name = type->get_local_name(&parser);
     string safe_name = make_safe_name(class_name);
 
@@ -926,15 +924,13 @@ write_prototypes_class(ostream &out_code, ostream *out_h, Object *obj) {
   out_code << " */\n";
 
   /*
-  for (fi = obj->_methods.begin(); fi != obj->_methods.end(); ++fi) {
-    Function *func = (*fi);
+  for (Function *func : obj->_methods) {
     write_prototype_for(out_code, func);
   }
   */
 
   /*
-  for (fi = obj->_constructors.begin(); fi != obj->_constructors.end(); ++fi) {
-    Function *func = (*fi);
+  for (Function *func : obj->_constructors) {
     std::string fname = "int Dtool_Init_" + ClassName + "(PyObject *self, PyObject *args, PyObject *kwds)";
     write_prototype_for_name(out_code, obj, func, fname);
   }
@@ -993,9 +989,6 @@ write_functions(ostream &out) {
  */
 void InterfaceMakerPythonNative::
 write_class_details(ostream &out, Object *obj) {
-  Functions::iterator fi;
-  Function::Remaps::const_iterator ri;
-
   // std::string cClassName = obj->_itype.get_scoped_name();
   std::string ClassName = make_safe_name(obj->_itype.get_scoped_name());
   std::string cClassName = obj->_itype.get_true_name();
@@ -1005,8 +998,7 @@ write_class_details(ostream &out, Object *obj) {
   out << " */\n";
 
   // First write out all the wrapper functions for the methods.
-  for (fi = obj->_methods.begin(); fi != obj->_methods.end(); ++fi) {
-    Function *func = (*fi);
+  for (Function *func : obj->_methods) {
     if (func) {
       // Write the definition of the generic wrapper function for this
       // function.
@@ -1015,18 +1007,13 @@ write_class_details(ostream &out, Object *obj) {
   }
 
   // Now write out generated getters and setters for the properties.
-  Properties::const_iterator pit;
-  for (pit = obj->_properties.begin(); pit != obj->_properties.end(); ++pit) {
-    Property *property = (*pit);
-
+  for (Property *property : obj->_properties) {
     write_getset(out, obj, property);
   }
 
   // Write the constructors.
   std::string fname = "static int Dtool_Init_" + ClassName + "(PyObject *self, PyObject *args, PyObject *kwds)";
-  for (fi = obj->_constructors.begin(); fi != obj->_constructors.end(); ++fi) {
-    Function *func = (*fi);
-
+  for (Function *func : obj->_constructors) {
     string expected_params;
     write_function_for_name(out, obj, func->_remaps, fname, expected_params, true, AT_keyword_args, RF_int);
   }
@@ -1052,17 +1039,16 @@ write_class_details(ostream &out, Object *obj) {
   }
 
   // Write make seqs: generated methods that return a sequence of items.
-  MakeSeqs::iterator msi;
-  for (msi = obj->_make_seqs.begin(); msi != obj->_make_seqs.end(); ++msi) {
-    if (is_function_legal((*msi)->_length_getter) &&
-        is_function_legal((*msi)->_element_getter)) {
-      write_make_seq(out, obj, ClassName, cClassName, *msi);
+  for (MakeSeq *make_seq : obj->_make_seqs) {
+    if (is_function_legal(make_seq->_length_getter) &&
+        is_function_legal(make_seq->_element_getter)) {
+      write_make_seq(out, obj, ClassName, cClassName, make_seq);
     } else {
-      if (!is_function_legal((*msi)->_length_getter)) {
-        std::cerr << "illegal length function for MAKE_SEQ: " << (*msi)->_length_getter->_name << "\n";
+      if (!is_function_legal(make_seq->_length_getter)) {
+        std::cerr << "illegal length function for MAKE_SEQ: " << make_seq->_length_getter->_name << "\n";
       }
-      if (!is_function_legal((*msi)->_element_getter)) {
-        std::cerr << "illegal element function for MAKE_SEQ: " << (*msi)->_element_getter->_name << "\n";
+      if (!is_function_legal(make_seq->_element_getter)) {
+        std::cerr << "illegal element function for MAKE_SEQ: " << make_seq->_element_getter->_name << "\n";
       }
     }
   }
@@ -1297,11 +1283,11 @@ write_module_support(ostream &out, ostream *out_h, InterrogateModuleDef *def) {
   out << "#ifndef LINK_ALL_STATIC\n";
   out << "  // Resolve externally imported types.\n";
 
-  for (std::set<CPPType *>::iterator ii = _external_imports.begin(); ii != _external_imports.end(); ++ii) {
-    string class_name = (*ii)->get_local_name(&parser);
+  for (CPPType *type : _external_imports) {
+    string class_name = type->get_local_name(&parser);
     string safe_name = make_safe_name(class_name);
 
-    if (has_get_class_type_function(*ii)) {
+    if (has_get_class_type_function(type)) {
       out << "  Dtool_Ptr_" << safe_name << " = LookupRuntimeTypedClass(" << class_name << "::get_class_type());\n";
     } else {
       out << "  Dtool_Ptr_" << safe_name << " = LookupNamedClass(\"" << class_name << "\");\n";
@@ -1320,28 +1306,36 @@ write_module_support(ostream &out, ostream *out_h, InterrogateModuleDef *def) {
       int enum_count = object->_itype.number_of_enum_values();
 
       if (object->_itype.is_scoped_enum()) {
-        // Convert as Python 3.4 enum.
+        // Convert as Python 3.4-style enum.
+        string class_name = object->_itype._cpptype->get_local_name(&parser);
+        string safe_name = make_safe_name(class_name);
+
         CPPType *underlying_type = TypeManager::unwrap_const(object->_itype._cpptype->as_enum_type()->get_underlying_type());
         string cast_to = underlying_type->get_local_name(&parser);
-        out << "#if PY_VERSION_HEX >= 0x03040000\n\n";
         out << "  // enum class " << object->_itype.get_scoped_name() << "\n";
         out << "  {\n";
         out << "    PyObject *members = PyTuple_New(" << enum_count << ");\n";
         out << "    PyObject *member;\n";
         for (int xx = 0; xx < enum_count; xx++) {
           out << "    member = PyTuple_New(2);\n"
-                 "    PyTuple_SET_ITEM(member, 0, PyUnicode_FromString(\""
+                 "#if PY_MAJOR_VERSION >= 3\n"
+                 "      PyTuple_SET_ITEM(member, 0, PyUnicode_FromString(\""
+              << object->_itype.get_enum_value_name(xx) << "\"));\n"
+                 "#else\n"
+                 "      PyTuple_SET_ITEM(member, 0, PyString_FromString(\""
               << object->_itype.get_enum_value_name(xx) << "\"));\n"
+                 "#endif\n"
                  "    PyTuple_SET_ITEM(member, 1, Dtool_WrapValue(("
               << cast_to << ")" << object->_itype.get_scoped_name() << "::"
               << object->_itype.get_enum_value_name(xx) << "));\n"
                  "    PyTuple_SET_ITEM(members, " << xx << ", member);\n";
         }
+        out << "    Dtool_Ptr_" << safe_name << " = Dtool_EnumType_Create(\""
+            << object->_itype.get_name() << "\", members, \""
+            << _def->module_name << "\");\n";
         out << "    PyModule_AddObject(module, \"" << object->_itype.get_name()
-            << "\", Dtool_EnumType_Create(\"" << object->_itype.get_name()
-            << "\", members, \"" << _def->module_name << "\"));\n";
+            << "\", (PyObject *)Dtool_Ptr_" << safe_name << ");\n";
         out << "  }\n";
-        out << "#endif\n";
       } else {
         out << "  // enum " << object->_itype.get_scoped_name() << "\n";
         for (int xx = 0; xx < enum_count; xx++) {
@@ -1555,7 +1549,6 @@ write_module_class(ostream &out, Object *obj) {
     is_runtime_typed = true;
   }
 
-  Functions::iterator fi;
   out << "/**\n";
   out << " * Python method tables for " << ClassName << " (" << export_class_name << ")\n" ;
   out << " */\n";
@@ -1566,8 +1559,7 @@ write_module_class(ostream &out, Object *obj) {
   bool got_copy = false;
   bool got_deepcopy = false;
 
-  for (fi = obj->_methods.begin(); fi != obj->_methods.end(); ++fi) {
-    Function *func = (*fi);
+  for (Function *func : obj->_methods) {
     if (func->_name == "__copy__") {
       got_copy = true;
     } else if (func->_name == "__deepcopy__") {
@@ -1604,9 +1596,7 @@ write_module_class(ostream &out, Object *obj) {
 
     bool has_nonslotted = false;
 
-    Function::Remaps::const_iterator ri;
-    for (ri = func->_remaps.begin(); ri != func->_remaps.end(); ++ri) {
-      FunctionRemap *remap = (*ri);
+    for (FunctionRemap *remap : func->_remaps) {
       if (!is_remap_legal(remap)) {
         continue;
       }
@@ -1698,9 +1688,7 @@ write_module_class(ostream &out, Object *obj) {
     out << "  {\"__deepcopy__\", &map_deepcopy_to_copy, METH_VARARGS, nullptr},\n";
   }
 
-  MakeSeqs::iterator msi;
-  for (msi = obj->_make_seqs.begin(); msi != obj->_make_seqs.end(); ++msi) {
-    MakeSeq *make_seq = (*msi);
+  for (MakeSeq *make_seq : obj->_make_seqs) {
     if (!is_function_legal(make_seq->_length_getter) ||
         !is_function_legal(make_seq->_element_getter)) {
       continue;
@@ -1876,10 +1864,7 @@ write_module_class(ostream &out, Object *obj) {
 
           // This function handles both delattr and setattr.  Fish out the
           // remaps for both types.
-          set<FunctionRemap*>::const_iterator ri;
-          for (ri = def._remaps.begin(); ri != def._remaps.end(); ++ri) {
-            FunctionRemap *remap = (*ri);
-
+          for (FunctionRemap *remap : def._remaps) {
             if (remap->_cppfunc->get_simple_name() == "__delattr__" && remap->_parameters.size() == 2) {
               delattr_remaps.insert(remap);
 
@@ -2029,10 +2014,7 @@ write_module_class(ostream &out, Object *obj) {
 
           // This function handles both delitem and setitem.  Fish out the
           // remaps for either one.
-          set<FunctionRemap*>::const_iterator ri;
-          for (ri = def._remaps.begin(); ri != def._remaps.end(); ++ri) {
-            FunctionRemap *remap = (*ri);
-
+          for (FunctionRemap *remap : def._remaps) {
             if (remap->_flags & FunctionRemap::F_setitem_int) {
               setitem_remaps.insert(remap);
 
@@ -2098,10 +2080,7 @@ write_module_class(ostream &out, Object *obj) {
 
           // This function handles both delitem and setitem.  Fish out the
           // remaps for either one.
-          set<FunctionRemap*>::const_iterator ri;
-          for (ri = def._remaps.begin(); ri != def._remaps.end(); ++ri) {
-            FunctionRemap *remap = (*ri);
-
+          for (FunctionRemap *remap : def._remaps) {
             if (remap->_flags & FunctionRemap::F_setitem) {
               setitem_remaps.insert(remap);
 
@@ -2185,9 +2164,7 @@ write_module_class(ostream &out, Object *obj) {
 
           // Iterate through the remaps to find the one that matches our
           // parameters.
-          set<FunctionRemap*>::const_iterator ri;
-          for (ri = def._remaps.begin(); ri != def._remaps.end(); ++ri) {
-            FunctionRemap *remap = (*ri);
+          for (FunctionRemap *remap : def._remaps) {
             if (remap->_const_method) {
               if ((remap->_flags & FunctionRemap::F_explicit_self) == 0) {
                 params_const.push_back("self");
@@ -2254,9 +2231,7 @@ write_module_class(ostream &out, Object *obj) {
 
           // Iterate through the remaps to find the one that matches our
           // parameters.
-          set<FunctionRemap*>::const_iterator ri;
-          for (ri = def._remaps.begin(); ri != def._remaps.end(); ++ri) {
-            FunctionRemap *remap = (*ri);
+          for (FunctionRemap *remap : def._remaps) {
             if (remap->_const_method) {
               if ((remap->_flags & FunctionRemap::F_explicit_self) == 0) {
                 params_const.push_back("self");
@@ -2340,10 +2315,7 @@ write_module_class(ostream &out, Object *obj) {
           set<FunctionRemap*> one_param_remaps;
           set<FunctionRemap*> two_param_remaps;
 
-          set<FunctionRemap*>::const_iterator ri;
-          for (ri = def._remaps.begin(); ri != def._remaps.end(); ++ri) {
-            FunctionRemap *remap = (*ri);
-
+          for (FunctionRemap *remap : def._remaps) {
             if (remap->_parameters.size() == 2) {
               one_param_remaps.insert(remap);
 
@@ -2537,17 +2509,14 @@ write_module_class(ostream &out, Object *obj) {
     out << "    return nullptr;\n";
     out << "  }\n\n";
 
-    for (fi = obj->_methods.begin(); fi != obj->_methods.end(); ++fi) {
+    for (Function *func : obj->_methods) {
       std::set<FunctionRemap*> remaps;
-      Function *func = (*fi);
       if (!func) {
         continue;
       }
       // We only accept comparison operators that take one parameter (besides
       // 'this').
-      Function::Remaps::const_iterator ri;
-      for (ri = func->_remaps.begin(); ri != func->_remaps.end(); ++ri) {
-        FunctionRemap *remap = (*ri);
+      for (FunctionRemap *remap : func->_remaps) {
         if (is_remap_legal(remap) && remap->_has_this && (remap->_args_type == AT_single_arg)) {
           remaps.insert(remap);
         }
@@ -2634,9 +2603,7 @@ write_module_class(ostream &out, Object *obj) {
   if (obj->_properties.size() > 0) {
     // Write out the array of properties, telling Python which getter and
     // setter to call when they are assigned or queried in Python code.
-    Properties::const_iterator pit;
-    for (pit = obj->_properties.begin(); pit != obj->_properties.end(); ++pit) {
-      Property *property = (*pit);
+    for (Property *property : obj->_properties) {
       const InterrogateElement &ielem = property->_ielement;
       if (!property->_has_this || property->_getter_remaps.empty()) {
         continue;
@@ -3060,10 +3027,10 @@ write_module_class(ostream &out, Object *obj) {
   out << "    // Dependent objects\n";
   if (bases.size() > 0) {
     string baseargs;
-    for (std::vector<CPPType*>::iterator bi = bases.begin(); bi != bases.end(); ++bi) {
-      string safe_name = make_safe_name((*bi)->get_local_name(&parser));
+    for (CPPType *base : bases) {
+      string safe_name = make_safe_name(base->get_local_name(&parser));
 
-      if (isExportThisRun(*bi)) {
+      if (isExportThisRun(base)) {
         baseargs += ", (PyTypeObject *)&Dtool_" + safe_name;
         out << "    Dtool_PyModuleClassInit_" << safe_name << "(nullptr);\n";
 
@@ -3158,29 +3125,37 @@ write_module_class(ostream &out, Object *obj) {
       // support recently.
 
     } else if (nested_obj->_itype.is_scoped_enum()) {
-      // Convert enum class as Python 3.4 enum.
+      // Convert enum class as Python 3.4-style enum.
+      string class_name = nested_obj->_itype._cpptype->get_local_name(&parser);
+      string safe_name = make_safe_name(class_name);
+
       int enum_count = nested_obj->_itype.number_of_enum_values();
       CPPType *underlying_type = TypeManager::unwrap_const(nested_obj->_itype._cpptype->as_enum_type()->get_underlying_type());
       string cast_to = underlying_type->get_local_name(&parser);
-      out << "#if PY_VERSION_HEX >= 0x03040000\n\n";
       out << "    // enum class " << nested_obj->_itype.get_scoped_name() << ";\n";
       out << "    {\n";
       out << "      PyObject *members = PyTuple_New(" << enum_count << ");\n";
       out << "      PyObject *member;\n";
       for (int xx = 0; xx < enum_count; xx++) {
         out << "      member = PyTuple_New(2);\n"
+               "#if PY_MAJOR_VERSION >= 3\n"
                "      PyTuple_SET_ITEM(member, 0, PyUnicode_FromString(\""
             << nested_obj->_itype.get_enum_value_name(xx) << "\"));\n"
+               "#else\n"
+               "      PyTuple_SET_ITEM(member, 0, PyString_FromString(\""
+            << nested_obj->_itype.get_enum_value_name(xx) << "\"));\n"
+               "#endif\n"
                "      PyTuple_SET_ITEM(member, 1, Dtool_WrapValue(("
             << cast_to << ")" << nested_obj->_itype.get_scoped_name() << "::"
             << nested_obj->_itype.get_enum_value_name(xx) << "));\n"
                "      PyTuple_SET_ITEM(members, " << xx << ", member);\n";
       }
+      out << "      Dtool_Ptr_" << safe_name << " = Dtool_EnumType_Create(\""
+          << nested_obj->_itype.get_name() << "\", members, \""
+          << _def->module_name << "\");\n";
       out << "      PyDict_SetItemString(dict, \"" << nested_obj->_itype.get_name()
-          << "\", Dtool_EnumType_Create(\"" << nested_obj->_itype.get_name()
-          << "\", members, \"" << _def->module_name << "\"));\n";
+          << "\", (PyObject *)Dtool_Ptr_" << safe_name << ");\n";
       out << "    }\n";
-      out << "#endif\n";
 
     } else if (nested_obj->_itype.is_enum()) {
       out << "    // enum " << nested_obj->_itype.get_scoped_name() << ";\n";
@@ -3207,9 +3182,7 @@ write_module_class(ostream &out, Object *obj) {
   }
 
   // Also add the static properties, which can't be added via getset.
-  Properties::const_iterator pit;
-  for (pit = obj->_properties.begin(); pit != obj->_properties.end(); ++pit) {
-    Property *property = (*pit);
+  for (Property *property : obj->_properties) {
     const InterrogateElement &ielem = property->_ielement;
     if (property->_has_this || property->_getter_remaps.empty()) {
       continue;
@@ -3354,9 +3327,7 @@ write_function_for_top(ostream &out, InterfaceMaker::Object *obj, InterfaceMaker
   // should even write it.
   bool has_remaps = false;
 
-  Function::Remaps::const_iterator ri;
-  for (ri = func->_remaps.begin(); ri != func->_remaps.end(); ++ri) {
-    FunctionRemap *remap = (*ri);
+  for (FunctionRemap *remap : func->_remaps) {
     if (!is_remap_legal(remap)) {
       continue;
     }
@@ -3805,18 +3776,12 @@ void InterfaceMakerPythonNative::
 write_coerce_constructor(ostream &out, Object *obj, bool is_const) {
   std::map<int, std::set<FunctionRemap *> > map_sets;
   std::map<int, std::set<FunctionRemap *> >::iterator mii;
-  std::set<FunctionRemap *>::iterator sii;
 
   int max_required_args = 0;
 
-  Functions::iterator fi;
-  Function::Remaps::const_iterator ri;
-
   // Go through the methods and find appropriate static make() functions.
-  for (fi = obj->_methods.begin(); fi != obj->_methods.end(); ++fi) {
-    Function *func = (*fi);
-    for (ri = func->_remaps.begin(); ri != func->_remaps.end(); ++ri) {
-      FunctionRemap *remap = (*ri);
+  for (Function *func : obj->_methods) {
+    for (FunctionRemap *remap : func->_remaps) {
       if (is_remap_legal(remap) && remap->_flags & FunctionRemap::F_coerce_constructor) {
         nassertd(!remap->_has_this) continue;
 
@@ -3850,10 +3815,8 @@ write_coerce_constructor(ostream &out, Object *obj, bool is_const) {
 
   // Now go through the constructors that are suitable for coercion.  This
   // excludes copy constructors and ones marked "explicit".
-  for (fi = obj->_constructors.begin(); fi != obj->_constructors.end(); ++fi) {
-    Function *func = (*fi);
-    for (ri = func->_remaps.begin(); ri != func->_remaps.end(); ++ri) {
-      FunctionRemap *remap = (*ri);
+  for (Function *func : obj->_constructors) {
+    for (FunctionRemap *remap : func->_remaps) {
       if (is_remap_legal(remap) && remap->_flags & FunctionRemap::F_coerce_constructor) {
         nassertd(!remap->_has_this) continue;
 
@@ -4879,6 +4842,46 @@ write_function_instance(ostream &out, FunctionRemap *remap,
       clear_error = true;
       only_pyobjects = false;
 
+    } else if (TypeManager::is_scoped_enum(type)) {
+      if (args_type == AT_single_arg) {
+        param_name = "arg";
+      } else {
+        indent(out, indent_level) << "PyObject *" << param_name;
+        if (default_value != nullptr) {
+          out << " = nullptr";
+        }
+        out << ";\n";
+        format_specifiers += "O";
+        parameter_list += ", &" + param_name;
+      }
+
+      CPPEnumType *enum_type = (CPPEnumType *)TypeManager::unwrap(type);
+      CPPType *underlying_type = enum_type->get_underlying_type();
+      underlying_type = TypeManager::unwrap_const(underlying_type);
+
+      //indent(out, indent_level);
+      //underlying_type->output_instance(out, param_name + "_val", &parser);
+      //out << default_expr << ";\n";
+      extra_convert << "long " << param_name << "_val";
+
+      if (default_value != nullptr) {
+        extra_convert << " = (long)";
+        default_value->output(extra_convert, 0, &parser, false);
+        extra_convert <<
+          ";\nif (" << param_name << " != nullptr) {\n"
+          "  " << param_name << "_val = Dtool_EnumValue_AsLong(" + param_name + ");\n"
+          "}";
+      } else {
+        extra_convert
+          << ";\n"
+          << param_name << "_val = Dtool_EnumValue_AsLong(" + param_name + ");\n";
+      }
+
+      pexpr_string = "(" + enum_type->get_local_name(&parser) + ")" + param_name + "_val";
+      expected_params += classNameFromCppName(enum_type->get_simple_name(), false);
+      extra_param_check << " && " << param_name << "_val != -1";
+      clear_error = true;
+
     } else if (TypeManager::is_bool(type)) {
       if (args_type == AT_single_arg) {
         param_name = "arg";
@@ -6278,7 +6281,24 @@ pack_return_value(ostream &out, int indent_level, FunctionRemap *remap,
   CPPType *orig_type = return_type->get_orig_type();
   CPPType *type = return_type->get_new_type();
 
-  if (return_type->new_type_is_atomic_string() ||
+  if (TypeManager::is_scoped_enum(type)) {
+    InterrogateDatabase *idb = InterrogateDatabase::get_ptr();
+    TypeIndex type_index = builder.get_type(TypeManager::unwrap(TypeManager::resolve_type(orig_type)), false);
+    const InterrogateType &itype = idb->get_type(type_index);
+    string safe_name = make_safe_name(itype.get_scoped_name());
+
+    indent(out, indent_level)
+      << "return PyObject_CallFunction((PyObject *)Dtool_Ptr_" << safe_name;
+
+    CPPType *underlying_type = ((CPPEnumType *)itype._cpptype)->get_underlying_type();
+    if (TypeManager::is_unsigned_integer(underlying_type)) {
+      out << ", \"k\", (unsigned long)";
+    } else {
+      out << ", \"l\", (long)";
+    }
+    out << "(" << return_expr << "));\n";
+
+  } else if (return_type->new_type_is_atomic_string() ||
       TypeManager::is_simple(type) ||
       TypeManager::is_char_pointer(type) ||
       TypeManager::is_wchar_pointer(type) ||
@@ -6504,11 +6524,7 @@ write_getset(ostream &out, Object *obj, Property *property) {
     std::set<FunctionRemap*> remaps;
 
     // Extract only the getters that take one integral argument.
-    Function::Remaps::iterator it;
-    for (it = property->_getter_remaps.begin();
-          it != property->_getter_remaps.end();
-          ++it) {
-      FunctionRemap *remap = *it;
+    for (FunctionRemap *remap : property->_getter_remaps) {
       int min_num_args = remap->get_min_num_args();
       int max_num_args = remap->get_max_num_args();
       if (min_num_args <= 1 && max_num_args >= 1 &&
@@ -6573,11 +6589,7 @@ write_getset(ostream &out, Object *obj, Property *property) {
       std::set<FunctionRemap*> remaps;
 
       // Extract only the setters that take two arguments.
-      Function::Remaps::iterator it;
-      for (it = property->_setter_remaps.begin();
-           it != property->_setter_remaps.end();
-           ++it) {
-        FunctionRemap *remap = *it;
+      for (FunctionRemap *remap : property->_setter_remaps) {
         int min_num_args = remap->get_min_num_args();
         int max_num_args = remap->get_max_num_args();
         if (min_num_args <= 2 && max_num_args >= 2 &&
@@ -6675,11 +6687,7 @@ write_getset(ostream &out, Object *obj, Property *property) {
     std::set<FunctionRemap*> remaps;
     // Extract only the getters that take one argument.  Fish out the ones
     // already taken by the sequence getter.
-    Function::Remaps::iterator it;
-    for (it = property->_getter_remaps.begin();
-          it != property->_getter_remaps.end();
-          ++it) {
-      FunctionRemap *remap = *it;
+    for (FunctionRemap *remap : property->_getter_remaps) {
       int min_num_args = remap->get_min_num_args();
       int max_num_args = remap->get_max_num_args();
       if (min_num_args <= 1 && max_num_args >= 1 &&
@@ -6808,11 +6816,7 @@ write_getset(ostream &out, Object *obj, Property *property) {
       std::set<FunctionRemap*> remaps;
 
       // Extract only the getters that take one integral argument.
-      Function::Remaps::iterator it;
-      for (it = property->_getkey_function->_remaps.begin();
-            it != property->_getkey_function->_remaps.end();
-            ++it) {
-        FunctionRemap *remap = *it;
+      for (FunctionRemap *remap : property->_getkey_function->_remaps) {
         int min_num_args = remap->get_min_num_args();
         int max_num_args = remap->get_max_num_args();
         if (min_num_args <= 1 && max_num_args >= 1 &&
@@ -6969,11 +6973,7 @@ write_getset(ostream &out, Object *obj, Property *property) {
       std::set<FunctionRemap*> remaps;
 
       // Extract only the setters that take one argument.
-      Function::Remaps::iterator it;
-      for (it = property->_setter_remaps.begin();
-            it != property->_setter_remaps.end();
-            ++it) {
-        FunctionRemap *remap = *it;
+      for (FunctionRemap *remap : property->_setter_remaps) {
         int min_num_args = remap->get_min_num_args();
         int max_num_args = remap->get_max_num_args();
         if (min_num_args <= 1 && max_num_args >= 1) {
@@ -7304,6 +7304,10 @@ is_cpp_type_legal(CPPType *in_ctype) {
 
   // bool answer = false;
   CPPType *type = TypeManager::resolve_type(in_ctype);
+  if (TypeManager::is_rvalue_reference(type)) {
+    return false;
+  }
+
   type = TypeManager::unwrap(type);
 
   if (TypeManager::is_void(type)) {
@@ -7362,9 +7366,7 @@ isExportThisRun(Function *func) {
     return false;
   }
 
-  Function::Remaps::const_iterator ri;
-  for (ri = func->_remaps.begin(); ri != func->_remaps.end();) {
-    FunctionRemap *remap = (*ri);
+  for (FunctionRemap *remap : func->_remaps) {
     return isExportThisRun(remap->_cpptype);
   }
 
@@ -7439,10 +7441,7 @@ has_coerce_constructor(CPPStructType *type) {
   CPPScope::Functions::iterator fgi;
   for (fgi = scope->_functions.begin(); fgi != scope->_functions.end(); ++fgi) {
     CPPFunctionGroup *fgroup = fgi->second;
-
-    CPPFunctionGroup::Instances::iterator ii;
-    for (ii = fgroup->_instances.begin(); ii != fgroup->_instances.end(); ++ii) {
-      CPPInstance *inst = (*ii);
+    for (CPPInstance *inst : fgroup->_instances) {
       CPPFunctionType *ftype = inst->_type->as_function_type();
       if (ftype == nullptr) {
         continue;
@@ -7527,9 +7526,7 @@ is_remap_coercion_possible(FunctionRemap *remap) {
  */
 bool InterfaceMakerPythonNative::
 is_function_legal(Function *func) {
-  Function::Remaps::const_iterator ri;
-  for (ri = func->_remaps.begin(); ri != func->_remaps.end(); ++ri) {
-    FunctionRemap *remap = (*ri);
+  for (FunctionRemap *remap : func->_remaps) {
     if (is_remap_legal(remap)) {
 // printf("  Function Is Marked Legal %s\n",func->_name.c_str());
 
@@ -7575,13 +7572,7 @@ DoesInheritFromIsClass(const CPPStructType *inclass, const std::string &name) {
     return true;
   }
 
-  CPPStructType::Derivation::const_iterator bi;
-  for (bi = inclass->_derivation.begin();
-      bi != inclass->_derivation.end();
-      ++bi) {
-
-    const CPPStructType::Base &base = (*bi);
-
+  for (const CPPStructType::Base &base : inclass->_derivation) {
     CPPStructType *base_type = TypeManager::resolve_type(base._base)->as_struct_type();
     if (base_type != nullptr) {
       if (DoesInheritFromIsClass(base_type, name)) {
@@ -7632,9 +7623,7 @@ has_init_type_function(CPPType *type) {
   }
   const CPPFunctionGroup *group = it->second;
 
-  CPPFunctionGroup::Instances::const_iterator ii;
-  for (ii = group->_instances.begin(); ii != group->_instances.end(); ++ii) {
-    const CPPInstance *cppinst = *ii;
+  for (const CPPInstance *cppinst : group->_instances) {
     const CPPFunctionType *cppfunc = cppinst->_type->as_function_type();
 
     if (cppfunc != nullptr &&

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

@@ -124,6 +124,26 @@ is_reference(CPPType *type) {
   }
 }
 
+/**
+ * Returns true if the indicated type is some kind of an rvalue reference.
+ */
+bool TypeManager::
+is_rvalue_reference(CPPType *type) {
+  switch (type->get_subtype()) {
+  case CPPDeclaration::ST_const:
+    return is_rvalue_reference(type->as_const_type()->_wrapped_around);
+
+  case CPPDeclaration::ST_reference:
+    return type->as_reference_type()->_value_category == CPPReferenceType::VC_rvalue;
+
+  case CPPDeclaration::ST_typedef:
+    return is_rvalue_reference(type->as_typedef_type()->_type);
+
+  default:
+    return false;
+  }
+}
+
 /**
  * Returns true if the indicated type is some kind of a reference or const
  * reference type at all, false otherwise.
@@ -310,6 +330,26 @@ is_struct(CPPType *type) {
   }
 }
 
+/**
+ * Returns true if the indicated type is an enum class, const or otherwise.
+ */
+bool TypeManager::
+is_scoped_enum(CPPType *type) {
+  switch (type->get_subtype()) {
+  case CPPDeclaration::ST_enum:
+    return ((CPPEnumType *)type)->is_scoped();
+
+  case CPPDeclaration::ST_const:
+    return is_scoped_enum(type->as_const_type()->_wrapped_around);
+
+  case CPPDeclaration::ST_typedef:
+    return is_scoped_enum(type->as_typedef_type()->_type);
+
+  default:
+    return false;
+  }
+}
+
 /**
  * Returns true if the indicated type is some kind of enumerated type, const
  * or otherwise.

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

@@ -44,6 +44,7 @@ public:
   static bool is_assignable(CPPType *type);
 
   static bool is_reference(CPPType *type);
+  static bool is_rvalue_reference(CPPType *type);
   static bool is_ref_to_anything(CPPType *type);
   static bool is_const_ref_to_anything(CPPType *type);
   static bool is_const_pointer_to_anything(CPPType *type);
@@ -52,6 +53,7 @@ public:
   static bool is_pointer(CPPType *type);
   static bool is_const(CPPType *type);
   static bool is_struct(CPPType *type);
+  static bool is_scoped_enum(CPPType *type);
   static bool is_enum(CPPType *type);
   static bool is_const_enum(CPPType *type);
   static bool is_const_ref_to_enum(CPPType *type);

+ 14 - 0
dtool/src/interrogatedb/py_panda.I

@@ -97,6 +97,20 @@ INLINE PyObject *DtoolInstance_RichComparePointers(PyObject *v1, PyObject *v2, i
   Py_RETURN_RICHCOMPARE(cmpval, 0, op);
 }
 
+/**
+ * Converts the enum value to a C long.
+ */
+INLINE long Dtool_EnumValue_AsLong(PyObject *value) {
+  PyObject *val = PyObject_GetAttrString(value, "value");
+  if (val != nullptr) {
+    long as_long = PyLongOrInt_AS_LONG(val);
+    Py_DECREF(val);
+    return as_long;
+  } else {
+    return -1;
+  }
+}
+
 /**
  * These functions wrap a pointer for a class that defines get_type_handle().
  */

+ 88 - 3
dtool/src/interrogatedb/py_panda.cxx

@@ -306,11 +306,39 @@ PyObject *_Dtool_Return(PyObject *value) {
   return value;
 }
 
+#if PY_VERSION_HEX < 0x03040000
+static PyObject *Dtool_EnumType_Str(PyObject *self) {
+  PyObject *name = PyObject_GetAttrString(self, "name");
+#if PY_MAJOR_VERSION >= 3
+  PyObject *repr = PyUnicode_FromFormat("%s.%s", Py_TYPE(self)->tp_name, PyString_AS_STRING(name));
+#else
+  PyObject *repr = PyString_FromFormat("%s.%s", Py_TYPE(self)->tp_name, PyString_AS_STRING(name));
+#endif
+  Py_DECREF(name);
+  return repr;
+}
+
+static PyObject *Dtool_EnumType_Repr(PyObject *self) {
+  PyObject *name = PyObject_GetAttrString(self, "name");
+  PyObject *value = PyObject_GetAttrString(self, "value");
+#if PY_MAJOR_VERSION >= 3
+  PyObject *repr = PyUnicode_FromFormat("<%s.%s: %ld>", Py_TYPE(self)->tp_name, PyString_AS_STRING(name), PyLongOrInt_AS_LONG(value));
+#else
+  PyObject *repr = PyString_FromFormat("<%s.%s: %ld>", Py_TYPE(self)->tp_name, PyString_AS_STRING(name), PyLongOrInt_AS_LONG(value));
+#endif
+  Py_DECREF(name);
+  Py_DECREF(value);
+  return repr;
+}
+#endif
+
 /**
- * Creates a Python 3.4-style enum type.  Steals reference to 'names'.
+ * Creates a Python 3.4-style enum type.  Steals reference to 'names', which
+ * should be a tuple of (name, value) pairs.
  */
-PyObject *Dtool_EnumType_Create(const char *name, PyObject *names, const char *module) {
+PyTypeObject *Dtool_EnumType_Create(const char *name, PyObject *names, const char *module) {
   static PyObject *enum_class = nullptr;
+#if PY_VERSION_HEX >= 0x03040000
   static PyObject *enum_meta = nullptr;
   static PyObject *enum_create = nullptr;
   if (enum_meta == nullptr) {
@@ -325,12 +353,69 @@ PyObject *Dtool_EnumType_Create(const char *name, PyObject *names, const char *m
 
   PyObject *result = PyObject_CallFunction(enum_create, (char *)"OsN", enum_class, name, names);
   nassertr(result != nullptr, nullptr);
+#else
+  static PyObject *name_str;
+  static PyObject *name_sunder_str;
+  static PyObject *value_str;
+  static PyObject *value_sunder_str;
+  // Emulate something vaguely like the enum module.
+  if (enum_class == nullptr) {
+#if PY_MAJOR_VERSION >= 3
+    name_str = PyUnicode_InternFromString("name");
+    value_str = PyUnicode_InternFromString("value");
+    name_sunder_str = PyUnicode_InternFromString("_name_");
+    value_sunder_str = PyUnicode_InternFromString("_value_");
+#else
+    name_str = PyString_InternFromString("name");
+    value_str = PyString_InternFromString("value");
+    name_sunder_str = PyString_InternFromString("_name_");
+    value_sunder_str = PyString_InternFromString("_value_");
+#endif
+    PyObject *name_value_tuple = PyTuple_New(4);
+    PyTuple_SET_ITEM(name_value_tuple, 0, name_str);
+    PyTuple_SET_ITEM(name_value_tuple, 1, value_str);
+    PyTuple_SET_ITEM(name_value_tuple, 2, name_sunder_str);
+    PyTuple_SET_ITEM(name_value_tuple, 3, value_sunder_str);
+    Py_INCREF(name_str);
+    Py_INCREF(value_str);
+
+    PyObject *slots_dict = PyDict_New();
+    PyDict_SetItemString(slots_dict, "__slots__", name_value_tuple);
+    Py_DECREF(name_value_tuple);
+
+    enum_class = PyObject_CallFunction((PyObject *)&PyType_Type, (char *)"s()N", "Enum", slots_dict);
+    nassertr(enum_class != nullptr, nullptr);
+  }
+  PyObject *result = PyObject_CallFunction((PyObject *)&PyType_Type, (char *)"s(O)N", name, enum_class, PyDict_New());
+  nassertr(result != nullptr, nullptr);
+
+  ((PyTypeObject *)result)->tp_str = Dtool_EnumType_Str;
+  ((PyTypeObject *)result)->tp_repr = Dtool_EnumType_Repr;
+
+  // Copy the names as instances of the above to the class dict.
+  Py_ssize_t size = PyTuple_GET_SIZE(names);
+  for (Py_ssize_t i = 0; i < size; ++i) {
+    PyObject *item = PyTuple_GET_ITEM(names, i);
+    PyObject *name = PyTuple_GET_ITEM(item, 0);
+    PyObject *value = PyTuple_GET_ITEM(item, 1);
+    PyObject *member = _PyObject_CallNoArg(result);
+    PyObject_SetAttr(member, name_str, name);
+    PyObject_SetAttr(member, name_sunder_str, name);
+    PyObject_SetAttr(member, value_str, value);
+    PyObject_SetAttr(member, value_sunder_str, value);
+    PyObject_SetAttr(result, name, member);
+    Py_DECREF(member);
+  }
+  Py_DECREF(names);
+#endif
+
   if (module != nullptr) {
     PyObject *modstr = PyUnicode_FromString(module);
     PyObject_SetAttrString(result, "__module__", modstr);
     Py_DECREF(modstr);
   }
-  return result;
+  nassertr(PyType_Check(result), nullptr);
+  return (PyTypeObject *)result;
 }
 
 /**

+ 15 - 3
dtool/src/interrogatedb/py_panda.h

@@ -158,6 +158,16 @@ static void Dtool_FreeInstance_##CLASS_NAME(PyObject *self) {\
   Py_TYPE(self)->tp_free(self);\
 }
 
+#define Define_Dtool_FreeInstanceRef_Private(CLASS_NAME,CNAME)\
+static void Dtool_FreeInstance_##CLASS_NAME(PyObject *self) {\
+  if (DtoolInstance_VOID_PTR(self) != nullptr) {\
+    if (((Dtool_PyInstDef *)self)->_memory_rules) {\
+      unref_delete((ReferenceCount *)(CNAME *)DtoolInstance_VOID_PTR(self));\
+    }\
+  }\
+  Py_TYPE(self)->tp_free(self);\
+}
+
 #define Define_Dtool_Simple_FreeInstance(CLASS_NAME, CNAME)\
 static void Dtool_FreeInstance_##CLASS_NAME(PyObject *self) {\
   ((Dtool_InstDef_##CLASS_NAME *)self)->_value.~##CLASS_NAME();\
@@ -248,8 +258,10 @@ EXPCL_INTERROGATEDB PyObject *_Dtool_Return(PyObject *value);
 /**
  * Wrapper around Python 3.4's enum library, which does not have a C API.
  */
-EXPCL_INTERROGATEDB PyObject *Dtool_EnumType_Create(const char *name, PyObject *names,
-                                                    const char *module = nullptr);
+EXPCL_INTERROGATEDB PyTypeObject *Dtool_EnumType_Create(const char *name, PyObject *names,
+                                                        const char *module = nullptr);
+EXPCL_INTERROGATEDB INLINE long Dtool_EnumValue_AsLong(PyObject *value);
+
 
 /**
 
@@ -292,7 +304,7 @@ Define_Dtool_Class(MODULE_NAME,CLASS_NAME,PUBLIC_NAME)
 #define Define_Module_ClassRef_Private(MODULE_NAME,CLASS_NAME,CNAME,PUBLIC_NAME)\
 Define_Module_Class_Internal(MODULE_NAME,CLASS_NAME,CNAME)\
 Define_Dtool_new(CLASS_NAME,CNAME)\
-Define_Dtool_FreeInstance_Private(CLASS_NAME,CNAME)\
+Define_Dtool_FreeInstanceRef_Private(CLASS_NAME,CNAME)\
 Define_Dtool_Class(MODULE_NAME,CLASS_NAME,PUBLIC_NAME)
 
 #define Define_Module_ClassRef(MODULE_NAME,CLASS_NAME,CNAME,PUBLIC_NAME)\

+ 2 - 0
dtool/src/pystub/pystub.cxx

@@ -121,6 +121,7 @@ extern "C" {
   EXPCL_PYSTUB int PyObject_Repr(...);
   EXPCL_PYSTUB int PyObject_RichCompareBool(...);
   EXPCL_PYSTUB int PyObject_SelfIter(...);
+  EXPCL_PYSTUB int PyObject_SetAttr(...);
   EXPCL_PYSTUB int PyObject_SetAttrString(...);
   EXPCL_PYSTUB int PyObject_Str(...);
   EXPCL_PYSTUB int PyObject_Type(...);
@@ -351,6 +352,7 @@ int PyObject_Malloc(...) { return 0; }
 int PyObject_Repr(...) { return 0; }
 int PyObject_RichCompareBool(...) { return 0; }
 int PyObject_SelfIter(...) { return 0; }
+int PyObject_SetAttr(...) { return 0; }
 int PyObject_SetAttrString(...) { return 0; }
 int PyObject_Str(...) { return 0; }
 int PyObject_Type(...) { return 0; }

+ 3 - 0
panda/src/bullet/bulletVehicle.cxx

@@ -232,6 +232,9 @@ void BulletVehicle::
 do_sync_b2p() {
 
   for (int i=0; i < _vehicle->getNumWheels(); i++) {
+    // synchronize the wheels with the (interpolated) chassis worldtransform
+    _vehicle->updateWheelTransform(i, true);
+
     btWheelInfo info = _vehicle->getWheelInfo(i);
 
     PandaNode *node = (PandaNode *)info.m_clientInfo;

+ 4 - 4
panda/src/bullet/bulletWorld.cxx

@@ -276,10 +276,6 @@ do_sync_p2b(PN_stdfloat dt, int num_substeps) {
 void BulletWorld::
 do_sync_b2p() {
 
-  for (BulletVehicle *vehicle : _vehicles) {
-    vehicle->do_sync_b2p();
-  }
-
   for (BulletRigidBodyNode *body : _bodies) {
     body->do_sync_b2p();
   }
@@ -295,6 +291,10 @@ do_sync_b2p() {
   for (BulletBaseCharacterControllerNode *character : _characters) {
     character->do_sync_b2p();
   }
+
+  for (BulletVehicle *vehicle : _vehicles) {
+    vehicle->do_sync_b2p();
+  }
 }
 
 /**

+ 1 - 1
panda/src/display/graphicsWindow.h

@@ -93,7 +93,7 @@ PUBLISHED:
   void enable_pointer_mode(int device, double speed);
   void disable_pointer_mode(int device);
 
-  MouseData get_pointer(int device) const;
+  virtual MouseData get_pointer(int device) const;
   virtual bool move_pointer(int device, int x, int y);
   virtual void close_ime();
 

+ 1 - 1
panda/src/glstuff/glCgShaderContext_src.cxx

@@ -525,7 +525,7 @@ issue_parameters(int altered) {
       }
 
       // Check if the size of the shader input and ptr_data match
-      int input_size = spec._dim[0] * spec._dim[1] * spec._dim[2];
+      size_t input_size = spec._dim[0] * spec._dim[1] * spec._dim[2];
 
       // dimension is negative only if the parameter had the (deprecated)k_
       // prefix.

+ 3 - 1
panda/src/glstuff/glGraphicsStateGuardian_src.cxx

@@ -11610,17 +11610,18 @@ do_issue_tex_gen() {
   // effectively define an identity matrix that maps the spatial coordinates
   // one-for-one to UV's.  If you want a mapping other than identity, use a
   // TexMatrixAttrib (or a TexProjectorEffect).
+#ifndef OPENGLES
   static const PN_stdfloat s_data[4] = { 1, 0, 0, 0 };
   static const PN_stdfloat t_data[4] = { 0, 1, 0, 0 };
   static const PN_stdfloat r_data[4] = { 0, 0, 1, 0 };
   static const PN_stdfloat q_data[4] = { 0, 0, 0, 1 };
+#endif
 
   _tex_gen_modifies_mat = false;
 
   bool got_point_sprites = false;
 
   for (int i = 0; i < _num_active_texture_stages; i++) {
-    TextureStage *stage = _target_texture->get_on_ff_stage(i);
     set_active_texture_stage(i);
     if (_supports_point_sprite) {
 #ifdef OPENGLES
@@ -11636,6 +11637,7 @@ do_issue_tex_gen() {
     glDisable(GL_TEXTURE_GEN_R);
     glDisable(GL_TEXTURE_GEN_Q);
 
+    TextureStage *stage = _target_texture->get_on_ff_stage(i);
     TexGenAttrib::Mode mode = _target_tex_gen->get_mode(stage);
     switch (mode) {
     case TexGenAttrib::M_off:

+ 3 - 0
panda/src/gobj/shader.cxx

@@ -2504,6 +2504,9 @@ do_read_source(string &into, const Filename &fn, BamCacheRecord *record) {
     into.resize(into.size() - 1);
   }
 
+  // Except add back a newline at the end, which is needed by Intel drivers.
+  into += "\n";
+
   return true;
 }
 

+ 42 - 2
panda/src/gobj/simpleAllocator.I

@@ -32,9 +32,9 @@ SimpleAllocator(size_t max_size, Mutex &lock) :
  * pointer.
  */
 SimpleAllocatorBlock *SimpleAllocator::
-alloc(size_t size) {
+alloc(size_t size, size_t alignment) {
   MutexHolder holder(_lock);
-  return do_alloc(size);
+  return do_alloc(size, alignment);
 }
 
 /**
@@ -148,6 +148,24 @@ SimpleAllocatorBlock(SimpleAllocator *alloc,
 {
 }
 
+/**
+ * Transfers ownership from the given SimpleAllocatorBlock to this one.
+ */
+INLINE SimpleAllocatorBlock::
+SimpleAllocatorBlock(SimpleAllocatorBlock &&from) :
+  _allocator(from._allocator)
+{
+  if (_allocator == nullptr) {
+    return;
+  }
+
+  MutexHolder holder(_allocator->_lock);
+  _start = from._start;
+  _size = from._size;
+  LinkedListNode::operator = (std::move(from));
+  from._allocator = nullptr;
+}
+
 /**
  * The block automatically frees itself when it destructs.
  */
@@ -156,6 +174,28 @@ INLINE SimpleAllocatorBlock::
   free();
 }
 
+/**
+ * Frees this block and instead takes ownership of the given other block.
+ */
+INLINE SimpleAllocatorBlock &SimpleAllocatorBlock::
+operator = (SimpleAllocatorBlock &&from) {
+  free();
+
+  _allocator = from._allocator;
+  if (_allocator == nullptr) {
+    _start = 0;
+    _size = 0;
+    return *this;
+  }
+
+  MutexHolder holder(_allocator->_lock);
+  _start = from._start;
+  _size = from._size;
+  LinkedListNode::operator = (std::move(from));
+  from._allocator = nullptr;
+  return *this;
+}
+
 /**
  * Releases the allocated space.
  */

+ 40 - 7
panda/src/gobj/simpleAllocator.cxx

@@ -13,6 +13,37 @@
 
 #include "simpleAllocator.h"
 
+/**
+ * Move constructor.
+ */
+SimpleAllocator::
+SimpleAllocator(SimpleAllocator &&from) noexcept :
+  LinkedListNode(std::move(from)),
+  _total_size(from._total_size),
+  _max_size(from._max_size),
+  _contiguous(from._contiguous),
+  _lock(from._lock)
+{
+  MutexHolder holder(_lock);
+  from._total_size = 0;
+  from._max_size = 0;
+  from._contiguous = 0;
+
+  // We still need to leave the list in a valid state.
+  from._prev = &from;
+  from._next = &from;
+
+  // Change all the blocks to point to the new allocator.
+  LinkedListNode *next = _next;
+  while (next != this) {
+    SimpleAllocatorBlock *block = (SimpleAllocatorBlock *)next;
+    nassertv(block->_allocator == &from);
+    block->_allocator = this;
+
+    next = block->_next;
+  }
+}
+
 /**
  *
  */
@@ -66,7 +97,7 @@ write(std::ostream &out) const {
  * Assumes the lock is already held.
  */
 SimpleAllocatorBlock *SimpleAllocator::
-do_alloc(size_t size) {
+do_alloc(size_t size, size_t alignment) {
   if (size > _contiguous) {
     // Don't even bother.
     return nullptr;
@@ -86,9 +117,9 @@ do_alloc(size_t size) {
     // Scan until we have reached the last allocated block.
     while (block->_next != this) {
       SimpleAllocatorBlock *next = (SimpleAllocatorBlock *)block->_next;
-      size_t free_size = next->_start - end;
-      if (size <= free_size) {
-        SimpleAllocatorBlock *new_block = make_block(end, size);
+      size_t start = end + ((alignment - end) % alignment);
+      if (start + size <= next->_start) {
+        SimpleAllocatorBlock *new_block = make_block(start, size);
         nassertr(new_block->get_allocator() == this, nullptr);
 
         new_block->insert_before(next);
@@ -103,6 +134,7 @@ do_alloc(size_t size) {
         }
         return new_block;
       }
+      size_t free_size = next->_start - end;
       if (free_size > best) {
         best = free_size;
       }
@@ -113,9 +145,9 @@ do_alloc(size_t size) {
   }
 
   // No free blocks; check for room at the end.
-  size_t free_size = _max_size - end;
-  if (size <= free_size) {
-    SimpleAllocatorBlock *new_block = make_block(end, size);
+  size_t start = end + ((alignment - end) % alignment);
+  if (start + size <= _max_size) {
+    SimpleAllocatorBlock *new_block = make_block(start, size);
     nassertr(new_block->get_allocator() == this, nullptr);
 
     new_block->insert_before(this);
@@ -131,6 +163,7 @@ do_alloc(size_t size) {
     return new_block;
   }
 
+  size_t free_size = _max_size - end;
   if (free_size > best) {
     best = free_size;
   }

+ 14 - 5
panda/src/gobj/simpleAllocator.h

@@ -29,9 +29,10 @@ class SimpleAllocatorBlock;
 class EXPCL_PANDA_GOBJ SimpleAllocator : public LinkedListNode {
 PUBLISHED:
   INLINE explicit SimpleAllocator(size_t max_size, Mutex &lock);
+  SimpleAllocator(SimpleAllocator &&from) noexcept;
   virtual ~SimpleAllocator();
 
-  INLINE SimpleAllocatorBlock *alloc(size_t size);
+  INLINE SimpleAllocatorBlock *alloc(size_t size, size_t alignment=1);
 
   INLINE bool is_empty() const;
   INLINE size_t get_total_size() const;
@@ -45,7 +46,7 @@ PUBLISHED:
   void write(std::ostream &out) const;
 
 protected:
-  SimpleAllocatorBlock *do_alloc(size_t size);
+  SimpleAllocatorBlock *do_alloc(size_t size, size_t alignment=1);
   INLINE bool do_is_empty() const;
 
   virtual SimpleAllocatorBlock *make_block(size_t start, size_t size);
@@ -91,6 +92,14 @@ protected:
   INLINE SimpleAllocatorBlock(SimpleAllocator *alloc,
                               size_t start, size_t size);
 
+public:
+  SimpleAllocatorBlock() = default;
+  SimpleAllocatorBlock(const SimpleAllocatorBlock &copy) = delete;
+  INLINE SimpleAllocatorBlock(SimpleAllocatorBlock &&from);
+
+  SimpleAllocatorBlock &operator = (const SimpleAllocatorBlock &copy) = delete;
+  INLINE SimpleAllocatorBlock &operator = (SimpleAllocatorBlock &&from);
+
 PUBLISHED:
   INLINE ~SimpleAllocatorBlock();
   INLINE void free();
@@ -114,9 +123,9 @@ protected:
   INLINE bool do_realloc(size_t size);
 
 private:
-  SimpleAllocator *_allocator;
-  size_t _start;
-  size_t _size;
+  SimpleAllocator *_allocator = nullptr;
+  size_t _start = 0;
+  size_t _size = 0;
 
   friend class SimpleAllocator;
 };

+ 2 - 0
panda/src/pipeline/config_pipeline.cxx

@@ -12,6 +12,7 @@
  */
 
 #include "config_pipeline.h"
+#include "cycleData.h"
 #include "mainThread.h"
 #include "externalThread.h"
 #include "genericThread.h"
@@ -70,6 +71,7 @@ init_libpipeline() {
   }
   initialized = true;
 
+  CycleData::init_type();
   MainThread::init_type();
   ExternalThread::init_type();
   GenericThread::init_type();

+ 3 - 0
panda/src/pipeline/cycleData.cxx

@@ -13,6 +13,9 @@
 
 #include "cycleData.h"
 
+#ifdef DO_PIPELINING
+TypeHandle CycleData::_type_handle;
+#endif
 
 /**
  *

+ 16 - 0
panda/src/pipeline/cycleData.h

@@ -65,6 +65,22 @@ public:
 
   virtual TypeHandle get_parent_type() const;
   virtual void output(std::ostream &out) const;
+
+#ifdef DO_PIPELINING
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+
+  static void init_type() {
+    NodeReferenceCount::init_type();
+    register_type(_type_handle, "CycleData",
+                  NodeReferenceCount::get_class_type());
+  }
+
+private:
+  static TypeHandle _type_handle;
+#endif
 };
 
 INLINE std::ostream &

+ 36 - 0
panda/src/putil/linkedListNode.I

@@ -33,6 +33,25 @@ LinkedListNode(bool) {
   _prev = this;
 }
 
+/**
+ * This move constructor replaces the other link with this one.
+ */
+INLINE LinkedListNode::
+LinkedListNode(LinkedListNode &&from) noexcept {
+  if (from._prev != nullptr) {
+    nassertv(from._prev->_next == &from);
+    from._prev->_next = this;
+  }
+  _prev = from._prev;
+  if (from._next != nullptr) {
+    nassertv(from._next->_prev == &from);
+    from._next->_prev = this;
+  }
+  _next = from._next;
+  from._next = nullptr;
+  from._prev = nullptr;
+}
+
 /**
  *
  */
@@ -41,6 +60,23 @@ INLINE LinkedListNode::
   nassertv((_next == nullptr && _prev == nullptr) || (_next == this && _prev == this));
 }
 
+/**
+ * Replaces the given other node with this node.
+ */
+INLINE LinkedListNode &LinkedListNode::
+operator = (LinkedListNode &&from) {
+  nassertr((_next == nullptr && _prev == nullptr) || (_next == this && _prev == this), *this);
+  nassertr(from._prev != nullptr && from._next != nullptr, *this);
+  nassertr(from._prev->_next == &from && from._next->_prev == &from, *this);
+  from._prev->_next = this;
+  from._next->_prev = this;
+  _prev = from._prev;
+  _next = from._next;
+  from._next = nullptr;
+  from._prev = nullptr;
+  return *this;
+}
+
 /**
  * Returns true if the node is member of any list, false if it has been
  * removed or never added.  The head of a list generally appears to to always

+ 3 - 0
panda/src/putil/linkedListNode.h

@@ -32,8 +32,11 @@ class EXPCL_PANDA_PUTIL LinkedListNode {
 protected:
   INLINE LinkedListNode();
   INLINE LinkedListNode(bool);
+  INLINE LinkedListNode(LinkedListNode &&from) noexcept;
   INLINE ~LinkedListNode();
 
+  INLINE LinkedListNode &operator = (LinkedListNode &&from);
+
   INLINE bool is_on_list() const;
   INLINE void remove_from_list();
   INLINE void insert_before(LinkedListNode *node);

+ 28 - 0
panda/src/windisplay/winGraphicsWindow.cxx

@@ -120,6 +120,34 @@ WinGraphicsWindow::
   }
 }
 
+/**
+ * Returns the MouseData associated with the nth input device's pointer.
+ */
+MouseData WinGraphicsWindow::
+get_pointer(int device) const {
+  MouseData result;
+  {
+    LightMutexHolder holder(_input_lock);
+    nassertr(device >= 0 && device < (int)_input_devices.size(), MouseData());
+
+    result = _input_devices[device].get_pointer();
+
+    // We recheck this immediately to get the most up-to-date value.
+    POINT cpos;
+    if (device == 0 && GetCursorPos(&cpos) && ScreenToClient(_hWnd, &cpos)) {
+      double time = ClockObject::get_global_clock()->get_real_time();
+      RECT view_rect;
+      if (GetClientRect(_hWnd, &view_rect)) {
+        result._in_window = PtInRect(&view_rect, cpos);
+        result._xpos = cpos.x;
+        result._ypos = cpos.y;
+        ((GraphicsWindowInputDevice &)_input_devices[0]).set_pointer(result._in_window, result._xpos, result._ypos, time);
+      }
+    }
+  }
+  return result;
+}
+
 /**
  * Forces the pointer to the indicated position within the window, if
  * possible.

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

@@ -72,6 +72,7 @@ public:
                     GraphicsOutput *host);
   virtual ~WinGraphicsWindow();
 
+  virtual MouseData get_pointer(int device) const;
   virtual bool move_pointer(int device, int x, int y);
 
   virtual void close_ime();

+ 31 - 3
panda/src/x11display/x11GraphicsWindow.cxx

@@ -143,6 +143,36 @@ x11GraphicsWindow::
   }
 }
 
+/**
+ * Returns the MouseData associated with the nth input device's pointer.  This
+ * is deprecated; use get_pointer_device().get_pointer() instead, or for raw
+ * mice, use the InputDeviceManager interface.
+ */
+MouseData x11GraphicsWindow::
+get_pointer(int device) const {
+  MouseData result;
+  {
+    LightMutexHolder holder(_input_lock);
+    nassertr(device >= 0 && device < (int)_input_devices.size(), MouseData());
+
+    result = _input_devices[device].get_pointer();
+
+    // We recheck this immediately to get the most up-to-date value.
+    if (device == 0 && !_dga_mouse_enabled && result._in_window) {
+      XEvent event;
+      if (XQueryPointer(_display, _xwindow, &event.xbutton.root,
+          &event.xbutton.window, &event.xbutton.x_root, &event.xbutton.y_root,
+          &event.xbutton.x, &event.xbutton.y, &event.xbutton.state)) {
+        double time = ClockObject::get_global_clock()->get_real_time();
+        result._xpos = event.xbutton.x;
+        result._ypos = event.xbutton.y;
+        ((GraphicsWindowInputDevice &)_input_devices[0]).set_pointer(result._in_window, result._xpos, result._ypos, time);
+      }
+    }
+  }
+  return result;
+}
+
 /**
  * Forces the pointer to the indicated position within the window, if
  * possible.
@@ -1355,9 +1385,7 @@ open_raw_mice() {
 void x11GraphicsWindow::
 poll_raw_mice() {
 #ifdef PHAVE_LINUX_INPUT_H
-  for (int di = 0; di < _mouse_device_info.size(); ++di) {
-    MouseDeviceInfo &inf = _mouse_device_info[di];
-
+  for (MouseDeviceInfo &inf : _mouse_device_info) {
     // Read all bytes into buffer.
     if (inf._fd >= 0) {
       while (1) {

+ 1 - 0
panda/src/x11display/x11GraphicsWindow.h

@@ -34,6 +34,7 @@ public:
                     GraphicsOutput *host);
   virtual ~x11GraphicsWindow();
 
+  virtual MouseData get_pointer(int device) const;
   virtual bool move_pointer(int device, int x, int y);
   virtual bool begin_frame(FrameMode mode, Thread *current_thread);
   virtual void end_frame(FrameMode mode, Thread *current_thread);