Browse Source

Merge branch 'master' into cmake

Sam Edwards 10 years ago
parent
commit
c902774cfd
100 changed files with 4484 additions and 3816 deletions
  1. 25 4
      README.md
  2. 0 1
      contrib/src/ai/p3ai_composite.cxx
  3. 6 4
      direct/src/actor/Actor.py
  4. 3 7
      direct/src/controls/GravityWalker.py
  5. 3 1
      direct/src/dcparser/dcPython.h
  6. 1 1
      direct/src/directbase/ppython.cxx
  7. 1 1
      direct/src/directnotify/Notifier.py
  8. 0 44
      direct/src/directscripts/cleancvstree
  9. 0 124
      direct/src/directscripts/make-panda3d-tgz.py
  10. 0 1
      direct/src/distributed/GridChild.py
  11. 0 97
      direct/src/extensions/CInterval-extensions.py
  12. 0 50
      direct/src/extensions/ConfigVariable-extensions.py
  13. 0 5
      direct/src/extensions/ConfigVariableBool-extensions.py
  14. 0 5
      direct/src/extensions/ConfigVariableDouble-extensions.py
  15. 0 5
      direct/src/extensions/ConfigVariableInt-extensions.py
  16. 0 18
      direct/src/extensions/ConfigVariableList-extensions.py
  17. 0 6
      direct/src/extensions/ConfigVariableString-extensions.py
  18. 0 31
      direct/src/extensions/HTTPChannel-extensions.py
  19. 0 16
      direct/src/extensions/Mat3-extensions.py
  20. 0 27
      direct/src/extensions/MouseWatcherRegion-extensions.py
  21. 0 20
      direct/src/extensions/Node-extensions.py
  22. 0 1093
      direct/src/extensions/NodePath-extensions.py
  23. 0 26
      direct/src/extensions/NurbsCurveEvaluator-extensions.py
  24. 0 36
      direct/src/extensions/NurbsSurfaceEvaluator-extensions.py
  25. 0 6
      direct/src/extensions/PandaSystem-extensions.py
  26. 0 3
      direct/src/extensions/Sources.pp
  27. 0 83
      direct/src/extensions/SpriteParticleRenderer-extensions.py
  28. 0 15
      direct/src/extensions/VBase3-extensions.py
  29. 0 15
      direct/src/extensions/VBase4-extensions.py
  30. 0 1
      direct/src/extensions/__init__.py
  31. 12 24
      direct/src/p3d/AppRunner.py
  32. 136 61
      direct/src/p3d/DeploymentTools.py
  33. 17 21
      direct/src/p3d/HostInfo.py
  34. 4 3
      direct/src/p3d/PackageInfo.py
  35. 148 63
      direct/src/p3d/Packager.py
  36. 1 1
      direct/src/p3d/coreapi.pdef
  37. 20 9
      direct/src/p3d/packp3d.py
  38. 12 4
      direct/src/p3d/panda3d.pdef
  39. 17 8
      direct/src/p3d/ppackage.py
  40. 3 3
      direct/src/p3d/thirdparty.pdef
  41. 2 2
      direct/src/particles/ParticleTest.py
  42. 17 6
      direct/src/plugin/load_plugin.cxx
  43. 4 2
      direct/src/plugin/load_plugin.h
  44. 4 3
      direct/src/plugin/p3dAuthSession.cxx
  45. 162 59
      direct/src/plugin/p3dCert.cxx
  46. 3 3
      direct/src/plugin/p3dCert.h
  47. 571 0
      direct/src/plugin/p3dCert_strings.cxx
  48. 45 0
      direct/src/plugin/p3dCert_strings.h
  49. 91 3
      direct/src/plugin/p3dInstance.cxx
  50. 12 0
      direct/src/plugin/p3dInstanceManager.I
  51. 23 6
      direct/src/plugin/p3dInstanceManager.cxx
  52. 4 1
      direct/src/plugin/p3dInstanceManager.h
  53. 98 70
      direct/src/plugin/p3dOsxSplashWindow.cxx
  54. 2 0
      direct/src/plugin/p3dOsxSplashWindow.h
  55. 4 4
      direct/src/plugin/p3dPythonMain.cxx
  56. 174 52
      direct/src/plugin/p3dPythonRun.cxx
  57. 8 3
      direct/src/plugin/p3dPythonRun.h
  58. 51 15
      direct/src/plugin/p3dSession.cxx
  59. 186 6
      direct/src/plugin/p3dSplashWindow.cxx
  60. 36 3
      direct/src/plugin/p3dSplashWindow.h
  61. 34 8
      direct/src/plugin/p3dWinSplashWindow.cxx
  62. 4 2
      direct/src/plugin/p3dWinSplashWindow.h
  63. 147 16
      direct/src/plugin/p3dX11SplashWindow.cxx
  64. 6 3
      direct/src/plugin/p3dX11SplashWindow.h
  65. 7 2
      direct/src/plugin/p3d_plugin.cxx
  66. 7 2
      direct/src/plugin/p3d_plugin.h
  67. 7 6
      direct/src/plugin/run_p3dpython.cxx
  68. 4 4
      direct/src/plugin/run_p3dpython.h
  69. 3 3
      direct/src/plugin_activex/PPInstance.cpp
  70. 2 2
      direct/src/plugin_npapi/ppInstance.cxx
  71. 23 6
      direct/src/plugin_standalone/p3dEmbed.cxx
  72. 185 135
      direct/src/plugin_standalone/panda3d.cxx
  73. 2 0
      direct/src/plugin_standalone/panda3d.h
  74. 5 5
      direct/src/plugin_standalone/panda3dBase.cxx
  75. 3 2
      direct/src/plugin_standalone/panda3dBase.h
  76. 1 1
      direct/src/showbase/Loader.py
  77. 1 1
      direct/src/showbase/PythonUtil.py
  78. 8 6
      direct/src/showbase/ShowBase.py
  79. 1 1
      direct/src/showbase/Transitions.py
  80. 60 49
      direct/src/showbase/VFSImporter.py
  81. 92 91
      direct/src/showutil/FreezeTool.py
  82. 5 1
      direct/src/showutil/Rope.py
  83. 32 17
      direct/src/showutil/pfreeze.py
  84. 213 203
      direct/src/stdpy/file.py
  85. 2 0
      direct/src/tkpanels/ParticlePanel.py
  86. 221 218
      dtool/metalibs/dtoolconfig/pydtool.cxx
  87. 541 495
      dtool/src/cppparser/cppBison.cxx.prebuilt
  88. 220 200
      dtool/src/cppparser/cppBison.h.prebuilt
  89. 274 108
      dtool/src/cppparser/cppBison.yxx
  90. 2 2
      dtool/src/cppparser/cppBisonDefs.h
  91. 3 1
      dtool/src/cppparser/cppEnumType.cxx
  92. 318 23
      dtool/src/cppparser/cppExpression.cxx
  93. 25 5
      dtool/src/cppparser/cppExpression.h
  94. 2 1
      dtool/src/cppparser/cppExtensionType.cxx
  95. 1 1
      dtool/src/cppparser/cppExtensionType.h
  96. 56 6
      dtool/src/cppparser/cppIdentifier.cxx
  97. 4 1
      dtool/src/cppparser/cppIdentifier.h
  98. 45 4
      dtool/src/cppparser/cppInstance.cxx
  99. 7 4
      dtool/src/cppparser/cppInstance.h
  100. 5 0
      dtool/src/cppparser/cppInstanceIdentifier.cxx

+ 25 - 4
README.md

@@ -81,14 +81,18 @@ sudo apt-get install build-essential pkg-config python-dev libpng-dev libjpeg-de
 ```
 
 Once Panda3D has built, you can either install the .deb or .rpm package that
-it produced (if relevant to your platform, and you added --installer).  On
-other systems, you will need to use the installpanda script to install it onto
-your system.  Careful: it is not easy to uninstall Panda3D in this way!
+it produced, depending on which Linux distribution you are using.  For example,
+to install the package on Debian or Ubuntu, use this:
 
 ```bash
-python2.7 makepanda/installpanda.py --prefix=/usr/local
+sudo dpkg -i panda3d*.deb
 ```
 
+If you are not using a Linux distribution that supports .deb or .rpm packages, you
+may have to use the installpanda.py script instead, which will directly copy the
+files into the appropriate locations on your computer.  You may have to run the
+`ldconfig` tool in order to update your library cache after installing Panda3D.
+
 Mac OS X
 --------
 
@@ -110,3 +114,20 @@ by the release number, eg. 10.6 or 10.7.
 If the build was successful, makepanda will have generated a .dmg file in
 the source directory containing the installer.  Simply open it and run the
 package file in order to install the SDK onto your system.
+
+Reporting Issues
+================
+
+If you encounter any bugs when using Panda3D, please report them in the bug
+tracker.  This is hosted at:
+
+  https://bugs.launchpad.net/panda3d
+
+Make sure to first use the search function to see if the bug has already been
+reported.  When filling out a bug report, make sure that you include as much
+information as possible to help the developers track down the issue, such as
+your version of Panda3D, operating system, architecture, and any code and
+models that are necessary for the developers to reproduce the issue.
+
+If you're not sure whether you've encountered a bug, feel free to ask about
+it in the forums or the IRC channel first.

+ 0 - 1
contrib/src/ai/p3ai_composite.cxx

@@ -1 +0,0 @@
-#include "ai_composite1.cxx"

+ 6 - 4
direct/src/actor/Actor.py

@@ -23,6 +23,8 @@ class Actor(DirectObject, NodePath):
                                        LoaderOptions.LFConvertAnim)
 
     validateSubparts = ConfigVariableBool('validate-subparts', True)
+    mergeLODBundles = ConfigVariableBool('merge-lod-bundles', True)
+    allowAsyncBind = ConfigVariableBool('allow-async-bind', True)
     
     class PartDef:
 
@@ -170,10 +172,10 @@ class Actor(DirectObject, NodePath):
         # ['common']; when it is false, __animControlDict has one key
         # per each LOD name.
         
-        if mergeLODBundles == None:
+        if mergeLODBundles is None:
             # If this isn't specified, it comes from the Config.prc
             # file.
-            self.mergeLODBundles = base.config.GetBool('merge-lod-bundles', True)
+            self.mergeLODBundles = Actor.mergeLODBundles.getValue()
         else:
             self.mergeLODBundles = mergeLODBundles
 
@@ -181,8 +183,8 @@ class Actor(DirectObject, NodePath):
         # asynchronous animation binding.  This requires that you have
         # run "egg-optchar -preload" on your animation and models to
         # generate the appropriate AnimPreloadTable.
-        if allowAsyncBind == None:
-            self.allowAsyncBind = base.config.GetBool('allow-async-bind', True)
+        if allowAsyncBind is None:
+            self.allowAsyncBind = Actor.allowAsyncBind.getValue()
         else:
             self.allowAsyncBind = allowAsyncBind
 

+ 3 - 7
direct/src/controls/GravityWalker.py

@@ -179,9 +179,9 @@ class GravityWalker(DirectObject.DirectObject):
         # a higher or lower value depending on whether you want an avatar
         # that is outside of the world to step up to the floor when they
         # get under valid floor:
-        cRay = CollisionRay(0.0, 0.0, CollisionHandlerRayStart, 0.0, 0.0, -1.0)
+        self.cRay = CollisionRay(0.0, 0.0, CollisionHandlerRayStart, 0.0, 0.0, -1.0)
         cRayNode = CollisionNode('GW.cRayNode')
-        cRayNode.addSolid(cRay)
+        cRayNode.addSolid(self.cRay)
         self.cRayNodePath = self.avatarNodePath.attachNewNode(cRayNode)
         cRayNode.setFromCollideMask(bitmask)
         cRayNode.setIntoCollideMask(BitMask32.allOff())
@@ -697,8 +697,4 @@ class GravityWalker(DirectObject.DirectObject):
     # There are sometimes issues if the collision ray height is
     # so tall that it collides with multiple levels of floors.
     def setCollisionRayHeight(self, height):
-        oldNode = self.avatarNodePath.getNode(0)
-        cRayNode = oldNode.getChild(2)
-        cRayNode.removeSolid(0)
-        cRay = CollisionRay(0.0, 0.0, height, 0.0, 0.0, -1.0)
-        cRayNode.addSolid(cRay)        
+        self.cRay.setOrigin(0.0, 0.0, height)

+ 3 - 1
direct/src/dcparser/dcPython.h

@@ -20,8 +20,10 @@
 
 #ifdef HAVE_PYTHON
 
-#undef HAVE_LONG_LONG  // NSPR and Python both define this.
+#define PY_SSIZE_T_CLEAN 1
+
 #undef _POSIX_C_SOURCE
+#undef _XOPEN_SOURCE
 #include <Python.h>
 
 // Python 2.5 adds Py_ssize_t; earlier versions don't have it.

+ 1 - 1
direct/src/directbase/ppython.cxx

@@ -36,7 +36,7 @@ int main(int argc, char *mb_argv[]) {
     size_t len = mbstowcs(NULL, mb_argv[i], 0);
     argv[i] = new wchar_t[len + 1];
     mbstowcs(argv[i], mb_argv[i], len);
-    argv[i][len] = NULL;
+    argv[i][len] = 0;
   }
   // Just for good measure
   argv[argc] = NULL;

+ 1 - 1
direct/src/directnotify/Notifier.py

@@ -235,7 +235,7 @@ class Notifier:
         Prints the string to output followed by a newline.
         """
         if self.streamWriter:
-            self.streamWriter.appendData(string + '\n')
+            self.streamWriter.write(string + '\n')
         else:
             print >> sys.stderr, string
 

+ 0 - 44
direct/src/directscripts/cleancvstree

@@ -1,44 +0,0 @@
-##############################################################################
-#
-# cleancvstree
-#
-# Cleancvstree searches a CVS tree for files that are not in CVS, and
-# deletes them.  Be careful using it --- it's very aggressive.
-#
-##############################################################################
-
-import sys,os
-
-def cleanCvsTree(dir):
-    try:
-        sub = os.listdir(dir)
-    except:
-	print "Could not read directory: "+dir
-	return
-    valid = {}
-    try:
-	readentries = 0
-        cvsent = open(dir + "/CVS/Entries")
-        for line in cvsent:
-            words = line.split("/")
-            if (len(words) > 1):
-                valid[words[1]] = 1
-        cvsent.close()
-	readentries = 1
-    except:
-	print "Could not read "+dir+"/CVS/Entries"
-    if (readentries):
-	for file in sub:
-	    if (os.path.isfile(dir+"/"+file)):
-                if (valid.has_key(file)==0):
-		    os.unlink(dir+"/"+file)
-    for file in sub:
-	if (file != "CVS"):
-            if (os.path.isdir(dir+"/"+file)):
-                cleanCvsTree(dir+"/"+file)
-
-if (os.path.isdir(sys.argv[1])==0):
-    print "Not a directory: "+sys.argv[1]
-    os.exit(1)
-
-cleanCvsTree(sys.argv[1])

+ 0 - 124
direct/src/directscripts/make-panda3d-tgz.py

@@ -1,124 +0,0 @@
-#!/usr/bin/env python
-
-"""This script generates the panda3d-date.tar.gz tarball for a file
-release of panda3d onto the SourceForge download site.
-
-Options:
-
-    -d cvsroot
-        Specifies the CVSROOT string to use to tag and export the
-        tree.  The default is $SFROOT if it is defined, or $CVSROOT
-        otherwise.
-
-    -r tag
-        Specifies the tag to export from.  If this parameter is
-        specified, the tree is not tagged again; otherwise, the
-        current head of the CVS tree is tagged with the file version
-        name.
-
-    -m module
-        Specifies the module to check out and build.  The default is
-        panda3d.
-"""
-
-import sys
-import os
-import os.path
-import getopt
-import time
-import glob
-import shutil
-
-CVSROOT = os.getenv('SFROOT') or os.getenv('CVSROOT')
-ORIGTAG = ''
-MODULE  = 'panda3d'
-
-def usage(code, msg = ''):
-    print >> sys.stderr, __doc__
-    print >> sys.stderr, msg
-    sys.exit(code)
-
-try:
-    opts, args = getopt.getopt(sys.argv[1:], 'd:r:m:h')
-except getopt.error, msg:
-    usage(1, msg)
-
-for opt, arg in opts:
-    if opt == '-d':
-        CVSROOT = arg
-    elif opt == '-r':
-        ORIGTAG = arg
-    elif opt == '-m':
-        MODULE = arg
-    elif opt == '-h':
-        usage(0)
-
-if not CVSROOT:
-    usage(1, 'CVSROOT must have a value.')
-
-if not MODULE:
-    usage(1, 'MODULE must have a value.')
-
-basename = MODULE + '-' + time.strftime("%Y-%m-%d")
-tarfile = basename + '.tar.gz'
-zipfile = basename + '.zip'
-
-if os.path.exists(basename):
-    print basename, 'already exists in the local directory!'
-    sys.exit(1)
-
-if not ORIGTAG:
-    # If we weren't given a starting tag, make one.
-    tag = basename
-
-    print 'Tagging sources.'
-    cmd = 'cvs -f -d "%s" rtag -F -r HEAD "%s" "%s"' % (CVSROOT, tag, MODULE)
-    if os.system(cmd) != 0:
-        sys.exit(1)
-else:
-    # Otherwise, we were given a starting tag, so use it.
-    tag = ORIGTAG
-
-print 'Checking out "%s" as "%s".' % (MODULE, basename)
-cmd = 'cvs -z3 -f -d "%s" export -r "%s" -d "%s" "%s"' % (CVSROOT, tag,
-                                                          basename, MODULE)
-if os.system(cmd) != 0:
-    sys.exit(1)
-
-# Move the contents of the doc module into the root directory where people
-# will expect to see it.
-docdir = basename + os.sep + 'doc'
-if os.path.exists(docdir):
-    files = glob.glob(docdir + os.sep + '*')
-    for file in files:
-        shutil.copy(file, basename)
-        os.remove(file)
-    os.rmdir(docdir)
-
-# Generate the autoconf scripts for ppremake.
-if MODULE == 'ppremake':
-    ppremakedir = basename
-else:
-    ppremakedir = basename + os.sep + 'ppremake'
-if os.path.exists(ppremakedir):
-    cmd = 'cd "./%s" && aclocal && autoheader && automake --foreign -a && autoconf' % (ppremakedir)
-    if os.system(cmd) != 0:
-        sys.exit(1)
-
-# Generate the tarball.
-print 'Generating %s' % (tarfile)
-if os.path.exists(tarfile):
-    os.remove(tarfile)
-cmd = 'tar cf - "%s" | gzip -9 > "%s"' % (basename, tarfile)
-if os.system(cmd) != 0:
-    sys.exit(1)
-
-# Also generate a zip file.
-print 'Generating %s' % (zipfile)
-if os.path.exists(zipfile):
-    os.remove(zipfile)
-cmd = 'zip -9rq "%s" "%s"' % (zipfile, basename)
-if os.system(cmd) != 0:
-    sys.exit(1)
-
-shutil.rmtree(basename)

+ 0 - 1
direct/src/distributed/GridChild.py

@@ -1,6 +1,5 @@
 from direct.distributed.DistributedSmoothNodeBase import DistributedSmoothNodeBase
 from direct.distributed.GridParent import GridParent
-from pandac.PandaModules import EmbeddedValue
 
 class GridChild:
     """

+ 0 - 97
direct/src/extensions/CInterval-extensions.py

@@ -1,97 +0,0 @@
-
-    """
-    CInterval-extensions module: contains methods to extend functionality
-    of the CInterval class
-    """
-
-    from direct.directnotify.DirectNotifyGlobal import directNotify
-    notify = directNotify.newCategory("Interval")
-
-    def setT(self, t):
-        # Overridden from the C++ function to call privPostEvent
-        # afterward.  We do this by renaming the C++ function in
-        # FFIRename.
-        self._priv__cSetT(t)
-        self.privPostEvent()
-
-    def play(self, t0 = 0.0, duration = None, scale = 1.0):
-        self.notify.error("using deprecated CInterval.play() interface")
-        if duration:  # None or 0 implies full length
-            self.start(t0, t0 + duration, scale)
-        else:
-            self.start(t0, -1, scale)
-
-    def stop(self):
-        self.notify.error("using deprecated CInterval.stop() interface")
-        self.finish()
-
-    def setFinalT(self):
-        self.notify.error("using deprecated CInterval.setFinalT() interface")
-        self.finish()
-
-    def privPostEvent(self):
-        # Call after calling any of the priv* methods to do any required
-        # Python finishing steps.
-        t = self.getT()
-        if hasattr(self, "setTHooks"):
-            for func in self.setTHooks:
-                func(t)
-
-    def popupControls(self, tl = None):
-        """
-        Popup control panel for interval.
-        """
-        from direct.showbase.TkGlobal import Toplevel, Frame, Button, LEFT, X, Pmw
-        import math
-        from direct.tkwidgets import EntryScale
-        if tl == None:
-            tl = Toplevel()
-            tl.title('Interval Controls')
-        outerFrame = Frame(tl)
-        def entryScaleCommand(t, s=self):
-            s.setT(t)
-            s.pause()
-        self.es = es = EntryScale.EntryScale(
-            outerFrame, text = self.getName(),
-            min = 0, max = math.floor(self.getDuration() * 100) / 100,
-            command = entryScaleCommand)
-        es.set(self.getT(), fCommand = 0)
-        es.pack(expand = 1, fill = X)
-        bf = Frame(outerFrame)
-        # Jump to start and end
-        def toStart(s=self, es=es):
-            s.clearToInitial()
-            s.setT(0)
-            es.set(0, fCommand = 0)
-        def toEnd(s=self):
-            s.setT(s.getDuration())
-            s.pause()
-        jumpToStart = Button(bf, text = '<<', command = toStart)
-        # Stop/play buttons
-        def doPlay(s=self, es=es):
-            s.resume(es.get())
-
-        stop = Button(bf, text = 'Stop',
-                      command = lambda s=self: s.pause())
-        play = Button(
-            bf, text = 'Play',
-            command = doPlay)
-        jumpToEnd = Button(bf, text = '>>', command = toEnd)
-        jumpToStart.pack(side = LEFT, expand = 1, fill = X)
-        play.pack(side = LEFT, expand = 1, fill = X)
-        stop.pack(side = LEFT, expand = 1, fill = X)
-        jumpToEnd.pack(side = LEFT, expand = 1, fill = X)
-        bf.pack(expand = 1, fill = X)
-        outerFrame.pack(expand = 1, fill = X)
-        # Add function to update slider during setT calls
-        def update(t, es=es):
-            es.set(t, fCommand = 0)
-        if not hasattr(self, "setTHooks"):
-            self.setTHooks = []
-        self.setTHooks.append(update)
-        self.setWantsTCallback(1)
-        # Clear out function on destroy
-        def onDestroy(e, s=self, u=update):
-            if u in s.setTHooks:
-                s.setTHooks.remove(u)
-        tl.bind('<Destroy>', onDestroy)

+ 0 - 50
direct/src/extensions/ConfigVariable-extensions.py

@@ -1,50 +0,0 @@
-
-    def __str__(self):
-        return self.getStringValue()
-
-    def __hash__(self):
-        raise AttributeError, "ConfigVariables are not immutable."
-
-    def ls(self):
-        from pandac.Notify import Notify
-        self.write(Notify.out())
-
-    def __int__(self):
-        return int(self.getValue())
-
-    def __long__(self):
-        return long(self.getValue())
-
-    def __float__(self):
-        return float(self.getValue())
-
-    def __nonzero__(self):
-        return bool(self.getValue())
-
-    def __oct__(self):
-        return oct(self.getValue())
-
-    def __hex__(self):
-        return hex(self.getValue())
-
-    def __cmp__(self, other):
-        return self.getValue().__cmp__(other)
-
-    def __neg__(self):
-        return -self.getValue()
-
-    def __coerce__(self, other):
-        return (self.getValue(), other)
-
-    def __len__(self):
-        return self.getNumWords()
-    
-    def __getitem__(self, n):
-        if n < 0 or n >= self.getNumWords():
-            raise IndexError
-        return self.getWord(n)
-
-    def __setitem__(self, n, value):
-        if n < 0 or n > self.getNumWords():
-            raise IndexError
-        self.setWord(n, value)

+ 0 - 5
direct/src/extensions/ConfigVariableBool-extensions.py

@@ -1,5 +0,0 @@
-    
-    def __getitem__(self, n):
-        if n < 0 or n >= self.getNumWords():
-            raise IndexError
-        return self.getWord(n)

+ 0 - 5
direct/src/extensions/ConfigVariableDouble-extensions.py

@@ -1,5 +0,0 @@
-    
-    def __getitem__(self, n):
-        if n < 0 or n >= self.getNumWords():
-            raise IndexError
-        return self.getWord(n)

+ 0 - 5
direct/src/extensions/ConfigVariableInt-extensions.py

@@ -1,5 +0,0 @@
-    
-    def __getitem__(self, n):
-        if n < 0 or n >= self.getNumWords():
-            raise IndexError
-        return self.getWord(n)

+ 0 - 18
direct/src/extensions/ConfigVariableList-extensions.py

@@ -1,18 +0,0 @@
-
-    def __hash__(self):
-        raise AttributeError, "ConfigVariables are not immutable."
-
-    def ls(self):
-        from pandac.Notify import Notify
-        self.write(Notify.out())
-
-    def __cmp__(self, other):
-        return list(self).__cmp__(list(other))
-
-    def __len__(self):
-        return self.getNumValues()
-    
-    def __getitem__(self, n):
-        if n < 0 or n >= self.getNumUniqueValues():
-            raise IndexError
-        return self.getUniqueValue(n)

+ 0 - 6
direct/src/extensions/ConfigVariableString-extensions.py

@@ -1,6 +0,0 @@
-
-    def __len__(self):
-        return self.getValue().__len__()
-    
-    def __getitem__(self, n):
-        return self.getValue().__getitem__(n)

+ 0 - 31
direct/src/extensions/HTTPChannel-extensions.py

@@ -1,31 +0,0 @@
-
-    """
-    HTTPChannel-extensions module: contains methods to extend functionality
-    of the HTTPChannel class
-    """
-
-    def spawnTask(self, name = None, callback = None, extraArgs = []):
-        """Spawns a task to service the download recently requested
-        via beginGetDocument(), etc., and/or downloadToFile() or
-        downloadToRam().  If a callback is specified, that function is
-        called when the download is complete, passing in the extraArgs
-        given.
-
-        Returns the newly-spawned task.
-        """
-        if not name:
-            name = str(self.getUrl())
-        from direct.task import Task
-        task = Task.Task(self.doTask)
-        task.callback = callback
-        task.callbackArgs = extraArgs
-        return taskMgr.add(task, name)    
-        
-    def doTask(self, task):
-        from direct.task import Task
-        if self.run():
-            return Task.cont
-        if task.callback:
-            task.callback(*task.callbackArgs)
-        return Task.done
-    

+ 0 - 16
direct/src/extensions/Mat3-extensions.py

@@ -1,16 +0,0 @@
-
-    """
-    Mat3-extensions module: contains methods to extend functionality
-    of the LMatrix3f class.
-    """
-
-    def __repr__(self):
-        return '%s(\n%s,\n%s,\n%s)' % (
-            self.__class__.__name__, self.getRow(0).pPrintValues(), self.getRow(1).pPrintValues(), self.getRow(2).pPrintValues())
-
-    def pPrintValues(self):
-        """
-        Pretty print
-        """
-        return "\n%s\n%s\n%s" % (
-            self.getRow(0).pPrintValues(), self.getRow(1).pPrintValues(), self.getRow(2).pPrintValues())

+ 0 - 27
direct/src/extensions/MouseWatcherRegion-extensions.py

@@ -1,27 +0,0 @@
-
-    """
-    MouseWatcherRegion-extensions module: contains methods to extend
-    functionality of the MouseWatcherRegion class
-    """
-
-    def setRelative(self, np, left, right, bottom, top):
-        """setRelation(NodePath np, float left, float right,
-                       float bottom, float top)
-
-        Sets the region to represnt the indicated rectangle, relative
-        to the given NodePath.  It is assumed that np represents some
-        node parented within the render2d hierarchy.
-
-        """
-        from pandac import Point3
-        
-        # Get the relative transform to the node.
-        mat = np.getMat(render2d)
-
-        # Use this matrix to transform the corners of the region.
-        ll = mat.xformPoint(Point3.Point3(left, 0, bottom))
-        ur = mat.xformPoint(Point3.Point3(right, 0, top))
-
-        # Set the frame to the transformed coordinates.
-        self.setFrame(ll[0], ur[0], ll[2], ur[2])
-        

+ 0 - 20
direct/src/extensions/Node-extensions.py

@@ -1,20 +0,0 @@
-
-    """
-    Node-extensions module: contains methods to extend functionality
-    of the Node class
-    """
-
-    def isHidden(self):
-        """Determine if a node is hidden.  Just pick the first parent,
-        since this is an ambiguous question for instanced nodes"""
-        import RenderRelation
-        import PruneTransition
-        rrClass = RenderRelation.RenderRelation.getClassType()
-        if self.getNumParents(rrClass) > 0:
-            arc = self.getParent(rrClass, 0)
-            if arc.hasTransition(PruneTransition.PruneTransition.getClassType()):
-                return 1
-            else:
-                return arc.getParent().isHidden()
-        else:
-            return 0

+ 0 - 1093
direct/src/extensions/NodePath-extensions.py

@@ -1,1093 +0,0 @@
-
-    """
-    NodePath-extensions module: contains methods to extend functionality
-    of the NodePath class
-    """
-
-    def id(self):
-        """Returns a unique id identifying the NodePath instance"""
-        return self.getKey()
-
-    def __hash__(self):
-        return self.getKey()
-
-    # For iterating over children
-    def getChildrenAsList(self):
-        """Converts a node path's child NodePathCollection into a list"""
-        return self.getChildren()
-
-    def printChildren(self):
-        """Prints out the children of the bottom node of a node path"""
-        for child in self.getChildren():
-            print child.getName()
-
-    def removeChildren(self):
-        """Deletes the children of the bottom node of a node path"""
-        for child in self.getChildren():
-            child.removeNode()
-
-    def toggleVis(self):
-        """Toggles visibility of a nodePath"""
-        if self.isHidden():
-            self.show()
-            return 1
-        else:
-            self.hide()
-            return 0
-
-    def showSiblings(self):
-        """Show all the siblings of a node path"""
-        for sib in self.getParent().getChildren():
-            if sib.node() != self.node():
-                sib.show()
-
-    def hideSiblings(self):
-        """Hide all the siblings of a node path"""
-        for sib in self.getParent().getChildren():
-            if sib.node() != self.node():
-                sib.hide()
-
-    def showAllDescendants(self):
-        """Show the node path and all its children"""
-        self.show()
-        for child in self.getChildren():
-            child.showAllDescendants()
-
-    def isolate(self):
-        """Show the node path and hide its siblings"""
-        self.showAllDescendants()
-        self.hideSiblings()
-
-    def remove(self):
-        """Remove a node path from the scene graph"""
-        # Send message in case anyone needs to do something
-        # before node is deleted
-        messenger.send('preRemoveNodePath', [self])
-        # Remove nodePath
-        self.removeNode()
-
-    def lsNames(self):
-        """Walk down a tree and print out the path"""
-        if self.isEmpty():
-            print "(empty)"
-        else:
-            type = self.node().getType().getName()
-            name = self.getName()
-            print type + "  " + name
-            self.lsNamesRecurse()
-
-    def lsNamesRecurse(self, indentString=' '):
-        """Walk down a tree and print out the path"""
-        for nodePath in self.getChildren():
-            type = nodePath.node().getType().getName()
-            name = nodePath.getName()
-            print indentString + type + "  " + name
-            nodePath.lsNamesRecurse(indentString + " ")
-
-    def reverseLsNames(self):
-        """Walk up a tree and print out the path to the root"""
-        ancestry = self.getAncestry()
-        indentString = ""
-        for nodePath in ancestry:
-            type = nodePath.node().getType().getName()
-            name = nodePath.getName()
-            print indentString + type + "  " + name
-            indentString = indentString + " "
-
-    def getAncestry(self):
-        """Get a list of a node path's ancestors"""
-        node = self.node()
-        if (self.hasParent()):
-            ancestry = self.getParent().getAncestry()
-            ancestry.append(self)
-            return ancestry
-        else:
-            return [self]
-
-    def getTightBounds(self):
-        from pandac import Point3
-        v1 = Point3.Point3(0)
-        v2 = Point3.Point3(0)
-        self.calcTightBounds(v1, v2)
-        return v1, v2
-
-    def pPrintString(self, other = None):
-        """
-        pretty print
-        """
-        if __debug__:
-            # Normally I would have put the if __debug__ around
-            # the entire funciton, the that doesn't seem to work
-            # with -extensions.  Maybe someone will look into
-            # this further.
-            if other:
-                pos = self.getPos(other)
-                hpr = self.getHpr(other)
-                scale = self.getScale(other)
-                shear = self.getShear(other)
-                otherString = "  'other': %s,\n" % (other.getName(),)
-            else:
-                pos = self.getPos()
-                hpr = self.getHpr()
-                scale = self.getScale()
-                shear = self.getShear()
-                otherString = '\n'
-            return (
-                "%s = {"%(self.getName()) +
-                otherString +
-                "  'Pos':   (%s),\n" % pos.pPrintValues() +
-                "  'Hpr':   (%s),\n" % hpr.pPrintValues() +
-                "  'Scale': (%s),\n" % scale.pPrintValues() +
-                "  'Shear': (%s),\n" % shear.pPrintValues() +
-                "}")
-
-    def printPos(self, other = None, sd = 2):
-        """ Pretty print a node path's pos """
-        formatString = '%0.' + '%d' % sd + 'f'
-        if other:
-            pos = self.getPos(other)
-            otherString = other.getName() + ', '
-        else:
-            pos = self.getPos()
-            otherString = ''
-        print (self.getName() + '.setPos(' + otherString +
-               formatString % pos[0] + ', ' +
-               formatString % pos[1] + ', ' +
-               formatString % pos[2] +
-               ')\n')
-
-    def printHpr(self, other = None, sd = 2):
-        """ Pretty print a node path's hpr """
-        formatString = '%0.' + '%d' % sd + 'f'
-        if other:
-            hpr = self.getHpr(other)
-            otherString = other.getName() + ', '
-        else:
-            hpr = self.getHpr()
-            otherString = ''
-        print (self.getName() + '.setHpr(' + otherString +
-               formatString % hpr[0] + ', ' +
-               formatString % hpr[1] + ', ' +
-               formatString % hpr[2] +
-               ')\n')
-
-    def printScale(self, other = None, sd = 2):
-        """ Pretty print a node path's scale """
-        formatString = '%0.' + '%d' % sd + 'f'
-        if other:
-            scale = self.getScale(other)
-            otherString = other.getName() + ', '
-        else:
-            scale = self.getScale()
-            otherString = ''
-        print (self.getName() + '.setScale(' + otherString +
-               formatString % scale[0] + ', ' +
-               formatString % scale[1] + ', ' +
-               formatString % scale[2] +
-               ')\n')
-
-    def printPosHpr(self, other = None, sd = 2):
-        """ Pretty print a node path's pos and, hpr """
-        formatString = '%0.' + '%d' % sd + 'f'
-        if other:
-            pos = self.getPos(other)
-            hpr = self.getHpr(other)
-            otherString = other.getName() + ', '
-        else:
-            pos = self.getPos()
-            hpr = self.getHpr()
-            otherString = ''
-        print (self.getName() + '.setPosHpr(' + otherString +
-               formatString % pos[0] + ', ' +
-               formatString % pos[1] + ', ' +
-               formatString % pos[2] + ', ' +
-               formatString % hpr[0] + ', ' +
-               formatString % hpr[1] + ', ' +
-               formatString % hpr[2] +
-               ')\n')
-
-    def printPosHprScale(self, other = None, sd = 2):
-        """ Pretty print a node path's pos, hpr, and scale """
-        formatString = '%0.' + '%d' % sd + 'f'
-        if other:
-            pos = self.getPos(other)
-            hpr = self.getHpr(other)
-            scale = self.getScale(other)
-            otherString = other.getName() + ', '
-        else:
-            pos = self.getPos()
-            hpr = self.getHpr()
-            scale = self.getScale()
-            otherString = ''
-        print (self.getName() + '.setPosHprScale(' + otherString +
-               formatString % pos[0] + ', ' +
-               formatString % pos[1] + ', ' +
-               formatString % pos[2] + ', ' +
-               formatString % hpr[0] + ', ' +
-               formatString % hpr[1] + ', ' +
-               formatString % hpr[2] + ', ' +
-               formatString % scale[0] + ', ' +
-               formatString % scale[1] + ', ' +
-               formatString % scale[2] +
-               ')\n')
-
-    def printTransform(self, other = None, sd = 2, fRecursive = 0):
-        from pandac.PandaModules import Vec3
-        fmtStr = '%%0.%df' % sd
-        name = self.getName()
-        if other == None:
-            transform = self.getTransform()
-        else:
-            transform = self.getTransform(other)
-        if transform.hasPos():
-            pos = transform.getPos()
-            if not pos.almostEqual(Vec3(0)):
-                outputString = '%s.setPos(%s, %s, %s)' % (name, fmtStr, fmtStr, fmtStr)
-                print outputString % (pos[0], pos[1], pos[2])
-        if transform.hasHpr():
-            hpr = transform.getHpr()
-            if not hpr.almostEqual(Vec3(0)):
-                outputString = '%s.setHpr(%s, %s, %s)' % (name, fmtStr, fmtStr, fmtStr)
-                print outputString % (hpr[0], hpr[1], hpr[2])
-        if transform.hasScale():
-            if transform.hasUniformScale():
-                scale = transform.getUniformScale()
-                if scale != 1.0:
-                    outputString = '%s.setScale(%s)' % (name, fmtStr)
-                    print outputString % scale
-            else:
-                scale = transform.getScale()
-                if not scale.almostEqual(Vec3(1)):
-                    outputString = '%s.setScale(%s, %s, %s)' % (name, fmtStr, fmtStr, fmtStr)
-                    print outputString % (scale[0], scale[1], scale[2])
-        if fRecursive:
-            for child in self.getChildren():
-                child.printTransform(other, sd, fRecursive)
-
-    def iPos(self, other = None):
-        """ Set node path's pos to 0, 0, 0 """
-        if other:
-            self.setPos(other, 0, 0, 0)
-        else:
-            self.setPos(0, 0, 0)
-
-    def iHpr(self, other = None):
-        """ Set node path's hpr to 0, 0, 0 """
-        if other:
-            self.setHpr(other, 0, 0, 0)
-        else:
-            self.setHpr(0, 0, 0)
-
-    def iScale(self, other = None):
-        """ SEt node path's scale to 1, 1, 1 """
-        if other:
-            self.setScale(other, 1, 1, 1)
-        else:
-            self.setScale(1, 1, 1)
-
-    def iPosHpr(self, other = None):
-        """ Set node path's pos and hpr to 0, 0, 0 """
-        if other:
-            self.setPosHpr(other, 0, 0, 0, 0, 0, 0)
-        else:
-            self.setPosHpr(0, 0, 0, 0, 0, 0)
-
-    def iPosHprScale(self, other = None):
-        """ Set node path's pos and hpr to 0, 0, 0 and scale to 1, 1, 1 """
-        if other:
-            self.setPosHprScale(other, 0, 0, 0, 0, 0, 0, 1, 1, 1)
-        else:
-            self.setPosHprScale(0, 0, 0, 0, 0, 0, 1, 1, 1)
-
-    # private methods
-
-    def __lerp(self, functorFunc, duration, blendType, taskName=None):
-        """
-        __lerp(self, functorFunc, float, string, string)
-        Basic lerp functionality used by other lerps.
-        Fire off a lerp. Make it a task if taskName given.
-        """
-        # functorFunc is a function which can be called to create a functor.
-        # functor creation is defered so initial state (sampled in functorFunc)
-        # will be appropriate for the time the lerp is spawned
-        from direct.task import Task
-        from direct.interval import LerpBlendHelpers
-        from direct.task.TaskManagerGlobal import taskMgr
-
-        # upon death remove the functorFunc
-        def lerpUponDeath(task):
-            # Try to break circular references
-            try:
-                del task.functorFunc
-            except:
-                pass
-            try:
-                del task.lerp
-            except:
-                pass
-
-        # make the task function
-        def lerpTaskFunc(task):
-            from pandac.Lerp import Lerp
-            from pandac.ClockObject import ClockObject
-            from direct.task.Task import Task, cont, done
-            if task.init == 1:
-                # make the lerp
-                functor = task.functorFunc()
-                task.lerp = Lerp(functor, task.duration, task.blendType)
-                task.init = 0
-            dt = globalClock.getDt()
-            task.lerp.setStepSize(dt)
-            task.lerp.step()
-            if (task.lerp.isDone()):
-                # Reset the init flag, in case the task gets re-used
-                task.init = 1
-                return(done)
-            else:
-                return(cont)
-
-        # make the lerp task
-        lerpTask = Task.Task(lerpTaskFunc)
-        lerpTask.init = 1
-        lerpTask.functorFunc = functorFunc
-        lerpTask.duration = duration
-        lerpTask.blendType = LerpBlendHelpers.getBlend(blendType)
-        lerpTask.setUponDeath(lerpUponDeath)
-
-        if (taskName == None):
-            # don't spawn a task, return one instead
-            return lerpTask
-        else:
-            # spawn the lerp task
-            taskMgr.add(lerpTask, taskName)
-            return lerpTask
-
-    def __autoLerp(self, functorFunc, time, blendType, taskName):
-        """_autoLerp(self, functor, float, string, string)
-        This lerp uses C++ to handle the stepping. Bonus is
-        its more efficient, trade-off is there is less control"""
-        from pandac import AutonomousLerp
-        from direct.interval import LerpBlendHelpers
-        # make a lerp that lives in C++ land
-        functor = functorFunc()
-        lerp = AutonomousLerp.AutonomousLerp(functor, time,
-                              LerpBlendHelpers.getBlend(blendType),
-                              base.eventHandler)
-        lerp.start()
-        return lerp
-
-
-    # user callable lerp methods
-    def lerpColor(self, *posArgs, **keyArgs):
-        """
-        determine which lerpColor* to call based on arguments
-        """
-        if (len(posArgs) == 2):
-            return apply(self.lerpColorVBase4, posArgs, keyArgs)
-        elif (len(posArgs) == 3):
-            return apply(self.lerpColorVBase4VBase4, posArgs, keyArgs)
-        elif (len(posArgs) == 5):
-            return apply(self.lerpColorRGBA, posArgs, keyArgs)
-        elif (len(posArgs) == 9):
-            return apply(self.lerpColorRGBARGBA, posArgs, keyArgs)
-        else:
-            # bad args
-            raise Exception("Error: NodePath.lerpColor: bad number of args")
-
-
-    def lerpColorRGBA(self, r, g, b, a, time,
-                      blendType="noBlend", auto=None, task=None):
-        """lerpColorRGBA(self, float, float, float, float, float,
-        string="noBlend", string=none, string=none)
-        """
-        def functorFunc(self = self, r = r, g = g, b = b, a = a):
-            from pandac import ColorLerpFunctor
-            # just end rgba values, use current color rgba values for start
-            startColor = self.getColor()
-            functor = ColorLerpFunctor.ColorLerpFunctor(
-                self,
-                startColor[0], startColor[1],
-                startColor[2], startColor[3],
-                r, g, b, a)
-            return functor
-        #determine whether to use auto, spawned, or blocking lerp
-        if (auto != None):
-            return self.__autoLerp(functorFunc, time, blendType, auto)
-        elif (task != None):
-            return self.__lerp(functorFunc, time, blendType, task)
-        else:
-            return self.__lerp(functorFunc, time, blendType)
-
-    def lerpColorRGBARGBA(self, sr, sg, sb, sa, er, eg, eb, ea, time,
-                          blendType="noBlend", auto=None, task=None):
-        """lerpColorRGBARGBA(self, float, float, float, float, float,
-        float, float, float, float, string="noBlend", string=none, string=none)
-        """
-        def functorFunc(self = self, sr = sr, sg = sg, sb = sb, sa = sa,
-                        er = er, eg = eg, eb = eb, ea = ea):
-            from pandac import ColorLerpFunctor
-            # start and end rgba values
-            functor = ColorLerpFunctor.ColorLerpFunctor(self, sr, sg, sb, sa,
-                                                        er, eg, eb, ea)
-            return functor
-        #determine whether to use auto, spawned, or blocking lerp
-        if (auto != None):
-            return self.__autoLerp(functorFunc, time, blendType, auto)
-        elif (task != None):
-            return self.__lerp(functorFunc, time, blendType, task)
-        else:
-            return self.__lerp(functorFunc, time, blendType)
-
-    def lerpColorVBase4(self, endColor, time,
-                        blendType="noBlend", auto=None, task=None):
-        """lerpColorVBase4(self, VBase4, float, string="noBlend", string=none,
-        string=none)
-        """
-        def functorFunc(self = self, endColor = endColor):
-            from pandac import ColorLerpFunctor
-            # just end vec4, use current color for start
-            startColor = self.getColor()
-            functor = ColorLerpFunctor.ColorLerpFunctor(
-                self, startColor, endColor)
-            return functor
-        #determine whether to use auto, spawned, or blocking lerp
-        if (auto != None):
-            return self.__autoLerp(functorFunc, time, blendType, auto)
-        elif (task != None):
-            return self.__lerp(functorFunc, time, blendType, task)
-        else:
-            return self.__lerp(functorFunc, time, blendType)
-
-    def lerpColorVBase4VBase4(self, startColor, endColor, time,
-                          blendType="noBlend", auto=None, task=None):
-        """lerpColorVBase4VBase4(self, VBase4, VBase4, float, string="noBlend",
-        string=none, string=none)
-        """
-        def functorFunc(self = self, startColor = startColor,
-                        endColor = endColor):
-            from pandac import ColorLerpFunctor
-            # start color and end vec
-            functor = ColorLerpFunctor.ColorLerpFunctor(
-                self, startColor, endColor)
-            return functor
-        #determine whether to use auto, spawned, or blocking lerp
-        if (auto != None):
-            return self.__autoLerp(functorFunc, time, blendType, auto)
-        elif (task != None):
-            return self.__lerp(functorFunc, time, blendType, task)
-        else:
-            return self.__lerp(functorFunc, time, blendType)
-
-
-
-    # user callable lerp methods
-    def lerpColorScale(self, *posArgs, **keyArgs):
-        """lerpColorScale(self, *positionArgs, **keywordArgs)
-        determine which lerpColorScale* to call based on arguments
-        """
-        if (len(posArgs) == 2):
-            return apply(self.lerpColorScaleVBase4, posArgs, keyArgs)
-        elif (len(posArgs) == 3):
-            return apply(self.lerpColorScaleVBase4VBase4, posArgs, keyArgs)
-        elif (len(posArgs) == 5):
-            return apply(self.lerpColorScaleRGBA, posArgs, keyArgs)
-        elif (len(posArgs) == 9):
-            return apply(self.lerpColorScaleRGBARGBA, posArgs, keyArgs)
-        else:
-            # bad args
-            raise Exception("Error: NodePath.lerpColorScale: bad number of args")
-
-
-    def lerpColorScaleRGBA(self, r, g, b, a, time,
-                      blendType="noBlend", auto=None, task=None):
-        """lerpColorScaleRGBA(self, float, float, float, float, float,
-        string="noBlend", string=none, string=none)
-        """
-        def functorFunc(self = self, r = r, g = g, b = b, a = a):
-            from pandac import ColorScaleLerpFunctor
-            # just end rgba values, use current color rgba values for start
-            startColor = self.getColor()
-            functor = ColorScaleLerpFunctor.ColorScaleLerpFunctor(
-                self,
-                startColor[0], startColor[1],
-                startColor[2], startColor[3],
-                r, g, b, a)
-            return functor
-        #determine whether to use auto, spawned, or blocking lerp
-        if (auto != None):
-            return self.__autoLerp(functorFunc, time, blendType, auto)
-        elif (task != None):
-            return self.__lerp(functorFunc, time, blendType, task)
-        else:
-            return self.__lerp(functorFunc, time, blendType)
-
-    def lerpColorScaleRGBARGBA(self, sr, sg, sb, sa, er, eg, eb, ea, time,
-                          blendType="noBlend", auto=None, task=None):
-        """lerpColorScaleRGBARGBA(self, float, float, float, float, float,
-        float, float, float, float, string="noBlend", string=none, string=none)
-        """
-        def functorFunc(self = self, sr = sr, sg = sg, sb = sb, sa = sa,
-                        er = er, eg = eg, eb = eb, ea = ea):
-            from pandac import ColorScaleLerpFunctor
-            # start and end rgba values
-            functor = ColorScaleLerpFunctor.ColorScaleLerpFunctor(self, sr, sg, sb, sa,
-                                                        er, eg, eb, ea)
-            return functor
-        #determine whether to use auto, spawned, or blocking lerp
-        if (auto != None):
-            return self.__autoLerp(functorFunc, time, blendType, auto)
-        elif (task != None):
-            return self.__lerp(functorFunc, time, blendType, task)
-        else:
-            return self.__lerp(functorFunc, time, blendType)
-
-    def lerpColorScaleVBase4(self, endColor, time,
-                        blendType="noBlend", auto=None, task=None):
-        """lerpColorScaleVBase4(self, VBase4, float, string="noBlend", string=none,
-        string=none)
-        """
-        def functorFunc(self = self, endColor = endColor):
-            from pandac import ColorScaleLerpFunctor
-            # just end vec4, use current color for start
-            startColor = self.getColor()
-            functor = ColorScaleLerpFunctor.ColorScaleLerpFunctor(
-                self, startColor, endColor)
-            return functor
-        #determine whether to use auto, spawned, or blocking lerp
-        if (auto != None):
-            return self.__autoLerp(functorFunc, time, blendType, auto)
-        elif (task != None):
-            return self.__lerp(functorFunc, time, blendType, task)
-        else:
-            return self.__lerp(functorFunc, time, blendType)
-
-    def lerpColorScaleVBase4VBase4(self, startColor, endColor, time,
-                          blendType="noBlend", auto=None, task=None):
-        """lerpColorScaleVBase4VBase4(self, VBase4, VBase4, float, string="noBlend",
-        string=none, string=none)
-        """
-        def functorFunc(self = self, startColor = startColor,
-                        endColor = endColor):
-            from pandac import ColorScaleLerpFunctor
-            # start color and end vec
-            functor = ColorScaleLerpFunctor.ColorScaleLerpFunctor(
-                self, startColor, endColor)
-            return functor
-        #determine whether to use auto, spawned, or blocking lerp
-        if (auto != None):
-            return self.__autoLerp(functorFunc, time, blendType, auto)
-        elif (task != None):
-            return self.__lerp(functorFunc, time, blendType, task)
-        else:
-            return self.__lerp(functorFunc, time, blendType)
-
-
-
-    def lerpHpr(self, *posArgs, **keyArgs):
-        """lerpHpr(self, *positionArgs, **keywordArgs)
-        Determine whether to call lerpHprHPR or lerpHprVBase3
-        based on first argument
-        """
-        # check to see if lerping with
-        # three floats or a VBase3
-        if (len(posArgs) == 4):
-            return apply(self.lerpHprHPR, posArgs, keyArgs)
-        elif(len(posArgs) == 2):
-            return apply(self.lerpHprVBase3, posArgs, keyArgs)
-        else:
-            # bad args
-            raise Exception("Error: NodePath.lerpHpr: bad number of args")
-
-    def lerpHprHPR(self, h, p, r, time, other=None,
-                   blendType="noBlend", auto=None, task=None, shortest=1):
-        """lerpHprHPR(self, float, float, float, float, string="noBlend",
-        string=none, string=none, NodePath=none)
-        Perform a hpr lerp with three floats as the end point
-        """
-        def functorFunc(self = self, h = h, p = p, r = r,
-                        other = other, shortest=shortest):
-            from pandac import HprLerpFunctor
-            # it's individual hpr components
-            if (other != None):
-                # lerp wrt other
-                startHpr = self.getHpr(other)
-                functor = HprLerpFunctor.HprLerpFunctor(
-                    self,
-                    startHpr[0], startHpr[1], startHpr[2],
-                    h, p, r, other)
-                if shortest:
-                    functor.takeShortest()
-            else:
-                startHpr = self.getHpr()
-                functor = HprLerpFunctor.HprLerpFunctor(
-                    self,
-                    startHpr[0], startHpr[1], startHpr[2],
-                    h, p, r)
-                if shortest:
-                    functor.takeShortest()
-            return functor
-        #determine whether to use auto, spawned, or blocking lerp
-        if (auto != None):
-            return self.__autoLerp(functorFunc, time, blendType, auto)
-        elif (task != None):
-            return self.__lerp(functorFunc, time, blendType, task)
-        else:
-            return self.__lerp(functorFunc, time, blendType)
-
-    def lerpHprVBase3(self, hpr, time, other=None,
-                      blendType="noBlend", auto=None, task=None, shortest=1):
-        """lerpHprVBase3(self, VBase3, float, string="noBlend", string=none,
-        string=none, NodePath=None)
-        Perform a hpr lerp with a VBase3 as the end point
-        """
-        def functorFunc(self = self, hpr = hpr,
-                        other = other, shortest=shortest):
-            from pandac import HprLerpFunctor
-            # it's a vbase3 hpr
-            if (other != None):
-                # lerp wrt other
-                functor = HprLerpFunctor.HprLerpFunctor(
-                    self, (self.getHpr(other)), hpr, other)
-                if shortest:
-                    functor.takeShortest()
-            else:
-                functor = HprLerpFunctor.HprLerpFunctor(
-                    self, (self.getHpr()), hpr)
-                if shortest:
-                    functor.takeShortest()
-            return functor
-        #determine whether to use auto, spawned, or blocking lerp
-        if (auto != None):
-            return self.__autoLerp(functorFunc, time, blendType, auto)
-        elif (task != None):
-            return self.__lerp(functorFunc, time, blendType, task)
-        else:
-            return self.__lerp(functorFunc, time, blendType)
-
-
-    def lerpPos(self, *posArgs, **keyArgs):
-        """lerpPos(self, *positionArgs, **keywordArgs)
-        Determine whether to call lerpPosXYZ or lerpPosPoint3
-        based on the first argument
-        """
-        # check to see if lerping with three
-        # floats or a Point3
-        if (len(posArgs) == 4):
-            return apply(self.lerpPosXYZ, posArgs, keyArgs)
-        elif(len(posArgs) == 2):
-            return apply(self.lerpPosPoint3, posArgs, keyArgs)
-        else:
-            # bad number off args
-            raise Exception("Error: NodePath.lerpPos: bad number of args")
-
-    def lerpPosXYZ(self, x, y, z, time, other=None,
-                   blendType="noBlend", auto=None, task=None):
-        """lerpPosXYZ(self, float, float, float, float, string="noBlend",
-        string=None, NodePath=None)
-        Perform a pos lerp with three floats as the end point
-        """
-        def functorFunc(self = self, x = x, y = y, z = z, other = other):
-            from pandac import PosLerpFunctor
-            if (other != None):
-                # lerp wrt other
-                startPos = self.getPos(other)
-                functor = PosLerpFunctor.PosLerpFunctor(self,
-                                         startPos[0], startPos[1], startPos[2],
-                                         x, y, z, other)
-            else:
-                startPos = self.getPos()
-                functor = PosLerpFunctor.PosLerpFunctor(self, startPos[0],
-                                         startPos[1], startPos[2], x, y, z)
-            return functor
-        #determine whether to use auto, spawned, or blocking lerp
-        if (auto != None):
-            return  self.__autoLerp(functorFunc, time, blendType, auto)
-        elif (task != None):
-            return self.__lerp(functorFunc, time, blendType, task)
-        else:
-            return self.__lerp(functorFunc, time, blendType)
-
-    def lerpPosPoint3(self, pos, time, other=None,
-                      blendType="noBlend", auto=None, task=None):
-        """lerpPosPoint3(self, Point3, float, string="noBlend", string=None,
-        string=None, NodePath=None)
-        Perform a pos lerp with a Point3 as the end point
-        """
-        def functorFunc(self = self, pos = pos, other = other):
-            from pandac import PosLerpFunctor
-            if (other != None):
-                #lerp wrt other
-                functor = PosLerpFunctor.PosLerpFunctor(
-                    self, (self.getPos(other)), pos, other)
-            else:
-                functor = PosLerpFunctor.PosLerpFunctor(
-                    self, (self.getPos()), pos)
-            return functor
-        #determine whether to use auto, spawned, or blocking lerp
-        if (auto != None):
-            return self.__autoLerp(functorFunc, time, blendType, auto)
-        elif (task != None):
-            return self.__lerp(functorFunc, time, blendType, task)
-        else:
-            return self.__lerp(functorFunc, time, blendType)
-
-
-    def lerpPosHpr(self, *posArgs, **keyArgs):
-        """lerpPosHpr(self, *positionArgs, **keywordArgs)
-        Determine whether to call lerpPosHprXYZHPR or lerpHprPoint3VBase3
-        based on first argument
-        """
-        # check to see if lerping with
-        # six floats or a Point3 and a VBase3
-        if (len(posArgs) == 7):
-            return apply(self.lerpPosHprXYZHPR, posArgs, keyArgs)
-        elif(len(posArgs) == 3):
-            return apply(self.lerpPosHprPoint3VBase3, posArgs, keyArgs)
-        else:
-            # bad number off args
-            raise Exception("Error: NodePath.lerpPosHpr: bad number of args")
-
-    def lerpPosHprPoint3VBase3(self, pos, hpr, time, other=None,
-                               blendType="noBlend", auto=None, task=None, shortest=1):
-        """lerpPosHprPoint3VBase3(self, Point3, VBase3, string="noBlend",
-        string=none, string=none, NodePath=None)
-        """
-        def functorFunc(self = self, pos = pos, hpr = hpr,
-                        other = other, shortest=shortest):
-            from pandac import PosHprLerpFunctor
-            if (other != None):
-                # lerp wrt other
-                startPos = self.getPos(other)
-                startHpr = self.getHpr(other)
-                functor = PosHprLerpFunctor.PosHprLerpFunctor(
-                    self, startPos, pos,
-                    startHpr, hpr, other)
-                if shortest:
-                    functor.takeShortest()
-            else:
-                startPos = self.getPos()
-                startHpr = self.getHpr()
-                functor = PosHprLerpFunctor.PosHprLerpFunctor(
-                    self, startPos, pos,
-                    startHpr, hpr)
-                if shortest:
-                    functor.takeShortest()
-            return functor
-        #determine whether to use auto, spawned, or blocking lerp
-        if (auto != None):
-            return self.__autoLerp(functorFunc, time, blendType, auto)
-        elif (task != None):
-            return self.__lerp(functorFunc, time, blendType, task)
-        else:
-            return self.__lerp(functorFunc, time, blendType)
-
-    def lerpPosHprXYZHPR(self, x, y, z, h, p, r, time, other=None,
-                         blendType="noBlend", auto=None, task=None, shortest=1):
-        """lerpPosHpr(self, float, string="noBlend", string=none,
-        string=none, NodePath=None)
-        """
-        def functorFunc(self = self, x = x, y = y, z = z,
-                        h = h, p = p, r = r, other = other, shortest=shortest):
-            from pandac import PosHprLerpFunctor
-            if (other != None):
-                # lerp wrt other
-                startPos = self.getPos(other)
-                startHpr = self.getHpr(other)
-                functor = PosHprLerpFunctor.PosHprLerpFunctor(self,
-                                            startPos[0], startPos[1],
-                                            startPos[2], x, y, z,
-                                            startHpr[0], startHpr[1],
-                                            startHpr[2], h, p, r,
-                                            other)
-                if shortest:
-                    functor.takeShortest()
-            else:
-                startPos = self.getPos()
-                startHpr = self.getHpr()
-                functor = PosHprLerpFunctor.PosHprLerpFunctor(self,
-                                            startPos[0], startPos[1],
-                                            startPos[2], x, y, z,
-                                            startHpr[0], startHpr[1],
-                                            startHpr[2], h, p, r)
-                if shortest:
-                    functor.takeShortest()
-            return functor
-        #determine whether to use auto, spawned, or blocking lerp
-        if (auto != None):
-            return self.__autoLerp(functorFunc, time, blendType, auto)
-        elif (task != None):
-            return self.__lerp(functorFunc, time, blendType, task)
-        else:
-            return self.__lerp(functorFunc, time, blendType)
-
-
-    def lerpPosHprScale(self, pos, hpr, scale, time, other=None,
-                        blendType="noBlend", auto=None, task=None, shortest=1):
-        """lerpPosHpr(self, Point3, VBase3, float, float, string="noBlend",
-        string=none, string=none, NodePath=None)
-        Only one case, no need for extra args. Call the appropriate lerp
-        (auto, spawned, or blocking) based on how(if) a task name is given
-        """
-        def functorFunc(self = self, pos = pos, hpr = hpr,
-                        scale = scale, other = other, shortest=shortest):
-            from pandac import PosHprScaleLerpFunctor
-            if (other != None):
-                # lerp wrt other
-                startPos = self.getPos(other)
-                startHpr = self.getHpr(other)
-                startScale = self.getScale(other)
-                functor = PosHprScaleLerpFunctor.PosHprScaleLerpFunctor(self,
-                                                 startPos, pos,
-                                                 startHpr, hpr,
-                                                 startScale, scale, other)
-                if shortest:
-                    functor.takeShortest()
-            else:
-                startPos = self.getPos()
-                startHpr = self.getHpr()
-                startScale = self.getScale()
-                functor = PosHprScaleLerpFunctor.PosHprScaleLerpFunctor(self,
-                                                 startPos, pos,
-                                                 startHpr, hpr,
-                                                 startScale, scale)
-                if shortest:
-                    functor.takeShortest()
-            return functor
-        #determine whether to use auto, spawned, or blocking lerp
-        if (auto != None):
-            return self.__autoLerp(functorFunc, time, blendType, auto)
-        elif (task != None):
-            return self.__lerp(functorFunc, time, blendType, task)
-        else:
-            return self.__lerp(functorFunc, time, blendType)
-
-
-    def lerpScale(self, *posArgs, **keyArgs):
-        """lerpSclae(self, *positionArgs, **keywordArgs)
-        Determine whether to call lerpScaleXYZ or lerpScaleaseV3
-        based on the first argument
-        """
-        # check to see if lerping with three
-        # floats or a Point3
-        if (len(posArgs) == 4):
-            return apply(self.lerpScaleXYZ, posArgs, keyArgs)
-        elif(len(posArgs) == 2):
-            return apply(self.lerpScaleVBase3, posArgs, keyArgs)
-        else:
-            # bad number off args
-            raise Exception("Error: NodePath.lerpScale: bad number of args")
-
-    def lerpScaleVBase3(self, scale, time, other=None,
-                        blendType="noBlend", auto=None, task=None):
-        """lerpPos(self, VBase3, float, string="noBlend", string=none,
-        string=none, NodePath=None)
-        """
-        def functorFunc(self = self, scale = scale, other = other):
-            from pandac import ScaleLerpFunctor
-            if (other != None):
-                # lerp wrt other
-                functor = ScaleLerpFunctor.ScaleLerpFunctor(self,
-                                           (self.getScale(other)),
-                                           scale, other)
-            else:
-                functor = ScaleLerpFunctor.ScaleLerpFunctor(self,
-                                           (self.getScale()), scale)
-
-            return functor
-        #determine whether to use auto, spawned, or blocking lerp
-        if (auto != None):
-            return self.__autoLerp(functorFunc, time, blendType, auto)
-        elif (task != None):
-            return self.__lerp(functorFunc, time, blendType, task)
-        else:
-            return self.__lerp(functorFunc, time, blendType)
-
-    def lerpScaleXYZ(self, sx, sy, sz, time, other=None,
-                     blendType="noBlend", auto=None, task=None):
-        """lerpPos(self, float, float, float, float, string="noBlend",
-        string=none, string=none, NodePath=None)
-        """
-        def functorFunc(self = self, sx = sx, sy = sy, sz = sz, other = other):
-            from pandac import ScaleLerpFunctor
-            if (other != None):
-                # lerp wrt other
-                startScale = self.getScale(other)
-                functor = ScaleLerpFunctor.ScaleLerpFunctor(self,
-                                           startScale[0], startScale[1],
-                                           startScale[2], sx, sy, sz, other)
-            else:
-                startScale = self.getScale()
-                functor = ScaleLerpFunctor.ScaleLerpFunctor(self,
-                                           startScale[0], startScale[1],
-                                           startScale[2], sx, sy, sz)
-            return functor
-        #determine whether to use auto, spawned, or blocking lerp
-        if (auto != None):
-            return self.__autoLerp(functorFunc, time, blendType, auto)
-        elif (task != None):
-            return self.__lerp(functorFunc, time, blendType, task)
-        else:
-            return self.__lerp(functorFunc, time, blendType)
-
-
-
-
-    def place(self):
-        base.startDirect(fWantTk = 1)
-        from direct.tkpanels import Placer
-        return Placer.place(self)
-
-    def explore(self):
-        base.startDirect(fWantTk = 1)
-        from direct.tkwidgets import SceneGraphExplorer
-        return SceneGraphExplorer.explore(self)
-
-    def rgbPanel(self, cb = None):
-        base.startTk()
-        from direct.tkwidgets import Slider
-        return Slider.rgbPanel(self, cb)
-
-    def select(self):
-        base.startDirect(fWantTk = 0)
-        base.direct.select(self)
-
-    def deselect(self):
-        base.startDirect(fWantTk = 0)
-        base.direct.deselect(self)
-
-    def showCS(self, mask = None):
-        """
-        Shows the collision solids at or below this node.  If mask is
-        not None, it is a BitMask32 object (e.g. WallBitmask,
-        CameraBitmask) that indicates which particular collision
-        solids should be made visible; otherwise, all of them will be.
-        """
-        npc = self.findAllMatches('**/+CollisionNode')
-        for p in range(0, npc.getNumPaths()):
-            np = npc[p]
-            if (mask == None or (np.node().getIntoCollideMask() & mask).getWord()):
-                np.show()
-
-    def hideCS(self, mask = None):
-        """
-        Hides the collision solids at or below this node.  If mask is
-        not None, it is a BitMask32 object (e.g. WallBitmask,
-        CameraBitmask) that indicates which particular collision
-        solids should be hidden; otherwise, all of them will be.
-        """
-        npc = self.findAllMatches('**/+CollisionNode')
-        for p in range(0, npc.getNumPaths()):
-            np = npc[p]
-            if (mask == None or (np.node().getIntoCollideMask() & mask).getWord()):
-                np.hide()
-
-    def posInterval(self, *args, **kw):
-        from direct.interval import LerpInterval
-        return LerpInterval.LerpPosInterval(self, *args, **kw)
-
-    def hprInterval(self, *args, **kw):
-        from direct.interval import LerpInterval
-        return LerpInterval.LerpHprInterval(self, *args, **kw)
-
-    def quatInterval(self, *args, **kw):
-        from direct.interval import LerpInterval
-        return LerpInterval.LerpQuatInterval(self, *args, **kw)
-
-    def scaleInterval(self, *args, **kw):
-        from direct.interval import LerpInterval
-        return LerpInterval.LerpScaleInterval(self, *args, **kw)
-
-    def shearInterval(self, *args, **kw):
-        from direct.interval import LerpInterval
-        return LerpInterval.LerpShearInterval(self, *args, **kw)
-
-    def posHprInterval(self, *args, **kw):
-        from direct.interval import LerpInterval
-        return LerpInterval.LerpPosHprInterval(self, *args, **kw)
-
-    def posQuatInterval(self, *args, **kw):
-        from direct.interval import LerpInterval
-        return LerpInterval.LerpPosQuatInterval(self, *args, **kw)
-
-    def hprScaleInterval(self, *args, **kw):
-        from direct.interval import LerpInterval
-        return LerpInterval.LerpHprScaleInterval(self, *args, **kw)
-
-    def quatScaleInterval(self, *args, **kw):
-        from direct.interval import LerpInterval
-        return LerpInterval.LerpQuatScaleInterval(self, *args, **kw)
-
-    def posHprScaleInterval(self, *args, **kw):
-        from direct.interval import LerpInterval
-        return LerpInterval.LerpPosHprScaleInterval(self, *args, **kw)
-
-    def posQuatScaleInterval(self, *args, **kw):
-        from direct.interval import LerpInterval
-        return LerpInterval.LerpPosQuatScaleInterval(self, *args, **kw)
-
-    def posHprScaleShearInterval(self, *args, **kw):
-        from direct.interval import LerpInterval
-        return LerpInterval.LerpPosHprScaleShearInterval(self, *args, **kw)
-
-    def posQuatScaleShearInterval(self, *args, **kw):
-        from direct.interval import LerpInterval
-        return LerpInterval.LerpPosQuatScaleShearInterval(self, *args, **kw)
-
-    def colorInterval(self, *args, **kw):
-        from direct.interval import LerpInterval
-        return LerpInterval.LerpColorInterval(self, *args, **kw)
-
-    def colorScaleInterval(self, *args, **kw):
-        from direct.interval import LerpInterval
-        return LerpInterval.LerpColorScaleInterval(self, *args, **kw)
-
-    def attachCollisionSphere(self, name, cx, cy, cz, r, fromCollide, intoCollide):
-        from pandac import CollisionSphere
-        from pandac import CollisionNode
-        coll = CollisionSphere.CollisionSphere(cx, cy, cz, r)
-        collNode = CollisionNode.CollisionNode(name)
-        collNode.addSolid(coll)
-        collNode.setFromCollideMask(fromCollide)
-        collNode.setIntoCollideMask(intoCollide)
-        collNodePath = self.attachNewNode(collNode)
-        return collNodePath
-
-    def attachCollisionSegment(self, name, ax, ay, az, bx, by, bz, fromCollide, intoCollide):
-        from pandac import CollisionSegment
-        from pandac import CollisionNode
-        coll = CollisionSegment.CollisionSegment(ax, ay, az, bx, by, bz)
-        collNode = CollisionNode.CollisionNode(name)
-        collNode.addSolid(coll)
-        collNode.setFromCollideMask(fromCollide)
-        collNode.setIntoCollideMask(intoCollide)
-        collNodePath = self.attachNewNode(collNode)
-        return collNodePath
-
-    def attachCollisionRay(self, name, ox, oy, oz, dx, dy, dz, fromCollide, intoCollide):
-        from pandac import CollisionRay
-        from pandac import CollisionNode
-        coll = CollisionRay.CollisionRay(ox, oy, oz, dx, dy, dz)
-        collNode = CollisionNode.CollisionNode(name)
-        collNode.addSolid(coll)
-        collNode.setFromCollideMask(fromCollide)
-        collNode.setIntoCollideMask(intoCollide)
-        collNodePath = self.attachNewNode(collNode)
-        return collNodePath
-
-    def flattenMultitex(self, stateFrom = None, target = None,
-                        useGeom = 0, allowTexMat = 0, win = None):
-        from pandac import MultitexReducer
-        mr = MultitexReducer.MultitexReducer()
-        if target != None:
-            mr.setTarget(target)
-        mr.setUseGeom(useGeom)
-        mr.setAllowTexMat(allowTexMat)
-
-        if win == None:
-            win = base.win
-
-        if stateFrom == None:
-            mr.scan(self)
-        else:
-            mr.scan(self, stateFrom)
-        mr.flatten(win)

+ 0 - 26
direct/src/extensions/NurbsCurveEvaluator-extensions.py

@@ -1,26 +0,0 @@
-
-    """
-    NurbsCurveEvaluator-extensions module: contains methods to extend
-    functionality of the NurbsCurveEvaluator class
-    """
-
-    def getKnots(self):
-        """Returns the knot vector as a Python list of floats"""
-        knots = []
-        for i in range(self.getNumKnots()):
-            knots.append(self.getKnot(i))
-        return knots
-
-    def getVertices(self, relTo = None):
-        """Returns the vertices as a Python list of Vec4's, relative
-        to the indicated space if given."""
-        
-        verts = []
-        if relTo:
-            for i in range(self.getNumVertices()):
-                verts.append(self.getVertex(i, relTo))
-        else:
-            for i in range(self.getNumVertices()):
-                verts.append(self.getVertex(i))
-        return verts
-

+ 0 - 36
direct/src/extensions/NurbsSurfaceEvaluator-extensions.py

@@ -1,36 +0,0 @@
-
-    """
-    NurbsSurfaceEvaluator-extensions module: contains methods to extend
-    functionality of the NurbsSurfaceEvaluator class
-    """
-
-    def getUKnots(self):
-        """Returns the U knot vector as a Python list of floats"""
-        knots = []
-        for i in range(self.getNumUKnots()):
-            knots.append(self.getUKnot(i))
-        return knots
-
-    def getVKnots(self):
-        """Returns the V knot vector as a Python list of floats"""
-        knots = []
-        for i in range(self.getNumVKnots()):
-            knots.append(self.getVKnot(i))
-        return knots
-
-    def getVertices(self, relTo = None):
-        """Returns the vertices as a 2-d Python list of Vec4's, relative
-        to the indicated space if given."""
-
-        verts = []
-        for ui in range(self.getNumUVertices()):
-            v = []
-            if relTo:
-                for vi in range(self.getNumVVertices()):
-                    v.append(self.getVertex(ui, vi, relTo))
-            else:
-                for vi in range(self.getNumVVertices()):
-                    v.append(self.getVertex(ui, vi))
-            verts.append(v)
-            
-        return verts

+ 0 - 6
direct/src/extensions/PandaSystem-extensions.py

@@ -1,6 +0,0 @@
-
-    def getSystems(self):
-        l = []
-        for i in range(self.getNumSystems()):
-            l.append(self.getSystem(l))
-        return l

+ 0 - 3
direct/src/extensions/Sources.pp

@@ -1,3 +0,0 @@
-// For now, since we are not installing Python files, this file can
-// remain empty.
-

+ 0 - 83
direct/src/extensions/SpriteParticleRenderer-extensions.py

@@ -1,83 +0,0 @@
-
-    """
-    Contains methods to extend functionality
-    of the SpriteParticleRenderer class
-    """
-
-    # Initialize class variables for texture, source file and node for texture and
-    # node path textures to None.  These will be initialized to a hardcoded default
-    # or whatever the user specifies in his/her Configrc variable the first time they
-    # are accessed
-    # Will use instance copy of this in functions below
-    sourceTextureName = None
-    sourceFileName = None
-    sourceNodeName = None
-
-    def getSourceTextureName(self):
-        if self.sourceTextureName == None:
-            SpriteParticleRenderer.sourceTextureName = base.config.GetString(
-                'particle-sprite-texture', 'phase_3/maps/feyes.jpg')
-        # Return instance copy of class variable
-        return self.sourceTextureName
-
-    def setSourceTextureName(self, name):
-        # Set instance copy of class variable
-        self.sourceTextureName = name
-
-    def setTextureFromFile(self, fileName = None):
-        if fileName == None:
-            fileName = self.getSourceTextureName()
-        else:
-            self.setSourceTextureName(fileName)
-        t = loader.loadTexture(fileName)
-        if (t != None):
-            self.setTexture(t)
-        else:
-            print "Couldn't find rendererSpriteTexture file: %s" % fileName
-
-    def getSourceFileName(self):
-        if self.sourceFileName == None:
-            SpriteParticleRenderer.sourceFileName = base.config.GetString(
-                'particle-sprite-model', 'phase_3.5/models/props/suit-particles')
-        # Return instance copy of class variable
-        return self.sourceFileName
-
-    def setSourceFileName(self, name):
-        # Set instance copy of class variable
-        self.sourceFileName = name
-
-    def getSourceNodeName(self):
-        if self.sourceNodeName == None:
-            SpriteParticleRenderer.sourceNodeName = base.config.GetString(
-                'particle-sprite-node', '**/fire')
-        # Return instance copy of class variable
-        return self.sourceNodeName
-
-    def setSourceNodeName(self, name):
-        # Set instance copy of class variable
-        self.sourceNodeName = name
-
-    def setTextureFromNode(self, modelName = None, nodeName = None):
-        if modelName == None:
-            modelName = self.getSourceFileName()
-        else:
-            self.setSourceFileName(modelName)
-        if nodeName == None:
-            nodeName = self.getSourceNodeName()
-        else:
-            self.setSourceNodeName(nodeName)
-        # Load model and get texture
-        m = loader.loadModel(modelName)
-        if (m == None):
-            print "SpriteParticleRenderer: Couldn't find model: %s!" % modelName 
-            return None
-        nodeName = self.getSourceNodeName()
-        np = m.find(nodeName)
-        if np.isEmpty():
-            print "SpriteParticleRenderer: Couldn't find node: %s!" % nodeName
-            m.removeNode()
-            return None
-        self.setFromNode(np)
-        m.removeNode()
-        
-

+ 0 - 15
direct/src/extensions/VBase3-extensions.py

@@ -1,15 +0,0 @@
-
-    """
-    VBase3-extensions module: contains methods to extend functionality
-    of the VBase3 class
-    """
-
-    def __repr__(self):
-        return '%s(%s, %s, %s)' % (
-            self.__class__.__name__, self[0], self[1], self[2])
-
-    def pPrintValues(self):
-        """
-        Pretty print
-        """
-        return "% 10.4f, % 10.4f, % 10.4f" % (self[0], self[1], self[2])

+ 0 - 15
direct/src/extensions/VBase4-extensions.py

@@ -1,15 +0,0 @@
-
-    """
-    VBase4-extensions module: contains methods to extend functionality
-    of the VBase4 class
-    """
-
-    def __repr__(self):
-        return '%s(%s, %s, %s, %s)' % (
-            self.__class__.__name__, self[0], self[1], self[2], self[3])
-
-    def pPrintValues(self):
-        """
-        Pretty print
-        """
-        return '% 10.4f, % 10.4f, % 10.4f, % 10.4f' % (self[0], self[1], self[2], self[3])

+ 0 - 1
direct/src/extensions/__init__.py

@@ -1 +0,0 @@
-from PandaModules import *

+ 12 - 24
direct/src/p3d/AppRunner.py

@@ -13,22 +13,9 @@ __all__ = ["AppRunner", "dummyAppRunner", "ArgumentError"]
 
 import sys
 import os
-import __builtin__
-
-if 'VFSImporter' in sys.modules:
-    # If we've already got a VFSImporter module defined at the
-    # toplevel, we must have come in here by way of the
-    # p3dPythonRun.cxx program, which starts out by importing a frozen
-    # VFSImporter.  Let's make sure we don't have two VFSImporter
-    # modules.
-    import VFSImporter
-    import direct.showbase
-    direct.showbase.VFSImporter = VFSImporter
-    sys.modules['direct.showbase.VFSImporter'] = VFSImporter
-else:
-    # Otherwise, we can import the VFSImporter normally.
-    from direct.showbase import VFSImporter
+import __builtin__ as builtins
 
+from direct.showbase import VFSImporter
 from direct.showbase.DirectObject import DirectObject
 from panda3d.core import VirtualFileSystem, Filename, Multifile, loadPrcFileData, unloadPrcFile, getModelPath, WindowProperties, ExecutionEnvironment, PandaSystem, Notify, StreamWriter, ConfigVariableString, ConfigPageManager
 from panda3d.direct import init_app_for_gui
@@ -472,7 +459,7 @@ class AppRunner(DirectObject):
             file.unlink()
             return False
 
-        if not fileSpec.fullVerify(pathname = localPathname):
+        if not fileSpec.fullVerify(pathname = localPathname, notify = self.notify):
             # No good after download.
             self.notify.info("%s is still no good after downloading." % (url))
             return False
@@ -570,14 +557,14 @@ class AppRunner(DirectObject):
             for packageData in hostData.packages:
                 totalSize += packageData.totalSize
         self.notify.info("Total Panda3D disk space used: %s MB" % (
-            (totalSize + 524288) / 1048576))
+            (totalSize + 524288) // 1048576))
 
         if self.verifyContents == self.P3DVCNever:
             # We're not allowed to delete anything anyway.
             return
 
         self.notify.info("Configured max usage is: %s MB" % (
-            (self.maxDiskUsage + 524288) / 1048576))
+            (self.maxDiskUsage + 524288) // 1048576))
         if totalSize <= self.maxDiskUsage:
             # Still within budget; no need to clean up anything.
             return
@@ -635,13 +622,13 @@ class AppRunner(DirectObject):
         try:
             taskMgr.run()
 
-        except SystemExit:
+        except SystemExit as err:
             # Presumably the window has already been shut down here, but shut
             # it down again for good measure.
-            if hasattr(__builtin__, "base"):
+            if hasattr(builtins, "base"):
                 base.destroy()
 
-            self.notify.info("Normal exit.")
+            self.notify.info("Normal exit with status %s." % repr(err.code))
             raise
 
         except:
@@ -697,9 +684,10 @@ class AppRunner(DirectObject):
             # Replace the builtin open and file symbols so user code will get
             # our versions by default, which can open and read files out of
             # the multifile.
-            __builtin__.file = file.file
-            __builtin__.open = file.open
-            __builtin__.execfile = file.execfile
+            builtins.open = file.open
+            if sys.version_info < (3, 0):
+                builtins.file = file.open
+                builtins.execfile = file.execfile
             os.listdir = file.listdir
             os.walk = file.walk
             os.path.join = file.join

+ 136 - 61
direct/src/p3d/DeploymentTools.py

@@ -5,7 +5,7 @@ to build for as many platforms as possible. """
 __all__ = ["Standalone", "Installer"]
 
 import os, sys, subprocess, tarfile, shutil, time, zipfile, socket, getpass, struct
-from cStringIO import StringIO
+from io import BytesIO, TextIOWrapper
 from direct.directnotify.DirectNotifyGlobal import *
 from direct.showbase.AppRunnerGlobal import appRunner
 from panda3d.core import PandaSystem, HTTPClient, Filename, VirtualFileSystem, Multifile
@@ -21,8 +21,8 @@ try:
 except ImportError:
     pwd = None
 
-# Make sure this matches with the magic in p3dEmbed.cxx.
-P3DEMBED_MAGIC = "\xFF\x3D\x3D\x00"
+# Make sure this matches with the magic in p3dEmbedMain.cxx.
+P3DEMBED_MAGIC = 0xFF3D3D00
 
 # This filter function is used when creating
 # an archive that should be owned by root.
@@ -73,7 +73,7 @@ class Standalone:
 
         self.tempDir = Filename.temporary("", self.basename, "") + "/"
         self.tempDir.makeDir()
-        self.host = HostInfo(PandaSystem.getPackageHostUrl(), appRunner = appRunner, hostDir = self.tempDir, asMirror = False)
+        self.host = HostInfo(PandaSystem.getPackageHostUrl(), appRunner = appRunner, hostDir = self.tempDir, asMirror = False, perPlatform = True)
 
         self.http = HTTPClient.getGlobalPtr()
         if not self.host.hasContentsFile:
@@ -99,6 +99,9 @@ class Standalone:
         if len(platforms) == 0:
             Standalone.notify.warning("No platforms found to build for!")
 
+        if 'win32' in platforms and 'win_i386' in platforms:
+            platforms.remove('win32')
+
         outputDir = Filename(outputDir + "/")
         outputDir.makeDir()
         for platform in platforms:
@@ -158,10 +161,10 @@ class Standalone:
 
         # Find the magic size string and replace it with the real size,
         # regardless of the endianness of the p3dembed executable.
-        hex_size = hex(size)[2:].rjust(8, "0")
-        enc_size = "".join([chr(int(hex_size[i] + hex_size[i + 1], 16)) for i in range(0, len(hex_size), 2)])
-        p3dembed_data = p3dembed_data.replace(P3DEMBED_MAGIC, enc_size)
-        p3dembed_data = p3dembed_data.replace(P3DEMBED_MAGIC[::-1], enc_size[::-1])
+        p3dembed_data = p3dembed_data.replace(struct.pack('>I', P3DEMBED_MAGIC),
+                                              struct.pack('>I', size))
+        p3dembed_data = p3dembed_data.replace(struct.pack('<I', P3DEMBED_MAGIC),
+                                              struct.pack('<I', size))
 
         # Write the output file
         Standalone.notify.info("Creating %s..." % output)
@@ -170,12 +173,15 @@ class Standalone:
         ohandle.write(p3dembed_data)
 
         # Write out the tokens. Set log_basename to the basename by default
-        tokens = {"log_basename" : self.basename}
+        tokens = {"log_basename": self.basename}
         tokens.update(self.tokens)
         tokens.update(extraTokens)
-        for token in tokens.items():
-            ohandle.write("\0%s=%s" % token)
-        ohandle.write("\0\0")
+        for key, value in tokens.items():
+            ohandle.write(b"\0")
+            ohandle.write(key.encode('ascii'))
+            ohandle.write(b"=")
+            ohandle.write(value.encode())
+        ohandle.write(b"\0\0")
 
         # Buffer the p3d file to the output file. 1 MB buffer size.
         phandle = open(self.p3dfile.toOsSpecific(), "rb")
@@ -233,7 +239,7 @@ class PackageTree:
         if hostUrl in self.hosts:
             return self.hosts[hostUrl]
 
-        host = HostInfo(hostUrl, appRunner = appRunner, hostDir = self.hostDir, asMirror = False)
+        host = HostInfo(hostUrl, appRunner = appRunner, hostDir = self.hostDir, asMirror = False, perPlatform = True)
         if not host.hasContentsFile:
             if not host.readContentsFile():
                 if not host.downloadContentsFile(self.http):
@@ -391,7 +397,7 @@ class Icon:
         vfs = VirtualFileSystem.getGlobalPtr()
         stream = vfs.openWriteFile(fn, False, True)
         icns = open(stream, 'wb')
-        icns.write('icns\0\0\0\0')
+        icns.write(b'icns\0\0\0\0')
 
         icon_types = {16: 'is32', 32: 'il32', 48: 'ih32', 128: 'it32'}
         mask_types = {16: 's8mk', 32: 'l8mk', 48: 'h8mk', 128: 't8mk'}
@@ -404,7 +410,7 @@ class Icon:
                 if pngtype is None:
                     continue
                 icns.write(png_types[size])
-                icns.write('\0\0\0\0')
+                icns.write(b'\0\0\0\0')
                 start = icns.tell()
 
                 image.write(stream, "", pngtype)
@@ -444,6 +450,7 @@ class Installer:
     notify = directNotify.newCategory("Installer")
 
     def __init__(self, p3dfile, shortname, fullname, version, tokens = {}):
+        self.p3dFilename = p3dfile
         if not shortname:
             shortname = p3dfile.getBasenameWoExtension()
         self.shortname = shortname
@@ -477,22 +484,16 @@ class Installer:
         if not self.authoremail and ' ' not in uname:
             self.authoremail = "%s@%s" % (uname, socket.gethostname())
 
-        self.standalone = Standalone(p3dfile, tokens)
-        self.tempDir = Filename.temporary("", self.shortname, "") + "/"
-        self.tempDir.makeDir()
-        self.__tempRoots = {}
-
         # Load the p3d file to read out the required packages
         mf = Multifile()
-        if not mf.openRead(p3dfile):
+        if not mf.openRead(self.p3dFilename):
             Installer.notify.error("Not a Panda3D application: %s" % (p3dfile))
             return
 
         # Now load the p3dInfo file.
-        self.hostUrl = PandaSystem.getPackageHostUrl()
-        if not self.hostUrl:
-            self.hostUrl = self.standalone.host.hostUrl
+        self.hostUrl = None
         self.requires = []
+        self.extracts = []
         i = mf.findSubfile('p3d_info.xml')
         if i >= 0:
             stream = mf.openReadSubfile(i)
@@ -511,14 +512,54 @@ class Installer:
                         p3dRequires.Attribute('host')))
                     p3dRequires = p3dRequires.NextSiblingElement('requires')
 
+                p3dExtract = p3dPackage.FirstChildElement('extract')
+                while p3dExtract:
+                    filename = p3dExtract.Attribute('filename')
+                    self.extracts.append(filename)
+                    p3dExtract = p3dExtract.NextSiblingElement('extract')
+
                 if not self.fullname:
                     p3dConfig = p3dPackage.FirstChildElement('config')
                     if p3dConfig:
                         self.fullname = p3dConfig.Attribute('display_name')
+        else:
+            Installer.notify.warning("No p3d_info.xml was found in .p3d archive.")
+
+        mf.close()
+
+        if not self.hostUrl:
+            self.hostUrl = PandaSystem.getPackageHostUrl()
+            if not self.hostUrl:
+                self.hostUrl = self.standalone.host.hostUrl
+            Installer.notify.warning("No host URL was specified by .p3d archive.  Falling back to %s" % (self.hostUrl))
 
         if not self.fullname:
             self.fullname = self.shortname
 
+        self.tempDir = Filename.temporary("", self.shortname, "") + "/"
+        self.tempDir.makeDir()
+        self.__tempRoots = {}
+
+        if self.extracts:
+            # Copy .p3d to a temporary file so we can remove the extracts.
+            p3dfile = Filename(self.tempDir, self.p3dFilename.getBasename())
+            shutil.copyfile(self.p3dFilename.toOsSpecific(), p3dfile.toOsSpecific())
+            mf = Multifile()
+            if not mf.openReadWrite(p3dfile):
+                Installer.notify.error("Failure to open %s for writing." % (p3dfile))
+
+            # We don't really need this silly thing when embedding, anyway.
+            mf.setHeaderPrefix("")
+
+            for fn in self.extracts:
+                if not mf.removeSubfile(fn):
+                    Installer.notify.error("Failure to remove %s from multifile." % (p3dfile))
+
+            mf.repack()
+            mf.close()
+
+        self.standalone = Standalone(p3dfile, tokens)
+
     def __del__(self):
         try:
             appRunner.rmtree(self.tempDir)
@@ -533,6 +574,22 @@ class Installer:
         if not self.includeRequires:
             return
 
+        # Write out the extracts from the original .p3d.
+        if self.extracts:
+            mf = Multifile()
+            if not mf.openRead(self.p3dFilename):
+                Installer.notify.error("Failed to open .p3d archive: %s" % (filename))
+
+            for filename in self.extracts:
+                i = mf.findSubfile(filename)
+                if i < 0:
+                    Installer.notify.error("Cannot find extract in .p3d archive: %s" % (filename))
+                    continue
+
+                if not mf.extractSubfile(i, Filename(hostDir, filename)):
+                    Installer.notify.error("Failed to extract file from .p3d archive: %s" % (filename))
+            mf.close()
+
         pkgTree = PackageTree(platform, hostDir, self.hostUrl)
         pkgTree.installPackage("images", None, self.standalone.host.hostUrl)
 
@@ -618,6 +675,9 @@ class Installer:
         if len(platforms) == 0:
             Installer.notify.warning("No platforms found to build for!")
 
+        if 'win32' in platforms and 'win_i386' in platforms:
+            platforms.remove('win32')
+
         outputDir = Filename(outputDir + "/")
         outputDir.makeDir()
         for platform in platforms:
@@ -660,7 +720,8 @@ class Installer:
 
         Filename(tempdir, "usr/bin/").makeDir()
         if self.includeRequires:
-            extraTokens = {"host_dir" : "/usr/lib/" + self.shortname.lower()}
+            extraTokens = {"host_dir" : "/usr/lib/" + self.shortname.lower(),
+                           "start_dir" : "/usr/lib/" + self.shortname.lower()}
         else:
             extraTokens = {}
         self.standalone.build(Filename(tempdir, "usr/bin/" + self.shortname.lower()), platform, extraTokens)
@@ -712,7 +773,7 @@ class Installer:
         print >>desktop, "Type=Application"
         desktop.close()
 
-        if self.includeRequires:
+        if self.includeRequires or self.extracts:
             hostDir = Filename(tempdir, "usr/lib/%s/" % self.shortname.lower())
             hostDir.makeDir()
             self.installPackagesInto(hostDir, platform)
@@ -742,16 +803,18 @@ class Installer:
         tempdir, totsize = self.__buildTempLinux(platform)
 
         # Create a control file in memory.
-        controlfile = StringIO()
-        print >>controlfile, "Package: %s" % self.shortname.lower()
-        print >>controlfile, "Version: %s" % self.version
-        print >>controlfile, "Maintainer: %s <%s>" % (self.authorname, self.authoremail)
-        print >>controlfile, "Section: games"
-        print >>controlfile, "Priority: optional"
-        print >>controlfile, "Architecture: %s" % arch
-        print >>controlfile, "Installed-Size: %d" % -(-totsize / 1024)
-        print >>controlfile, "Description: %s" % self.fullname
-        print >>controlfile, "Depends: libc6, libgcc1, libstdc++6, libx11-6"
+        controlfile = BytesIO()
+        cout = TextIOWrapper(controlfile, encoding='utf-8', newline='')
+        cout.write(u"Package: %s\n" % self.shortname.lower())
+        cout.write(u"Version: %s\n" % self.version)
+        cout.write(u"Maintainer: %s <%s>\n" % (self.authorname, self.authoremail))
+        cout.write(u"Section: games\n")
+        cout.write(u"Priority: optional\n")
+        cout.write(u"Architecture: %s\n" % arch)
+        cout.write(u"Installed-Size: %d\n" % -(-totsize // 1024))
+        cout.write(u"Description: %s\n" % self.fullname)
+        cout.write(u"Depends: libc6, libgcc1, libstdc++6, libx11-6\n")
+        cout.flush()
         controlinfo = TarInfoRoot("control")
         controlinfo.mtime = modtime
         controlinfo.size = controlfile.tell()
@@ -762,33 +825,43 @@ class Installer:
         if output.exists():
             output.unlink()
         debfile = open(output.toOsSpecific(), "wb")
-        debfile.write("!<arch>\x0A")
-        debfile.write("debian-binary   %-12lu0     0     100644  %-10ld\x60\x0A" % (modtime, 4))
-        debfile.write("2.0\x0A")
-
-        # Write the control.tar.gz to the archive.
-        debfile.write("control.tar.gz  %-12lu0     0     100644  %-10ld\x60\x0A" % (modtime, 0))
+        debfile.write(b"!<arch>\x0A")
+        pad_mtime = str(modtime).encode().ljust(12, b' ')
+
+        # The first entry is a special file that marks it a .deb.
+        debfile.write(b"debian-binary   ")
+        debfile.write(pad_mtime)
+        debfile.write(b"0     0     100644  4         \x60\x0A")
+        debfile.write(b"2.0\x0A")
+
+        # Write the control.tar.gz to the archive.  We'll leave the
+        # size 0 for now, and go back and fill it in later.
+        debfile.write(b"control.tar.gz  ")
+        debfile.write(pad_mtime)
+        debfile.write(b"0     0     100644  0         \x60\x0A")
         ctaroffs = debfile.tell()
         ctarfile = tarfile.open("control.tar.gz", "w:gz", debfile, tarinfo = TarInfoRoot)
         ctarfile.addfile(controlinfo, controlfile)
         ctarfile.close()
         ctarsize = debfile.tell() - ctaroffs
-        if (ctarsize & 1): debfile.write("\x0A")
+        if (ctarsize & 1): debfile.write(b"\x0A")
 
-        # Write the data.tar.gz to the archive.
-        debfile.write("data.tar.gz     %-12lu0     0     100644  %-10ld\x60\x0A" % (modtime, 0))
+        # Write the data.tar.gz to the archive.  Again, leave size 0.
+        debfile.write(b"data.tar.gz     ")
+        debfile.write(pad_mtime)
+        debfile.write(b"0     0     100644  0         \x60\x0A")
         dtaroffs = debfile.tell()
         dtarfile = tarfile.open("data.tar.gz", "w:gz", debfile, tarinfo = TarInfoRoot)
         dtarfile.add(Filename(tempdir, "usr").toOsSpecific(), "/usr")
         dtarfile.close()
         dtarsize = debfile.tell() - dtaroffs
-        if (dtarsize & 1): debfile.write("\x0A")
+        if (dtarsize & 1): debfile.write(b"\x0A")
 
         # Write the correct sizes of the archives.
         debfile.seek(ctaroffs - 12)
-        debfile.write("%-10ld" % ctarsize)
+        debfile.write(str(ctarsize).encode().ljust(10, b' '))
         debfile.seek(dtaroffs - 12)
-        debfile.write("%-10ld" % dtarsize)
+        debfile.write(str(dtarsize).encode().ljust(10, b' '))
 
         debfile.close()
 
@@ -815,18 +888,20 @@ class Installer:
         tempdir, totsize = self.__buildTempLinux(platform)
 
         # Create a pkginfo file in memory.
-        pkginfo = StringIO()
-        print >>pkginfo, "# Generated using pdeploy"
-        print >>pkginfo, "# %s" % time.ctime(modtime)
-        print >>pkginfo, "pkgname = %s" % self.shortname.lower()
-        print >>pkginfo, "pkgver = %s" % pkgver
-        print >>pkginfo, "pkgdesc = %s" % self.fullname
-        print >>pkginfo, "builddate = %s" % modtime
-        print >>pkginfo, "packager = %s <%s>" % (self.authorname, self.authoremail)
-        print >>pkginfo, "size = %d" % totsize
-        print >>pkginfo, "arch = %s" % arch
+        pkginfo = BytesIO()
+        pout = TextIOWrapper(pkginfo, encoding='utf-8', newline='')
+        pout.write(u"# Generated using pdeploy\n")
+        pout.write(u"# %s\n" % time.ctime(modtime))
+        pout.write(u"pkgname = %s\n" % self.shortname.lower())
+        pout.write(u"pkgver = %s\n" % pkgver)
+        pout.write(u"pkgdesc = %s\n" % self.fullname)
+        pout.write(u"builddate = %s\n" % modtime)
+        pout.write(u"packager = %s <%s>\n" % (self.authorname, self.authoremail))
+        pout.write(u"size = %d\n" % totsize)
+        pout.write(u"arch = %s\n" % arch)
         if self.licensename != "":
-            print >>pkginfo, "license = %s" % self.licensename
+            pout.write(u"license = %s\n" % self.licensename)
+        pout.flush()
         pkginfoinfo = TarInfoRoot(".PKGINFO")
         pkginfoinfo.mtime = modtime
         pkginfoinfo.size = pkginfo.tell()
@@ -853,7 +928,7 @@ class Installer:
         exefile = Filename(output, "Contents/MacOS/" + self.shortname)
         exefile.makeDir()
         if self.includeRequires:
-            extraTokens = {"host_dir" : "../Resources"}
+            extraTokens = {"host_dir": "../Resources", "start_dir": "../Resources"}
         else:
             extraTokens = {}
         self.standalone.build(exefile, platform, extraTokens)
@@ -1064,7 +1139,7 @@ class Installer:
         exefile = Filename(Filename.getTempDirectory(), self.shortname + ".exe")
         exefile.unlink()
         if self.includeRequires:
-            extraTokens = {"host_dir" : "."}
+            extraTokens = {"host_dir": ".", "start_dir": "."}
         else:
             extraTokens = {}
         self.standalone.build(exefile, platform, extraTokens)

+ 17 - 21
direct/src/p3d/HostInfo.py

@@ -161,14 +161,14 @@ class HostInfo:
                 self.notify.info("Downloading contents file %s" % (request))
                 statusCode = None
                 statusString = ''
-                for attempt in range(ConfigVariableInt('contents-xml-dl-attempts', 3)):
+                for attempt in range(int(ConfigVariableInt('contents-xml-dl-attempts', 3))):
                     if attempt > 0:
                         self.notify.info("Retrying (%s)..."%(attempt,))
                     rf = Ramfile()
                     channel = http.makeChannel(False)
                     channel.getDocument(request)
                     if channel.downloadToRam(rf):
-                        self.notify.warning("Successfully downloaded %s" % (url,))
+                        self.notify.info("Successfully downloaded %s" % (url,))
                         break
                     else:
                         rf = None
@@ -369,7 +369,7 @@ class HostInfo:
             assert self.hostDir
             self.__findHostXmlForHostDir(xcontents)
 
-        if not self.hostDir:
+        if self.rootDir and not self.hostDir:
             self.hostDir = self.__determineHostDir(None, self.hostUrl)
 
         # Get the list of packages available for download and/or import.
@@ -403,7 +403,7 @@ class HostInfo:
         self.hasContentsFile = True
 
         # Now save the contents.xml file into the standard location.
-        if not self.appRunner or self.appRunner.verifyContents != self.appRunner.P3DVCNever:
+        if self.appRunner and self.appRunner.verifyContents != self.appRunner.P3DVCNever:
             assert self.hostDir
             filename = Filename(self.hostDir, 'contents.xml')
             filename.makeDir()
@@ -476,7 +476,7 @@ class HostInfo:
             self.descriptiveName = descriptiveName
 
         hostDirBasename = xhost.Attribute('host_dir')
-        if not self.hostDir:
+        if self.rootDir and not self.hostDir:
             self.hostDir = self.__determineHostDir(hostDirBasename, self.hostUrl)
 
         # Get the "download" URL, which is the source from which we
@@ -513,17 +513,15 @@ class HostInfo:
         PackageInfo, returns it. """
 
         if not platform:
-            # Ensure that we're on the same page with non-specified
-            # platforms.  We always use None, not empty string.
             platform = None
 
-        platforms = self.packages.setdefault((name, version), {})
-        package = platforms.get(platform, None)
+        platforms = self.packages.setdefault((name, version or ""), {})
+        package = platforms.get("", None)
         if not package:
             package = PackageInfo(self, name, version, platform = platform,
                                   solo = solo, asMirror = self.asMirror,
                                   perPlatform = perPlatform)
-            platforms[platform] = package
+            platforms[platform or ""] = package
 
         return package
 
@@ -533,12 +531,12 @@ class HostInfo:
         platform, if one is provided by this host, or None if not. """
 
         assert self.hasContentsFile
-        platforms = self.packages.get((name, version or None), {})
+        platforms = self.packages.get((name, version or ""), {})
 
-        if platform is not None:
+        if platform:
             # In this case, we are looking for a specific platform
             # only.
-            return platforms.get(platform or None, None)
+            return platforms.get(platform, None)
 
         # We are looking for one matching the current runtime
         # platform.  First, look for a package matching the current
@@ -547,7 +545,7 @@ class HostInfo:
 
         # If not found, look for one matching no particular platform.
         if not package:
-            package = platforms.get(None, None)
+            package = platforms.get("", None)
 
         return package
 
@@ -563,7 +561,7 @@ class HostInfo:
             if name and pn != name:
                 continue
 
-            if platform is None:
+            if not platform:
                 for p2 in platforms:
                     package = self.getPackage(pn, version, platform = p2)
                     if package:
@@ -581,14 +579,12 @@ class HostInfo:
 
         result = []
 
-        items = self.packages.items()
-        items.sort()
+        items = sorted(self.packages.items())
         for key, platforms in items:
             if self.perPlatform or includeAllPlatforms:
                 # If we maintain a different answer per platform,
                 # return all of them.
-                pitems = platforms.items()
-                pitems.sort()
+                pitems = sorted(platforms.items())
                 for pkey, package in pitems:
                     result.append(package)
             else:
@@ -597,7 +593,7 @@ class HostInfo:
                 # current platform, or no particular platform.
                 package = platforms.get(PandaSystem.getPlatform(), None)
                 if not package:
-                    package = platforms.get(None, None)
+                    package = platforms.get("", None)
 
                 if package:
                     result.append(package)
@@ -701,7 +697,7 @@ class HostInfo:
 
             # If we successfully got a hostname, we don't really need the
             # full hash.  We'll keep half of it.
-            keepHash = keepHash / 2;
+            keepHash = keepHash // 2
 
         md = HashVal()
         md.hashString(hostUrl)

+ 4 - 3
direct/src/p3d/PackageInfo.py

@@ -277,7 +277,7 @@ class PackageInfo:
             # We've already got one.
             yield self.stepComplete; return
 
-        if self.host.appRunner and self.host.appRunner.verifyContents != self.host.appRunner.P3DVCNever:
+        if not self.host.appRunner or self.host.appRunner.verifyContents != self.host.appRunner.P3DVCNever:
             # We're allowed to download it.
             self.http = http
 
@@ -523,7 +523,8 @@ class PackageInfo:
 
         # In case of unexpected failures on the internet, we will retry
         # the full download instead of just giving up.
-        for retry in range(core.ConfigVariableInt('package-full-dl-retries', 1)):
+        retries = core.ConfigVariableInt('package-full-dl-retries', 1).getValue()
+        for retry in range(retries):
             self.installPlans.append(planB[:])
 
         pc.stop()
@@ -1151,7 +1152,7 @@ class PackageInfo:
         thisDir = ScanDirectoryNode(self.getPackageDir(), ignoreUsageXml = True)
         diskSpace = thisDir.getTotalSize()
         self.notify.info("Package %s uses %s MB" % (
-            self.packageName, (diskSpace + 524288) / 1048576))
+            self.packageName, (diskSpace + 524288) // 1048576))
         return diskSpace
 
     def markUsed(self):

+ 148 - 63
direct/src/p3d/Packager.py

@@ -16,6 +16,7 @@ import types
 import getpass
 import struct
 import subprocess
+import copy
 from direct.p3d.FileSpec import FileSpec
 from direct.p3d.SeqValue import SeqValue
 from direct.showbase import Loader
@@ -185,7 +186,7 @@ class Packager:
         def getKey(self):
             """ Returns a tuple used for sorting the PackageEntry
             objects uniquely per package. """
-            return (self.packageName, self.platform, self.version)
+            return (self.packageName, self.platform or "", self.version or "")
 
         def fromFile(self, packageName, platform, version, solo, perPlatform,
                      installDir, descFilename, importDescFilename):
@@ -299,8 +300,7 @@ class Packager:
                 xhost.InsertEndChild(xmirror)
 
             if packager:
-                altHosts = self.altHosts.items()
-                altHosts.sort()
+                altHosts = sorted(self.altHosts.items())
                 for keyword, alt in altHosts:
                     he = packager.hosts.get(alt, None)
                     if he:
@@ -372,9 +372,15 @@ class Packager:
             self.requiredFilenames = []
             self.requiredModules = []
 
+            # A list of required packages that were missing.
+            self.missingPackages = []
+
             # This records the current list of modules we have added so
             # far.
             self.freezer = FreezeTool.Freezer(platform = self.packager.platform)
+            
+            # Map of extensions to files to number (ignored by dir)
+            self.ignoredDirFiles = {}
 
         def close(self):
             """ Writes out the contents of the current package.  Returns True
@@ -385,6 +391,12 @@ class Packager:
                 message = 'Cannot generate packages without an installDir; use -i'
                 raise PackagerError, message
 
+            if self.ignoredDirFiles:
+                exts = sorted(self.ignoredDirFiles.keys())
+                total = sum([x for x in self.ignoredDirFiles.values()])
+                self.notify.warning("excluded %s files not marked for inclusion: %s" \
+                                    % (total, ", ".join(["'" + ext + "'" for ext in exts])))
+                
             if not self.host:
                 self.host = self.packager.host
 
@@ -454,7 +466,7 @@ class Packager:
             if self.p3dApplication:
                 allowPythonDev = self.configs.get('allow_python_dev', 0)
                 if int(allowPythonDev):
-                    print "\n*** Generating %s.p3d with allow_python_dev enabled ***\n" % (self.packageName)
+                    print("\n*** Generating %s.p3d with allow_python_dev enabled ***\n" % (self.packageName))
 
             return result
 
@@ -485,6 +497,12 @@ class Packager:
             as a true package.  Either is implemented with a
             Multifile. """
 
+            if self.missingPackages:
+                missing = ', '.join([name for name, version in self.missingPackages])
+                self.notify.warning("Cannot build package %s due to missing dependencies: %s" % (self.packageName, missing))
+                self.cleanup()
+                return False
+
             self.multifile = Multifile()
 
             # Write the multifile to a temporary filename until we
@@ -570,8 +588,7 @@ class Packager:
 
             # Add known module names.
             self.moduleNames = {}
-            modules = self.freezer.modules.items()
-            modules.sort()
+            modules = sorted(self.freezer.modules.items())
             for newName, mdef in modules:
                 if mdef.guess:
                     # Not really a module.
@@ -702,7 +719,7 @@ class Packager:
             self.packageDesc = packageDir + self.packageDesc
             self.packageImportDesc = packageDir + self.packageImportDesc
 
-            print "Generating %s" % (self.packageFilename)
+            print("Generating %s" % (self.packageFilename))
 
             if self.p3dApplication:
                 self.packageFullpath = Filename(self.packager.p3dInstallDir, self.packageFilename)
@@ -820,6 +837,14 @@ class Packager:
             self.packager.contents[pe.getKey()] = pe
             self.packager.contentsChanged = True
 
+            # Hack for coreapi package, to preserve backward compatibility
+            # with old versions of the runtime, which still called the
+            # 32-bit Windows platform "win32".
+            if self.packageName == "coreapi" and self.platform == "win_i386":
+                pe2 = copy.copy(pe)
+                pe2.platform = "win32"
+                self.packager.contents[pe2.getKey()] = pe2
+
             self.cleanup()
             return True
 
@@ -1232,13 +1257,13 @@ class Packager:
                 elf.close()
                 return None
 
-            if not ident.startswith("\177ELF"):
+            if not ident.startswith(b"\177ELF"):
                 # No elf magic!  Beware of orcs.
                 return None
 
             # Make sure we read in the correct endianness and integer size
-            byteOrder = "<>"[ord(ident[5]) - 1]
-            elfClass = ord(ident[4]) - 1 # 0 = 32-bits, 1 = 64-bits
+            byteOrder = "<>"[ord(ident[5:6]) - 1]
+            elfClass = ord(ident[4:5]) - 1 # 0 = 32-bits, 1 = 64-bits
             headerStruct = byteOrder + ("HHIIIIIHHHHHH", "HHIQQQIHHHHHH")[elfClass]
             sectionStruct = byteOrder + ("4xI8xIII8xI", "4xI16xQQI12xQ")[elfClass]
             dynamicStruct = byteOrder + ("iI", "qQ")[elfClass]
@@ -1272,17 +1297,17 @@ class Packager:
                 elf.seek(offset)
                 data = elf.read(entsize)
                 tag, val = struct.unpack_from(dynamicStruct, data)
-                newSectionData = ""
+                newSectionData = b""
                 startReplace = None
                 pad = 0
 
                 # Read tags until we find a NULL tag.
                 while tag != 0:
                     if tag == 1: # A NEEDED entry.  Read it from the string table.
-                        filenames.append(stringTables[link][val : stringTables[link].find('\0', val)])
+                        filenames.append(stringTables[link][val : stringTables[link].find(b'\0', val)])
 
                     elif tag == 15 or tag == 29:
-                        rpath += stringTables[link][val : stringTables[link].find('\0', val)].split(':')
+                        rpath += stringTables[link][val : stringTables[link].find(b'\0', val)].split(b':')
                         # An RPATH or RUNPATH entry.
                         if not startReplace:
                             startReplace = elf.tell() - entsize
@@ -1296,7 +1321,7 @@ class Packager:
                     tag, val = struct.unpack_from(dynamicStruct, data)
 
                 if startReplace is not None:
-                    newSectionData += data + ("\0" * pad)
+                    newSectionData += data + (b"\0" * pad)
                     rewriteSections.append((startReplace, newSectionData))
             elf.close()
 
@@ -1329,7 +1354,7 @@ class Packager:
             for offset, data in rewriteSections:
                 elf.seek(offset)
                 elf.write(data)
-            elf.write("\0" * pad)
+            elf.write(b"\0" * pad)
             elf.close()
             return filenames
 
@@ -1468,6 +1493,10 @@ class Packager:
                     xhost = he.makeXml(packager = self.packager)
                     xpackage.InsertEndChild(xhost)
 
+            self.extracts.sort()
+            for name, xextract in self.extracts:
+                xpackage.InsertEndChild(xextract)
+
             doc.InsertEndChild(xpackage)
 
             # Write the xml file to a temporary file on disk, so we
@@ -1813,7 +1842,10 @@ class Packager:
                         self.notify.warning(message)
                     return
 
-            self.freezer.addModule(moduleName, filename = file.filename)
+            if file.text:
+                self.freezer.addModule(moduleName, filename = file.filename, text = file.text)
+            else:
+                self.freezer.addModule(moduleName, filename = file.filename)
 
         def addEggFile(self, file):
             # Precompile egg files to bam's.
@@ -2161,8 +2193,10 @@ class Packager:
                     ext = Filename(lowerName).getExtension()
                     if ext not in self.packager.nonuniqueExtensions:
                         self.skipFilenames[lowerName] = True
+
                 for moduleName, mdef in package.moduleNames.items():
-                    self.skipModules[moduleName] = mdef
+                    if not mdef.exclude:
+                        self.skipModules[moduleName] = mdef
 
     # Packager constructor
     def __init__(self, platform = None):
@@ -2185,6 +2219,9 @@ class Packager:
         # ignoring any request to specify a particular download host,
         # e.g. for testing and development.
         self.ignoreSetHost = False
+        
+        # Set this to true to verbosely log files ignored by dir().
+        self.verbosePrint = False
 
         # This will be appended to the basename of any .p3d package,
         # before the .p3d extension.
@@ -2333,7 +2370,7 @@ class Packager:
 
         # Binary files that are copied (and compressed) without
         # processing.
-        self.binaryExtensions = [ 'ttf', 'TTF', 'mid', 'ico' ]
+        self.binaryExtensions = [ 'ttf', 'TTF', 'mid', 'ico', 'cur' ]
 
         # Files that can have an existence in multiple different
         # packages simultaneously without conflict.
@@ -2373,7 +2410,7 @@ class Packager:
                 }
 
         # Files that should be extracted to disk.
-        self.extractExtensions = self.executableExtensions[:] + self.manifestExtensions[:] + [ 'ico' ]
+        self.extractExtensions = self.executableExtensions[:] + self.manifestExtensions[:] + [ 'ico', 'cur' ]
 
         # Files that indicate a platform dependency.
         self.platformSpecificExtensions = self.executableExtensions[:]
@@ -2390,6 +2427,14 @@ class Packager:
         # should be added exactly byte-for-byte as they are.
         self.unprocessedExtensions = []
 
+        # Files for which warnings should be suppressed when they are
+        # not handled by dir()
+        self.suppressWarningForExtensions = ['', 'pyc', 'pyo', 
+                                             'p3d', 'pdef', 
+                                             'c', 'C', 'cxx', 'cpp', 'h', 'H',
+                                             'hpp', 'pp', 'I', 'pem', 'p12', 'crt',
+                                             'o', 'obj', 'a', 'lib', 'bc', 'll']
+         
         # System files that should never be packaged.  For
         # case-insensitive filesystems (like Windows and OSX), put the
         # lowercase filename here.  Case-sensitive filesystems should
@@ -2456,7 +2501,7 @@ class Packager:
         if not os.path.isfile('/sbin/ldconfig'):
             return False
 
-        handle = subprocess.Popen(['/sbin/ldconfig', '-p'], stdout=subprocess.PIPE)
+        handle = subprocess.Popen(['/sbin/ldconfig', '-p'], stdout=subprocess.PIPE, universal_newlines=True)
         out, err = handle.communicate()
 
         if handle.returncode != 0:
@@ -2617,12 +2662,20 @@ class Packager:
             if dirname.makeTrueCase():
                 searchPath.appendDirectory(dirname)
 
+    def _ensureExtensions(self):
+        self.knownExtensions = \
+            self.imageExtensions + \
+            self.modelExtensions + \
+            self.textExtensions + \
+            self.binaryExtensions + \
+            self.uncompressibleExtensions + \
+            self.unprocessedExtensions
 
     def setup(self):
         """ Call this method to initialize the class after filling in
         some of the values in the constructor. """
 
-        self.knownExtensions = self.imageExtensions + self.modelExtensions + self.textExtensions + self.binaryExtensions + self.uncompressibleExtensions + self.unprocessedExtensions
+        self._ensureExtensions()
 
         self.currentPackage = None
 
@@ -2710,11 +2763,15 @@ class Packager:
         # errors, and that the pdef file doesn't contain any really
         # crazy Python code, all this will do is fill in the
         # '__statements' list in the module scope.
+        fn = packageDef.toOsSpecific()
+        f = open(fn)
+        code = compile(f.read(), fn, 'exec')
+        f.close()
 
         # It appears that having a separate globals and locals
         # dictionary causes problems with resolving symbols within a
         # class scope.  So, we just use one dictionary, the globals.
-        execfile(packageDef.toOsSpecific(), globals)
+        exec(code, globals)
 
         packages = []
 
@@ -2979,10 +3036,10 @@ class Packager:
             # environment.
             return None
 
+        # Make sure we have a fresh version of the contents file.
         host = appRunner.getHost(hostUrl)
-        if not host.readContentsFile():
-            if not host.downloadContentsFile(appRunner.http):
-                return None
+        if not host.downloadContentsFile(appRunner.http):
+            return None
 
         packageInfos = []
         packageInfo = host.getPackage(packageName, version, platform = platform)
@@ -3031,7 +3088,7 @@ class Packager:
         tuples = []
         for package in packages:
             version = self.__makeVersionTuple(package.version)
-            tuples.append((version, file))
+            tuples.append((version, package))
         tuples.sort(reverse = True)
 
         return [t[1] for t in tuples]
@@ -3044,7 +3101,7 @@ class Packager:
         tuples = []
         for package in packages:
             version = self.__makeVersionTuple(package.packageVersion)
-            tuples.append((version, file))
+            tuples.append((version, package))
         tuples.sort(reverse = True)
 
         return [t[1] for t in tuples]
@@ -3103,9 +3160,9 @@ class Packager:
         if panda1.version == panda2.version:
             return True
 
-        print 'Rejecting package %s, version "%s": depends on %s, version "%s" instead of version "%s"' % (
+        print('Rejecting package %s, version "%s": depends on %s, version "%s" instead of version "%s"' % (
             package.packageName, package.version,
-            panda1.packageName, panda1.version, panda2.version)
+            panda1.packageName, panda1.version, panda2.version))
         return False
 
     def __findPackageInRequires(self, packageName, list):
@@ -3188,7 +3245,9 @@ class Packager:
                                        requires = self.currentPackage.requires)
             if not package:
                 message = 'Unknown package %s, version "%s"' % (packageName, version)
-                raise PackagerError, message
+                self.notify.warning(message)
+                self.currentPackage.missingPackages.append((packageName, pversion))
+                continue
 
             self.requirePackage(package)
 
@@ -3293,42 +3352,27 @@ class Packager:
         producing their own custom panda3d for download.  Should be
         called before any other Python modules are named. """
 
-        # First, freeze just VFSImporter.py into its own
-        # _vfsimporter.pyd file.  This one is a special case, because
-        # we need this code in order to load python files from the
-        # Multifile, so this file can't itself be in the Multifile.
+        # This module and all its dependencies come frozen into p3dpython.
+        # We should mark them as having already been added so that we don't
+        # add them again to the Multifile.
+        self.do_module('direct.showbase.VFSImporter')
+        self.currentPackage.freezer.done(addStartupModules=True)
+        self.currentPackage.freezer.writeCode(None)
+        self.currentPackage.addExtensionModules()
+        self.currentPackage.freezer.reset()
 
-        # This requires a bit of care, because we only want to freeze
-        # VFSImporter.py, and not any other part of direct.  We do
-        # also want panda3d/__init__.py, though, since it would
-        # otherwise be part of the multifile.
-        self.do_excludeModule('direct')
-
-        # Import the actual VFSImporter module to get its filename on
-        # disk.
-        from direct.showbase import VFSImporter
-        filename = Filename.fromOsSpecific(VFSImporter.__file__)
-
-        self.do_module('VFSImporter', filename = filename)
-        self.do_freeze('_vfsimporter', compileToExe = False)
-
-        self.do_file('panda3d/core.pyd');
-
-        # Now that we're done freezing, explicitly add 'direct' to
-        # counteract the previous explicit excludeModule().
-        self.do_module('direct')
+        self.do_file('panda3d/_core.pyd', newDir='panda3d')
 
         # This is the key Python module that is imported at runtime to
         # start an application running.
         self.do_module('direct.p3d.AppRunner')
 
         # This is the main program that drives the runtime Python.  It
-        # is responsible for loading _vfsimporter.pyd, and then
-        # importing direct.p3d.AppRunner, to start an application
-        # running.  The program comes in two parts: an executable, and
-        # an associated dynamic library.  Note that the .exe and .dll
-        # extensions are automatically replaced with the appropriate
-        # platform-specific extensions.
+        # is responsible for importing direct.p3d.AppRunner to start an
+        # application running.  The program comes in two parts: an
+        # executable, and an associated dynamic library.  Note that the
+        # .exe and .dll extensions are automatically replaced with the
+        # appropriate platform-specific extensions.
 
         if self.platform.startswith('osx'):
             # On Mac, we package up a P3DPython.app bundle.  This
@@ -3411,7 +3455,7 @@ class Packager:
                 freezer.addModule(moduleName, newName = newName)
             else:
                 freezer.modules[newName] = freezer.modules[moduleName]
-        freezer.done(compileToExe = compileToExe)
+        freezer.done(addStartupModules = compileToExe)
 
         dirname = ''
         basename = filename
@@ -3604,6 +3648,43 @@ class Packager:
         filename = Filename(filename)
         self.currentPackage.excludeFile(filename)
 
+
+    def do_includeExtensions(self, executableExtensions = None, extractExtensions = None, 
+                         imageExtensions = None, textExtensions = None, 
+                         uncompressibleExtensions = None, unprocessedExtensions = None,
+                         suppressWarningForExtensions = None):
+        """ Ensure that dir() will include files with the given extensions.
+        The extensions should not have '.' prefixes.
+        
+        All except 'suppressWarningForExtensions' allow the given kinds of files
+        to be packaged with their respective semantics (read the source).
+        
+        'suppressWarningForExtensions' lists extensions *expected* to be ignored, 
+        so no warnings will be emitted for them.
+        """
+        if executableExtensions:
+            self.executableExtensions += executableExtensions
+
+        if extractExtensions:
+            self.extractExtensions += extractExtensions
+
+        if imageExtensions:
+            self.imageExtensions += imageExtensions
+
+        if textExtensions:
+            self.textExtensions += textExtensions
+
+        if uncompressibleExtensions:
+            self.uncompressibleExtensions += uncompressibleExtensions
+
+        if unprocessedExtensions:
+            self.unprocessedExtensions += unprocessedExtensions
+
+        if suppressWarningForExtensions:
+            self.suppressWarningForExtensions += suppressWarningForExtensions
+
+        self._ensureExtensions()
+
     def do_dir(self, dirname, newDir = None, unprocessed = None):
 
         """ Adds the indicated directory hierarchy to the current
@@ -3676,7 +3757,12 @@ class Packager:
                     filename.setBinary()
                 self.currentPackage.addFile(filename, newName = newName,
                                             explicit = False, unprocessed = unprocessed)
-
+            elif not ext in self.suppressWarningForExtensions:
+                newCount = self.currentPackage.ignoredDirFiles.get(ext, 0) + 1
+                self.currentPackage.ignoredDirFiles[ext] = newCount
+                
+                if self.verbosePrint:
+                    self.notify.warning("ignoring file %s" % filename) 
 
     def readContentsFile(self):
         """ Reads the contents.xml file at the beginning of
@@ -3750,8 +3836,7 @@ class Packager:
                 xhost = he.makeXml(packager = self)
                 xcontents.InsertEndChild(xhost)
 
-        contents = self.contents.items()
-        contents.sort()
+        contents = sorted(self.contents.items())
         for key, pe in contents:
             xpackage = pe.makeXml()
             xcontents.InsertEndChild(xpackage)

+ 1 - 1
direct/src/p3d/coreapi.pdef

@@ -66,7 +66,7 @@ class images(package):
             token = '%s_img' % (name)
             configDict[token] = basename
         else:
-            print "Could not locate %s" % (filename)
+            print("Could not locate %s" % (filename))
 
     # Also make a few special cases.  We use the same default image
     # for many of the states.

+ 20 - 9
direct/src/p3d/packp3d.py

@@ -57,6 +57,14 @@ Options:
      instead of -e for files that are uncompressible by their nature
      (e.g. mpg files).  This option may be repeated as necessary.
 
+  -x ext
+     Marks files with the given extensions of needing to be physically
+     extracted to disk before they can be loaded.  This is used for
+     file types that cannot be loaded via the virtual file system,
+     such as .ico files on Windows.
+     This option is currently only implemented when deploying the
+     application with pdeploy.
+
   -p python_lib_dir
      Adds a directory to search for additional Python modules.  You
      can use this to add your system's Python path, to allow packp3d
@@ -103,7 +111,7 @@ class ArgumentError(StandardError):
     pass
 
 def makePackedApp(args):
-    opts, args = getopt.getopt(args, 'o:d:m:S:e:n:p:c:r:s:Dh')
+    opts, args = getopt.getopt(args, 'o:d:m:S:e:n:x:p:c:r:s:Dh')
 
     packager = Packager.Packager()
 
@@ -134,6 +142,8 @@ def makePackedApp(args):
             packager.binaryExtensions.append(value)
         elif option == '-n':
             packager.uncompressibleExtensions.append(value)
+        elif option == '-x':
+            packager.extractExtensions.append(value)
         elif option == '-p':
             sys.path.append(value)
         elif option == '-c':
@@ -154,10 +164,10 @@ def makePackedApp(args):
                 PandaSystem.getPackageHostUrl(),
                 os.path.split(sys.argv[0])[1],
                 '%s.%s' % (sys.version_info[0], sys.version_info[1]))
-            sys.exit(1)
+            sys.exit(0)
 
     if not appFilename:
-        raise ArgumentError, "No target app specified.  Use:\n%s -o app.p3d" % (os.path.split(sys.argv[0])[1])
+        raise ArgumentError, "No target app specified.  Use:\n  %s -o app.p3d\nUse -h to get more usage information." % (os.path.split(sys.argv[0])[1])
 
     if args:
         raise ArgumentError, "Extra arguments on command line."
@@ -170,19 +180,20 @@ def makePackedApp(args):
       appDir = Filename('.')
     appBase = appFilename.getBasenameWoExtension()
 
-    if not main:
+    if main:
+        main = Filename.fromOsSpecific(main)
+        main.makeAbsolute(root)
+    else:
         main = Filename(root, 'main.py')
-        if main.exists():
-            main = 'main.py'
-        else:
+        if not main.exists():
             main = glob.glob(os.path.join(root.toOsSpecific(), '*.py'))
             if len(main) == 0:
                 raise ArgumentError, 'No Python files in root directory.'
             elif len(main) > 1:
                 raise ArgumentError, 'Multiple Python files in root directory; specify the main application with -m "main".'
-            main = os.path.split(main[0])[1]
 
-    main = Filename.fromOsSpecific(main)
+            main = Filename.fromOsSpecific(os.path.split(main[0])[1])
+            main.makeAbsolute(root)
 
     packager.installDir = appDir
     packager.allowPythonDev = allowPythonDev

+ 12 - 4
direct/src/p3d/panda3d.pdef

@@ -61,7 +61,7 @@ class panda3d(package):
            'panda3d.physics')
 
     # Include various standard Python encodings.  The rest is in morepy.
-    module('encodings', 'encodings.aliases', 'encodings.undefined,'
+    module('encodings', 'encodings.aliases', 'encodings.undefined',
            'encodings.utf_8', 'encodings.ascii', 'encodings.string_escape',
            'encodings.mbcs', 'encodings.latin_1', 'io')
 
@@ -195,7 +195,7 @@ class morepy(package):
            'termios', 'tty', 'pty', 'fcntl', 'pipes', 'posixfile',
            'resource', 'nis', 'syslog', 'commands', 'ic', 'MacOS',
            'macostools', 'findertools', 'EasyDialogs', 'FrameWork',
-           'autoGIL', 'ColorPicker')
+           'autoGIL', 'ColorPicker', 'ast')
 
     # To add the multitude of standard Python string encodings.
     module('encodings', 'encodings.*')
@@ -362,8 +362,16 @@ class rocket(package):
     config(display_name = "Panda3D libRocket support")
     require('panda3d')
 
-    module('panda3d.rocket', required = True)
-    module('_rocketcore', '_rocketcontrols')
+    file('rocket.py', extract = True, text = """
+from _rocketcore import *
+try:
+    from _rocketcontrols import *
+except ImportError:
+    pass
+""")
+
+    module('panda3d.rocket', '_rocketcore', required = True)
+    module('_rocketcontrols')
     file('libp3rocket.dll', required = True)
 
 class vrpn(package):

+ 17 - 8
direct/src/p3d/ppackage.py

@@ -123,6 +123,10 @@ Options:
      as such.  This only applies to .p3d packages, not to other types
      of packages!
 
+  -v
+     Emit a warning for any file not recognized by the dir() command
+     (indicating there may be a need for addExtensions(...)).
+     
   -h
      Display this help
 """
@@ -135,12 +139,12 @@ from direct.p3d import Packager
 from panda3d.core import *
 
 def usage(code, msg = ''):
-    print >> sys.stderr, usageText % {
+    sys.stderr.write(usageText % {
         'version' : PandaSystem.getPackageVersionString(),
         'host' : PandaSystem.getPackageHostUrl(),
         'prog' : os.path.split(sys.argv[0])[1]
-        }
-    print >> sys.stderr, msg
+        })
+    sys.stderr.write(msg + '\n')
     sys.exit(code)
 
 installDir = None
@@ -151,12 +155,13 @@ allowPythonDev = False
 universalBinaries = False
 systemRoot = None
 ignoreSetHost = False
+verbosePrint = False
 p3dSuffix = ''
 platforms = []
 
 try:
-    opts, args = getopt.getopt(sys.argv[1:], 'i:ps:S:DuP:R:Ha:h')
-except getopt.error, msg:
+    opts, args = getopt.getopt(sys.argv[1:], 'i:ps:S:DuP:R:Ha:hv')
+except getopt.error as msg:
     usage(1, msg)
 
 for opt, arg in opts:
@@ -188,10 +193,13 @@ for opt, arg in opts:
     elif opt == '-a':
         p3dSuffix = arg
 
+    elif opt == '-v':
+        verbosePrint = True
+        
     elif opt == '-h':
         usage(0)
     else:
-        print 'illegal option: ' + arg
+        print('illegal option: ' + arg)
         sys.exit(1)
 
 if not args:
@@ -212,7 +220,7 @@ else:
 
 if universalBinaries:
     if platforms:
-        print '\nYou may not specify both -u and -P.\n'
+        print('\nYou may not specify both -u and -P.\n')
         sys.exit(1)
     if PandaSystem.getPlatform().startswith('osx_'):
         platforms = ['osx_i386', 'osx_amd64']
@@ -230,6 +238,7 @@ for platform in platforms:
     packager.allowPythonDev = allowPythonDev
     packager.systemRoot = systemRoot
     packager.ignoreSetHost = ignoreSetHost
+    packager.verbosePrint = verbosePrint
     packager.p3dSuffix = p3dSuffix
 
     try:
@@ -242,7 +251,7 @@ for platform in platforms:
     except Packager.PackagerError:
         # Just print the error message and exit gracefully.
         inst = sys.exc_info()[1]
-        print inst.args[0]
+        print(inst.args[0])
         sys.exit(1)
 
 # An explicit call to exit() is required to exit the program, when

+ 3 - 3
direct/src/p3d/thirdparty.pdef

@@ -137,7 +137,7 @@ class pyopengl(package):
     module('OpenGL.GLU', 'OpenGL.GLUT', 'OpenGL.GLE', 'OpenGL.GLX')
 
 class httplib2(package):
-    config(display_name = "httplib2")
+    config(display_name = "httplib2", platform_specific = False)
     require('panda3d')
 
     module('httplib2', required = True)
@@ -149,7 +149,7 @@ class box2d(package):
     module('Box2D', required = True)
 
 class pyglet(package):
-    config(display_name = "pyglet")
-    require('panda3d')
+    config(display_name = "pyglet", platform_specific = False)
+    require('panda3d', 'morepy')
 
     module('pyglet', required = True)

+ 2 - 2
direct/src/particles/ParticleTest.py

@@ -2,8 +2,8 @@
 if __name__ == "__main__":
     from direct.directbase.TestStart import *
     
-    from pandac.LinearVectorForce import LinearVectorForce
-    from pandac.Vec3 import Vec3
+    from panda3d.physics import LinearVectorForce
+    from panda3d.core import Vec3
     import ParticleEffect
     from direct.tkpanels import ParticlePanel
     import Particles

+ 17 - 6
direct/src/plugin/load_plugin.cxx

@@ -140,7 +140,7 @@ load_plugin(const string &p3d_plugin_filename,
             const string &log_directory, const string &log_basename,
             bool trusted_environment, bool console_environment,
             const string &root_dir, const string &host_dir,
-            ostream &logfile) {
+            const string &start_dir, ostream &logfile) {
   if (plugin_loaded) {
     return true;
   }
@@ -259,7 +259,7 @@ load_plugin(const string &p3d_plugin_filename,
                    verify_contents, platform,
                    log_directory, log_basename,
                    trusted_environment, console_environment,
-                   root_dir, host_dir, logfile)) {
+                   root_dir, host_dir, start_dir, logfile)) {
     unload_dso();
     return false;
   }
@@ -283,7 +283,7 @@ init_plugin(const string &contents_filename, const string &host_url,
             const string &log_directory, const string &log_basename,
             bool trusted_environment, bool console_environment,
             const string &root_dir, const string &host_dir,
-            ostream &logfile) {
+            const string &start_dir, ostream &logfile) {
 
   // Ensure that all of the function pointers have been found.
   if (P3D_initialize_ptr == NULL ||
@@ -371,15 +371,26 @@ init_plugin(const string &contents_filename, const string &host_url,
     return false;
   }
 
-  if (!P3D_initialize_ptr(P3D_API_VERSION, contents_filename.c_str(),
+  // A bit of extra hand-hacked compatibility for using newer plug-ins
+  // with an older version of the core API.
+  int api_version = P3D_API_VERSION;
+  if (api_version == 17 && start_dir.empty()) {
+    api_version = 16;
+    if (host_dir.empty()) {
+      api_version = 15;
+    }
+  }
+
+  if (!P3D_initialize_ptr(api_version, contents_filename.c_str(),
                           host_url.c_str(), verify_contents, platform.c_str(),
                           log_directory.c_str(), log_basename.c_str(),
                           trusted_environment, console_environment, 
-                          root_dir.c_str(), host_dir.c_str())) {
+                          root_dir.c_str(), host_dir.c_str(),
+                          start_dir.c_str())) {
     // Oops, failure to initialize.
     logfile
       << "Failed to initialize plugin (passed API version " 
-      << P3D_API_VERSION << ")\n";
+      << api_version << ")\n";
     return false;
   }
 

+ 4 - 2
direct/src/plugin/load_plugin.h

@@ -67,13 +67,15 @@ load_plugin(const string &p3d_plugin_filename,
             P3D_verify_contents verify_contents, const string &platform,
             const string &log_directory, const string &log_basename,
             bool trusted_environment, bool console_environment,
-            const string &root_dir, const string &host_dir, ostream &logfile);
+            const string &root_dir, const string &host_dir,
+            const string &start_dir, ostream &logfile);
 bool
 init_plugin(const string &contents_filename, const string &host_url, 
             P3D_verify_contents verify_contents, const string &platform,
             const string &log_directory, const string &log_basename,
             bool trusted_environment, bool console_environment,
-            const string &root_dir, const string &host_dir, ostream &logfile);
+            const string &root_dir, const string &host_dir,
+            const string &start_dir, ostream &logfile);
 
 void unload_plugin(ostream &logfile);
 bool is_plugin_loaded();

+ 4 - 3
direct/src/plugin/p3dAuthSession.cxx

@@ -163,7 +163,7 @@ start_p3dcert() {
 
   P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
 
-  _start_dir = inst_mgr->get_root_dir() + "/start";
+  _start_dir = inst_mgr->get_start_dir();
 
   string root_dir = _inst->_p3dcert_package->get_package_dir();
   mkdir_complete(_start_dir, nout);
@@ -183,7 +183,7 @@ start_p3dcert() {
   // These are the enviroment variables we forward from the current
   // environment, if they are set.
   const wchar_t *keep[] = {
-    L"TMP", L"TEMP", L"HOME", L"USER", 
+    L"TMP", L"TEMP", L"HOME", L"USER",
     L"SYSTEMROOT", L"USERPROFILE", L"COMSPEC",
     NULL
   };
@@ -209,7 +209,8 @@ start_p3dcert() {
   // These are the enviroment variables we forward from the current
   // environment, if they are set.
   const char *keep[] = {
-    "TMP", "TEMP", "HOME", "USER", 
+    "TMP", "TEMP", "HOME", "USER",
+    "LANGUAGE", "LC_ALL", "LC_MESSAGES", "LANG",
 #ifdef HAVE_X11
     "DISPLAY", "XAUTHORITY",
 #endif

+ 162 - 59
direct/src/plugin/p3dCert.cxx

@@ -13,6 +13,7 @@
 ////////////////////////////////////////////////////////////////////
 
 #include "p3dCert.h"
+#include "p3dCert_strings.h"
 #include "wstring_encode.h"
 #include "mkdir_complete.h"
 
@@ -26,8 +27,9 @@
 #include <sys/stat.h>
 #include <string.h>
 #include <limits.h>
+#include <locale.h>
 
-#define BUTTON_WIDTH 120
+#define BUTTON_WIDTH 180 // fit the Russian text
 #define BUTTON_SPACE 10
 
 #include "ca_bundle_data_src.c"
@@ -36,63 +38,152 @@
 #define WIN32_LEAN_AND_MEAN
 #include <windows.h>
 #include <shellapi.h>
+#include <malloc.h>
 
 #define snprintf sprintf_s
 #endif
 
-static const char
-self_signed_cert_text[] =
-  "This Panda3D application uses a self-signed certificate.  "
-  "This means the author's name can't be verified, and you have "
-  "no way of knowing for sure who wrote it.\n\n"
+#ifdef __APPLE__
+#include <CoreFoundation/CoreFoundation.h>
+#endif
 
-  "We recommend you click Cancel to avoid running this application.";
+static LanguageIndex li = LI_default;
+
+#if defined(_WIN32)
+static LanguageIndex detect_language() {
+  // This function was introduced in Windows Vista; it may not be available
+  // on older systems.
+  typedef BOOL (*GUPL)(DWORD, PULONG, PZZWSTR, PULONG);
+  GUPL pGetUserPreferredUILanguages = (GUPL)GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")),
+                                                           TEXT("GetUserPreferredUILanguages"));
+  if (pGetUserPreferredUILanguages != NULL) {
+    ULONG num_langs = 0;
+    ULONG buffer_size = 0;
+    pGetUserPreferredUILanguages(8, &num_langs, NULL, &buffer_size);
+    PZZWSTR buffer = (PZZWSTR)_alloca(buffer_size);
+    if (pGetUserPreferredUILanguages(8, &num_langs, buffer, &buffer_size)) {
+      for (ULONG i = 0; i < num_langs; ++i) {
+        size_t len = wcslen(buffer);
+        if (len >= 2 && (buffer[2] == 0 || buffer[2] == L'-')) {
+          // It may be a two-letter code; match it in our list.
+          for (int j = 0; j < LI_COUNT; ++j) {
+            const char *lang_code = language_codes[j];
+            if (lang_code != NULL && lang_code[0] == buffer[0] &&
+                                     lang_code[1] == buffer[1]) {
+              return (LanguageIndex)j;
+            }
+          }
+        }
+        buffer += len + 1;
+      }
+    }
+  }
 
-static const char
-unknown_auth_cert_text[] =
-  "This Panda3D application has been signed, but we don't recognize "
-  "the authority that verifies the signature.  This means the author's "
-  "name can't be trusted, and you have no way of knowing "
-  "for sure who wrote it.\n\n"
+  // Fall back to the old Windows XP function.
+  LANGID lang = GetUserDefaultUILanguage() & 0x3ff;
+  if (lang == 0) {
+    return LI_default;
+  }
 
-  "We recommend you click Cancel to avoid running this application.";
+  for (int i = 0; i < LI_COUNT; ++i) {
+    if (language_ids[i] != 0 && language_ids[i] == lang) {
+      return (LanguageIndex)i;
+    }
+  }
+  return LI_default;
+}
 
-static const char
-verified_cert_text[] =
-  "This Panda3D application has been signed by %s. "
-  "If you trust %s, then click the Run button below "
-  "to run this application on your computer.  This will also "
-  "automatically approve this and any other applications signed by "
-  "%s in the future.\n\n"
+#elif defined(__APPLE__)
+static LanguageIndex detect_language() {
+  // Get and iterate through the list of preferred languages.
+  CFArrayRef langs = CFLocaleCopyPreferredLanguages();
+  CFIndex num_langs = CFArrayGetCount(langs);
 
-  "If you are unsure about this application, "
-  "you should click Cancel instead.";
+  for (long i = 0; i < num_langs; ++i) {
+    CFStringRef lang = (CFStringRef)CFArrayGetValueAtIndex(langs, i);
 
-static const char
-expired_cert_text[] =
-  "This Panda3D application has been signed by %s, "
-  "but the certificate has expired.\n\n"
+    CFIndex length = CFStringGetLength(lang);
+    if (length < 2) {
+      continue;
+    }
 
-  "You should check the current date set on your computer's clock "
-  "to make sure it is correct.\n\n"
+    CFIndex max_size = CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8) + 1;
+    char *buffer = (char *)alloca(max_size);
+    if (!CFStringGetCString(lang, buffer, max_size, kCFStringEncodingUTF8)) {
+      continue;
+    }
 
-  "If your computer's date is correct, we recommend "
-  "you click Cancel to avoid running this application.";
+    if (isalnum(buffer[2])) {
+      // It's not a two-letter code.
+      continue;
+    }
 
-static const char
-generic_error_cert_text[] =
-  "This Panda3D application has been signed, but there is a problem "
-  "with the certificate (OpenSSL error code %d).\n\n"
+    // See if we support this language.
+    for (int j = 0; j < LI_COUNT; ++j) {
+      const char *lang_code = language_codes[j];
+      if (lang_code != NULL && strncasecmp(buffer, lang_code, 2) == 0) {
+        CFRelease(langs);
+        return (LanguageIndex)j;
+      }
+    }
+  }
 
-  "We recommend you click Cancel to avoid running this application.";
+  CFRelease(langs);
+  return LI_default;
+}
 
-static const char
-no_cert_text[] =
-  "This Panda3D application has not been signed.  This means you have "
-  "no way of knowing for sure who wrote it.\n\n"
+#else
+static LanguageIndex detect_language() {
+  // First consult the LANGUAGE variable, which is a GNU extension that can
+  // contain multiple languages in order of preference.
+  const char *lang = getenv("LANGUAGE");
+  while (lang != NULL && lang[0] != 0) {
+    size_t len;
+    const char *next = strchr(lang, ':');
+    if (next == NULL) {
+      len = strlen(lang);
+    } else {
+      len = (next - lang);
+      ++next;
+    }
 
-  "Click Cancel to avoid running this application.";
+    if (len >= 2 && !isalnum(lang[2])) {
+      // It may be a two-letter language code; match it in our list.
+      for (int i = 0; i < LI_COUNT; ++i) {
+        const char *lang_code = language_codes[i];
+        if (lang_code != NULL && strncasecmp(lang, lang_code, 2) == 0) {
+          return (LanguageIndex)i;
+        }
+      }
+    }
+
+    lang = next;
+  }
+
+  // Fall back to the C locale functions.
+  setlocale(LC_ALL, "");
+  lang = setlocale(LC_MESSAGES, NULL);
+
+  if (lang == NULL || lang[0] == 0 || strcmp(lang, "C") == 0) {
+    // Try the LANG variable.
+    lang = getenv("LANG");
+  }
+
+  if (lang == NULL || strlen(lang) < 2 || isalnum(lang[2])) {
+    // Couldn't extract a meaningful two-letter code.
+    return LI_default;
+  }
 
+  // It may be a two-letter language code; match it in our list.
+  for (int i = 0; i < LI_COUNT; ++i) {
+    const char *lang_code = language_codes[i];
+    if (lang_code != NULL && strncasecmp(lang, lang_code, 2) == 0) {
+      return (LanguageIndex)i;
+    }
+  }
+  return LI_default;
+}
+#endif
 
 #ifdef _WIN32
 int WINAPI
@@ -107,6 +198,8 @@ wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdS
     return 1;
   }
 
+  li = detect_language();
+
   wstring cert_filename (argv[0]);
   wstring cert_dir (argv[1]);
   LocalFree(argv);
@@ -126,6 +219,8 @@ int main(int argc, char **argv) {
     return 1;
   }
 
+  li = detect_language();
+
   string cert_filename (argv[1]);
   string cert_dir (argv[2]);
 
@@ -148,7 +243,7 @@ AuthDialog(const wstring &cert_filename, const wstring &cert_dir) :
 AuthDialog::
 AuthDialog(const string &cert_filename, const string &cert_dir) :
 #endif
-  Fl_Window(435, 242, "New Panda3D Application"),
+  Fl_Window(435, 242, new_application_title[li]),
   _cert_dir(cert_dir)
 {
   _view_cert_dialog = NULL;
@@ -315,12 +410,20 @@ read_cert_file(const string &cert_filename) {
 #endif  // _WIN32
 
   if (fp == NULL) {
+#ifdef _WIN32
+    wcerr << L"Couldn't read " << cert_filename.c_str() << L"\n";
+#else
     cerr << "Couldn't read " << cert_filename.c_str() << "\n";
+#endif
     return;
   }
   _cert = PEM_read_X509(fp, NULL, NULL, (void *)"");
   if (_cert == NULL) {
+#ifdef _WIN32
+    wcerr << L"Could not read certificate in " << cert_filename.c_str() << L".\n";
+#else
     cerr << "Could not read certificate in " << cert_filename.c_str() << ".\n";
+#endif
     fclose(fp);
     return;
   }
@@ -510,19 +613,19 @@ layout() {
   short bx = (w() - nbuttons * BUTTON_WIDTH - (nbuttons - 1) * BUTTON_SPACE) / 2;
 
   if (_verify_result == 0 && _cert != NULL) {
-    Fl_Return_Button *run_button = new Fl_Return_Button(bx, next_y, BUTTON_WIDTH, 25, "Run");
+    Fl_Return_Button *run_button = new Fl_Return_Button(bx, next_y, BUTTON_WIDTH, 25, run_title[li]);
     run_button->callback(this->run_clicked, this);
     bx += BUTTON_WIDTH + BUTTON_SPACE;
   }
 
   if (_cert != NULL) {
-    Fl_Button *view_button = new Fl_Button(bx, next_y, BUTTON_WIDTH, 25, "View Certificate");
+    Fl_Button *view_button = new Fl_Button(bx, next_y, BUTTON_WIDTH, 25, show_cert_title[li]);
     view_button->callback(this->view_cert_clicked, this);
     bx += BUTTON_WIDTH + BUTTON_SPACE;
   }
 
   Fl_Button *cancel_button;
-  cancel_button = new Fl_Button(bx, next_y, BUTTON_WIDTH, 25, "Cancel");
+  cancel_button = new Fl_Button(bx, next_y, BUTTON_WIDTH, 25, cancel_title[li]);
   cancel_button->callback(this->cancel_clicked, this);
 
   next_y += 42;
@@ -542,12 +645,12 @@ void AuthDialog::
 get_text(char *header, size_t hlen, char *text, size_t tlen) {
   switch (_verify_result) {
   case -1:
-    strncpy(header, "No signature!", hlen);
-    strncpy(text, no_cert_text, tlen);
+    strncpy(header, no_cert_title[li], hlen);
+    strncpy(text, no_cert_text[li], tlen);
     break;
 
   case 0:
-    snprintf(text, tlen, verified_cert_text, _friendly_name.c_str(),
+    snprintf(text, tlen, verified_cert_text[li], _friendly_name.c_str(),
                         _friendly_name.c_str(), _friendly_name.c_str());
     break;
 
@@ -555,24 +658,24 @@ get_text(char *header, size_t hlen, char *text, size_t tlen) {
   case X509_V_ERR_CERT_HAS_EXPIRED:
   case X509_V_ERR_CRL_NOT_YET_VALID:
   case X509_V_ERR_CRL_HAS_EXPIRED:
-    strncpy(header, "Expired signature!", hlen);
-    snprintf(text, tlen, expired_cert_text, _friendly_name.c_str());
+    strncpy(header, expired_cert_title[li], hlen);
+    snprintf(text, tlen, expired_cert_text[li], _friendly_name.c_str());
     break;
 
   case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY:
-    strncpy(header, "Unverified signature!", hlen);
-    snprintf(text, tlen, unknown_auth_cert_text);
+    strncpy(header, unverified_cert_title[li], hlen);
+    strncpy(text, unknown_auth_cert_text[li], tlen);
     break;
 
   case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
   case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN:
-    strncpy(header, "Unverified signature!", hlen);
-    strncpy(text, self_signed_cert_text, tlen);
+    strncpy(header, unverified_cert_title[li], hlen);
+    strncpy(text, self_signed_cert_text[li], tlen);
     break;
 
   default:
-    strncpy(header, "Unverified signature!", hlen);
-    snprintf(text, tlen, generic_error_cert_text, _verify_result);
+    strncpy(header, unverified_cert_title[li], hlen);
+    snprintf(text, tlen, generic_error_cert_text[li], _verify_result);
   }
 }
 
@@ -583,7 +686,7 @@ get_text(char *header, size_t hlen, char *text, size_t tlen) {
 ////////////////////////////////////////////////////////////////////
 ViewCertDialog::
 ViewCertDialog(AuthDialog *auth_dialog, X509 *cert) :
-  Fl_Window(600, 400, "View Certificate"),
+  Fl_Window(600, 400, show_cert_title[li]),
   _auth_dialog(auth_dialog),
   _cert(cert)
 {
@@ -660,12 +763,12 @@ layout() {
 
   short bx = (w() - BUTTON_WIDTH * 2 - BUTTON_SPACE) / 2;
 
-  Fl_Return_Button *run_button = new Fl_Return_Button(bx, 360, BUTTON_WIDTH, 25, "Run");
+  Fl_Return_Button *run_button = new Fl_Return_Button(bx, 360, BUTTON_WIDTH, 25, run_title[li]);
   run_button->callback(this->run_clicked, this);
 
   bx += BUTTON_WIDTH + BUTTON_SPACE;
 
-  Fl_Button *cancel_button = new Fl_Button(bx, 360, BUTTON_WIDTH, 25, "Cancel");
+  Fl_Button *cancel_button = new Fl_Button(bx, 360, BUTTON_WIDTH, 25, cancel_title[li]);
   cancel_button->callback(this->cancel_clicked, this);
 
   end();

+ 3 - 3
direct/src/plugin/p3dCert.h

@@ -92,9 +92,9 @@ private:
   X509 *_cert;
   STACK_OF(X509) *_stack;
 
-  char _header[32];
-  char _text[512];
-  char _text_clean[1024];
+  char _header[64];
+  char _text[1024];
+  char _text_clean[2048];
 
   string _friendly_name;
   int _verify_result;

+ 571 - 0
direct/src/plugin/p3dCert_strings.cxx

@@ -0,0 +1,571 @@
+// Filename: p3dCert_strings.h
+// Created by:  rdb (25Mar15)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+#include "p3dCert_strings.h"
+
+// Translations kindly provided by:
+// eng: drwr
+// nld: rdb
+// deu: Sebastian Hoffmann <TheCheapestPixels at googlemail dot com>
+// spa: Imanol Celaya <imanol at celaya dot me>
+// ita: Flavio Clava
+// rus: montreal
+
+const char *language_codes[LI_COUNT] =
+  {"en", "nl", "de", "es", "it", "eo", "ru"};
+
+// https://msdn.microsoft.com/en-us/library/windows/desktop/dd318693%28v=vs.85%29.aspx
+const unsigned char language_ids[LI_COUNT] =
+  {0x09, 0x13, 0x07, 0x0A, 0x10, 0x8F, 0x19};
+
+const char *
+run_title[LI_COUNT] = {
+  "Run",
+  "Uitvoeren",
+  "Starten",
+  "Ejecutar",
+  "Lancia",
+  "Lan\304\211i",
+  "\320\227\320\260\320\277\321\203\321\201\321\202\320\270\321\202\321\214",
+};
+
+const char *
+cancel_title[LI_COUNT] = {
+  "Cancel",
+  "Annuleren",
+  "Abbrechen",
+  "Cancelar",
+  "Cancella",
+  "Nuligi",
+  "\320\236\321\202\320\274\320\265\320\275\320\260",
+};
+
+const char *
+show_cert_title[LI_COUNT] = {
+  "Show Certificate",
+  "Toon Certificaat",
+  "Zertifikat anzeigen",
+  "Mostrar certificado",
+  "Mostra certificato",
+  "Montri Ateston",
+  "\320\237\320\276\320\272\320\260\320\267\320\260\321\202\321\214 \321\201"
+  "\320\265\321\200\321\202\320\270\321\204\320\270\320\272\320\260\321\202",
+};
+
+const char *
+new_application_title[LI_COUNT] = {
+  "New Panda3D Application",
+  "Nieuwe Panda3D Applicatie",
+  "Neue Panda3D-Anwendung",
+  "Nueva aplicaci\303\263n de Panda3D",
+  "Nuova applicazione Panda3D",
+  "Nova aplika\304\265o de Panda3D",
+  "\320\235\320\276\320\262\320\276\320\265 Panda3D-\320\277\321\200\320\270"
+  "\320\273\320\276\320\266\320\265\320\275\320\270\320\265",
+};
+
+const char *
+no_cert_title[LI_COUNT] = {
+  "No signature!",
+  "Geen handtekening!",
+  "Keine Signatur!",
+  "Sin firma!",
+  "Nessuna firma!",
+  "Neniu subskribo!",
+  "\320\235\320\265\321\202 \320\277\320\276\320\264\320\277\320\270\321\201"
+  "\320\270!",
+};
+
+const char *
+unverified_cert_title[LI_COUNT] = {
+  "Unverified signature!",
+  "Ongeverifieerde handtekening!",
+  "Unbest\303\244tigte Signatur!",
+  "Firma sin verificar!",
+  "Firma non verificata!",
+  "Nekontrolita subskribo!",
+  "\320\235\320\265\320\277\321\200\320\276\320\262\320\265\321\200\320\265"
+  "\320\275\320\275\320\260\321\217 \320\277\320\276\320\264\320\277\320\270"
+  "\321\201\321\214!",
+};
+
+const char *
+expired_cert_title[LI_COUNT] = {
+  "Expired signature!",
+  "Verlopen handtekening!",
+  "Abgelaufene Signatur!",
+  "Firma caducada!",
+  "Firma scaduta!",
+  "Eksvalidi\304\235inta subskribo!",
+  "\320\241\321\200\320\276\320\272 \320\264\320\265\320\271\321\201\321\202"
+  "\320\262\320\270\321\217 \320\277\320\276\320\264\320\277\320\270\321\201"
+  "\320\270 \320\270\321\201\321\202\321\221\320\272!",
+};
+
+
+const char *
+self_signed_cert_text[LI_COUNT] = {
+  // eng
+  "This Panda3D application uses a self-signed certificate.  "
+  "This means the author's name can't be verified, and you have "
+  "no way of knowing for sure who wrote it.\n"
+  "\n"
+  "We recommend you click Cancel to avoid running this application.",
+
+  // nld
+  "Deze Panda3D applicatie gebruikt een zelf-getekend certificaat.  "
+  "Dit betekent dat de auteursnaam niet kan worden geverifieerd, en het "
+  "niet zeker is of de applicatie te vertrouwen is.\n"
+  "\n"
+  "Het is aanbevolen om op Annuleren te klikken om de applicatie af te "
+  "sluiten.",
+
+  // deu
+  "Diese Panda3D-Anwendung benutzt ein selbst-signiertes Zertifikat.  Dies "
+  "bedeutet, dass weder der Name des Autors \303\274berpr\303\274ft werden "
+  "kann, noch dass garantiert werden kann, dass tats\303\244chlich die "
+  "angegebene Person diese Anwendung geschrieben hat.\n"
+  "\n"
+  "Wir empfehlen, dass Sie Abbrechen dr\303\274cken um diese Anwendung nicht "
+  "auszuf\303\274hren.",
+
+  // spa
+  "Esta aplicaci\303\263n de Panda3D usa un certificado autofirmado.  "
+  "Esto significa que el nombre del autor no puede ser verificado y no se "
+  "puede conocer seguro quien la ha escrito.\n"
+  "\n"
+  "Se recomienda cancelar para evitar ejecutar esta aplicaci\303\263n.",
+
+  // ita
+  "Questa applicazione Panda3D usa un certificato autofirmato.  Ci\303\262 "
+  "significa che il nome dell'autore non pu\303\262 essere verificato, e "
+  "che non hai modo di assicurarti circa chi la abbia scritta.\n"
+  "\n"
+  "Raccomandiamo di cliccare su Cancella per evitare di lanciare questa "
+  "applicazione.",
+
+  // epo
+  "\304\210i tiu aplika\304\265o de Panda3D uzas memsubskribitan ateston.  "
+  "Tio signifas ke la nomo de la verkanto ne povas esti kontrolita, kaj vi "
+  "ne havas certan scimanieron pri la vera verkanto de la aplika\304\265o.\n"
+  "\n"
+  "Ni rekomendas ke vi premas la butonon 'Nuligi' por eviti lan\304\211on de "
+  "\304\211i tiu aplika\304\265o.",
+
+  // rus
+  "\320\255\321\202\320\276 \320\277\321\200\320\270\320\273\320\276\320\266"
+  "\320\265\320\275\320\270\320\265 \320\270\321\201\320\277\320\276\320\273"
+  "\321\214\320\267\321\203\320\265\321\202 \321\201\320\260\320\274\320\276"
+  "\320\267\320\260\320\262\320\265\321\200\320\265\320\275\320\275\321\213"
+  "\320\271 \321\201\320\265\321\200\321\202\320\270\321\204\320\270\320\272"
+  "\320\260\321\202.  \320\255\321\202\320\276 \320\276\320\267\320\275"
+  "\320\260\321\207\320\260\320\265\321\202, \321\207\321\202\320\276 "
+  "\320\270\320\274\321\217 \320\260\320\262\321\202\320\276\321\200\320\260 "
+  "\320\275\320\265 \320\274\320\276\320\266\320\265\321\202 \320\261\321\213"
+  "\321\202\321\214 \320\277\320\276\320\264\321\202\320\262\320\265\320\266"
+  "\320\264\320\265\320\275\320\276, \320\270 \320\262\321\213 \320\275"
+  "\320\265 \320\274\320\276\320\266\320\265\321\202\320\265 \320\267\320\275"
+  "\320\260\321\202\321\214, \320\272\321\202\320\276 \320\275\320\260"
+  "\320\277\320\270\321\201\320\260\320\273 \321\215\321\202\320\276 \320\277"
+  "\321\200\320\270\320\273\320\276\320\266\320\265\320\275\320\270\320\265.\n"
+  "\n"
+  "\320\234\321\213 \321\200\320\265\320\272\320\276\320\274\320\265\320\275"
+  "\320\264\321\203\320\265\320\274 \320\262\320\260\320\274 \320\275\320\260"
+  "\320\266\320\260\321\202\321\214 \"\320\236\321\202\320\274\320\265"
+  "\320\275\320\260\" \320\270 \320\275\320\265 \320\267\320\260\320\277"
+  "\321\203\321\201\320\272\320\260\321\202\321\214 \321\215\321\202\320\276 "
+  "\320\277\321\200\320\270\320\273\320\276\320\266\320\265\320\275\320\270"
+  "\320\265.",
+};
+
+
+const char *
+unknown_auth_cert_text[LI_COUNT] = {
+  "This Panda3D application has been signed, but we don't recognize "
+  "the authority that verifies the signature.  This means the author's "
+  "name can't be verified, and you have no way of knowing "
+  "for sure who wrote it.\n"
+  "\n"
+  "We recommend you click Cancel to avoid running this application.",
+
+   // nld
+  "Deze Panda3D applicatie is ondertekend, maar de certificaatautoriteit "
+  "die het certificaat heeft uitgegeven wordt niet herkend.  Dit betekent "
+  "dat de auteursnaam niet te vertrouwen is, en het niet zeker is wie de "
+  "applicatie gemaakt heeft.\n"
+  "\n"
+  "Het is aanbevolen om op Annuleren te klikken om de applicatie af te "
+  "sluiten.",
+
+  // deu
+  "Diese Panda3D-Anwendung wurde signiert, aber die "
+  "\303\234berpr\303\274fungsstelle wurde nicht anerkannt.  Dies bedeutet, "
+  "dass weder der Name des Autors \303\274berpr\303\274ft werden kann, noch "
+  "dass garantiert werden kann, dass tats\303\244chlich die angegebene "
+  "Person diese Anwendung geschrieben hat.\n"
+  "\n"
+  "Wir empfehlen, dass Sie Abbrechen dr\303\274cken um diese Anwendung nicht "
+  "auszuf\303\274hren.",
+
+  // spa
+  "Esta aplicaci\303\263n de Panda3D esta firmada, pero no reconocemos la "
+  "autoridad que la verifica.  Esto significa que el nombre del autor no "
+  "puede ser verificado y no se puede conocer seguro quien la ha escrito.\n"
+  "\n"
+  "Se recomienda cancelar para evitar ejecutar esta aplicaci\303\263n.",
+
+  // ita
+  "Questa applicazione Panda3D \303\250 stata firmata, ma non riconosciamo "
+  "l'autorit\303\240 che verifica la firma. Ci\303\262 significa che il "
+  "nome dell'autore non pu\303\262 essere verificato, e che non hai modo "
+  "di assicurarti circa chi la abbia scritta.\n"
+  "\n"
+  "Raccomandiamo di cliccare su Cancella per evitare di lanciare questa "
+  "applicazione.",
+
+  // epo
+  "\304\210i tiu aplika\304\265o estas subskribita, sed ni ne rekonas "
+  "la a\305\255toritaton, kiu kontrolas la subskribon. Tio signifas ke la "
+  "nomo de la verkanto ne povas esti konfidata, kaj vi ne havas certan "
+  "scimanieron pri la vera verkanto de la aplika\304\265o.\n"
+  "\n"
+  "Ni rekomendas ke vi premas la butonon 'Nuligi' por eviti lan\304\211on "
+  "de \304\211i tiu aplika\304\265o.",
+
+  // rus
+  "\320\243 \321\215\321\202\320\276\320\263\320\276 \320\277\321\200\320\270"
+  "\320\273\320\276\320\266\320\265\320\275\320\270\321\217 \320\265\321\201"
+  "\321\202\321\214 \320\277\320\276\320\264\320\277\320\270\321\201\321\214,"
+  " \320\277\320\276 \320\272\320\276\321\202\320\276\321\200\320\276\320\271"
+  " \320\274\321\213 \320\275\320\265 \320\274\320\276\320\266\320\265"
+  "\320\274 \321\200\320\260\321\201\320\277\320\276\320\267\320\275\320\260"
+  "\321\202\321\214 \320\262\320\273\320\260\320\264\320\265\320\273\321\214"
+  "\321\206\320\260.  \320\255\321\202\320\276 \320\276\320\267\320\275"
+  "\320\260\321\207\320\260\320\265\321\202, \321\207\321\202\320\276 "
+  "\320\270\320\274\321\217 \320\260\320\262\321\202\320\276\321\200\320\260 "
+  "\320\275\320\265 \320\274\320\276\320\266\320\265\321\202 \320\261\321\213"
+  "\321\202\321\214 \320\276\320\277\321\200\320\265\320\264\320\265\320\273"
+  "\320\265\320\275\320\276, \320\270 \320\275\320\265\320\262\320\276"
+  "\320\267\320\274\320\276\320\266\320\275\320\276 \321\202\320\276\321\207"
+  "\320\275\320\276 \321\203\320\267\320\275\320\260\321\202\321\214, "
+  "\320\272\321\202\320\276 \320\275\320\260\320\277\320\270\321\201\320\260"
+  "\320\273 \321\215\321\202\320\276 \320\277\321\200\320\270\320\273\320\276"
+  "\320\266\320\265\320\275\320\270\320\265.\n"
+  "\n"
+  "\320\234\321\213 \321\200\320\265\320\272\320\276\320\274\320\265\320\275"
+  "\320\264\321\203\320\265\320\274 \320\262\320\260\320\274 \320\275\320\260"
+  "\320\266\320\260\321\202\321\214 \"\320\236\321\202\320\274\320\265"
+  "\320\275\320\260\" \320\270 \320\275\320\265 \320\267\320\260\320\277"
+  "\321\203\321\201\320\272\320\260\321\202\321\214 \321\215\321\202\320\276 "
+  "\320\277\321\200\320\270\320\273\320\276\320\266\320\265\320\275\320\270"
+  "\320\265.",
+};
+
+
+const char *
+verified_cert_text[LI_COUNT] = {
+  // eng
+  "This Panda3D application has been signed by %s.  "
+  "If you trust %s, then click the Run button below "
+  "to run this application on your computer.  This will also "
+  "automatically approve this and any other applications signed by "
+  "%s in the future.\n"
+  "\n"
+  "If you are unsure about this application, "
+  "you should click Cancel instead.",
+
+  // nld
+  "Deze Panda3D applicatie is ondertekend door %s.  "
+  "Als u %s vertrouwt, klik dan de onderstaande knop Uitvoeren om de applicatie "
+  "op uw computer uit te voeren.  Dit zal ook deze en andere door %s getekende "
+  "applicaties in het vervolg toestaan.\n"
+  "\n"
+  "Als u niet zeker bent over deze applicatie is het aanbevolen om op "
+  "Annuleren te klikken.",
+
+  // deu
+  "Diese Panda3D-Anwendung wurde von %s signiert.  Falls Sie %s vertrauen, "
+  "dr\303\274cken Sie den Starten-Knopf, um diese Anwendung auszuf\303\274hren.  "
+  "Zudem werden in der Zukunft diese und alle anderen von %s signierten "
+  "Anwendungen automatisch als vertrauensw\303\274rdig anerkannt.\n"
+  "\n"
+  "Falls Sie sich unsicher \303\274ber diese Anwendung sind, sollten Sie "
+  "Abbrechen dr\303\274cken.",
+
+  // spa
+  "Esta aplicaci\303\263n de Panda3D ha sido firmada por %s.  Si se considera %s"
+  "de confianza el bot\303\263n inferior ejecutar\303\241 la aplicaci\303\263n."
+  "Esto validara esta y cualquier otra applizacion firmada por %s en el futuro.\n"
+  "\n"
+  "Si se duda de la aplicaci\303\263nse recomienda cancelar."
+
+  // ita
+  "Questa applicazione Panda3D \303\250 stata firmata da %s.  Se %s \303\250 "
+  "un'entit\303\240 fidata, allora clicca il bottone Lancia sottostante per "
+  "lanciare questa applicazione sul tuo computer. Inoltre, ci\303\262 "
+  "approver\303\240 automaticamente questa e ogni altra applicazione "
+  "firmata da %s in futuro.\n"
+  "\n"
+  "Se non sei sicuro circa questa applicazione, dovresti invece cliccare "
+  "su Cancella.",
+
+  // epo
+  "\304\210i tiu aplika\304\265o estas subskribita de %s.  "
+  "Se %s estas fidinda la\305\255 vi, premu la butonon 'Lan\304\211i' sube por "
+  "lan\304\211i \304\211i tiun aplika\304\265on per via komputilo.  Anka\305\255 "
+  "tio a\305\255tomate estonece aprobos \304\211i tiun kaj alian ajn "
+  "aplika\304\265on, kiu estas subskribita de %s.\n"
+  "\n"
+  "Se vi ne estas certa pri \304\211i tiu aplika\304\265o, "
+  "vi anstata\305\255e povus premi la butonon 'Nuligi'.",
+
+  // rus
+  "\320\255\321\202\320\276 \320\277\321\200\320\270\320\273\320\276\320\266"
+  "\320\265\320\275\320\270\320\265 \320\277\320\276\320\264\320\277\320\270"
+  "\321\201\320\260\320\275\320\276 %s.  "
+  "\320\225\321\201\320\273\320\270 \320\262\321\213 \320\264\320\276\320\262"
+  "\320\265\321\200\321\217\320\265\321\202\320\265 %s, \320\275\320\260"
+  "\320\266\320\274\320\270\321\202\320\265 \320\272\320\275\320\276\320\277"
+  "\320\272\321\203 \"\320\227\320\260\320\277\321\203\321\201\321\202"
+  "\320\270\321\202\321\214\" \320\262\320\275\320\270\320\267\321\203, "
+  "\321\207\321\202\320\276\320\261\321\213 \320\267\320\260\320\277\321\203"
+  "\321\201\321\202\320\270\321\202\321\214 \321\215\321\202\320\276 \320\277"
+  "\321\200\320\270\320\273\320\276\320\266\320\265\320\275\320\270\320\265 "
+  "\320\275\320\260 \320\262\320\260\321\210\320\265\320\274 \320\272\320\276"
+  "\320\274\320\277\321\214\321\216\321\202\320\265\321\200\320\265. \320\255"
+  "\321\202\320\276 \321\202\320\260\320\272\320\266\320\265 \320\260\320\262"
+  "\321\202\320\276\320\274\320\260\321\202\320\270\321\207\320\265\321\201"
+  "\320\272\320\270 \320\277\320\276\320\264\321\202\320\262\320\265\321\200"
+  "\320\264\320\270\321\202 \321\215\321\202\320\276 \320\270 \320\264"
+  "\321\200\321\203\320\263\320\270\320\265 \320\277\321\200\320\270\320\273"
+  "\320\276\320\266\320\265\320\275\320\270\321\217, \320\275\320\260\320\277"
+  "\320\270\321\201\320\260\320\275\320\275\321\213\320\265 %s \320\262 "
+  "\320\261\321\203\320\264\321\203\321\211\320\265\320\274.\n"
+  "\n"
+  "\320\225\321\201\320\273\320\270 \320\262\321\213 \320\275\320\265 "
+  "\321\203\320\262\320\265\321\200\320\265\320\275\321\213 \320\262 \321\215"
+  "\321\202\320\276\320\274 \320\277\321\200\320\270\320\273\320\276\320\266"
+  "\320\265\320\275\320\270\320\270, \320\275\320\260\320\266\320\274\320\270"
+  "\321\202\320\265 \320\272\320\275\320\276\320\277\320\272\321\203 \""
+  "\320\236\321\202\320\274\320\265\320\275\320\260\".",
+};
+
+
+const char *
+expired_cert_text[LI_COUNT] = {
+  // eng
+  "This Panda3D application has been signed by %s, "
+  "but the certificate has expired.\n"
+  "\n"
+  "You should check the current date set on your computer's clock "
+  "to make sure it is correct.\n"
+  "\n"
+  "If your computer's date is correct, we recommend "
+  "you click Cancel to avoid running this application.",
+
+  // nld
+  "Deze Panda3D applicatie is ondertekend door %s, maar de geldigheidsdatum "
+  "van het certificaat is verstreken.\n"
+  "\n"
+  "Controleer de datum op uw computerklok om te zorgen dat deze juist is "
+  "ingesteld.\n"
+  "\n"
+  "Als de datum op uw computer juist is, is het aanbevolen om op Annuleren te "
+  "klikken om de applicatie af te sluiten.",
+
+  // deu
+  "Diese Anwendung wurde von %s signiert, aber das Zertifikat ist abgelaufen.\n"
+  "\n"
+  "Sie sollten die aktuelle Uhrzeit auf Ihrem Computer "
+  "\303\274berpr\303\274fen, um sicherzugehen, dass sie korrekt ist.\n"
+  "\n"
+  "Falls die Uhrzeit auf Ihrem Computer korrekt ist, empfehlen wir Ihnen "
+  "Abbrechen zu dr\303\274cken.",
+
+  // spa
+  "Esta aplicaci\303\263n Panda3D ha sido firmada por %s pero el certificado ha"
+  "caducado.\n"
+  "\n"
+  "Se recomienda comprobar la fecha del reloj.\n"
+  "\n"
+  "Si la fecha del reloj es correcta se recomienda cancelar.",
+
+  // ita
+  "Questa applicazione Panda3D \303\250 stata firmata da %s, ma il "
+  "certificato \303\250 scaduto.\n"
+  "\n"
+  "Dovresti controllare la data attuale impostata nell'orologio del tuo "
+  "computer per assicurarti che sia corretta.\n"
+  "\n"
+  "Se la data del tuo computer \303\250 corretta, raccomandiamo di cliccare "
+  "Cancella per evitare di lanciare questa applicazione."
+
+  // epo
+  "\304\210i tiu aplika\304\265o de Panda3D estas subskribita de %s, "
+  "sed la atesto eksvalidi\304\235is.\n"
+  "\n"
+  "Vi povus kontroli la aktualan daton, kiu estas agordata sur la horlo\304\235o de "
+  "via komputilo por certigi ke \304\235i estas korekta.\n"
+  "\n"
+  "Se la dato de via komputilo estas korekta, ni rekomendas ke vi premas la "
+  "butonon 'Nuligi' por eviti lan\304\211on de \304\211i tiu aplika\304\265o.",
+
+  // rus
+  "\320\255\321\202\320\276 \320\277\321\200\320\270\320\273\320\276\320\266"
+  "\320\265\320\275\320\270\320\265 \320\277\320\276\320\264\320\277\320\270"
+  "\321\201\320\260\320\275\320\276 %s, \320\276\320\264\320\275\320\260"
+  "\320\272\320\276 \321\201\321\200\320\276\320\272 \320\264\320\265\320\271"
+  "\321\201\321\202\320\262\320\270\321\217 \321\201\320\265\321\200\321\202"
+  "\320\270\321\204\320\270\320\272\320\260\321\202\320\260 \320\270\321\201"
+  "\321\202\321\221\320\272.\n"
+  "\n"
+  "\320\237\321\200\320\276\320\262\320\265\321\200\321\214\321\202\320\265, "
+  "\320\277\320\276\320\266\320\260\320\273\321\203\320\271\321\201\321\202"
+  "\320\260, \320\264\320\260\321\202\321\203 \320\275\320\260 \320\262"
+  "\320\260\321\210\320\265\320\274 \320\272\320\276\320\274\320\277\321\214"
+  "\321\216\321\202\320\265\321\200\320\265.\n"
+  "\n"
+  "\320\225\321\201\320\273\320\270 \320\275\320\260 \320\262\320\260\321\210"
+  "\320\265\320\274 \320\272\320\276\320\274\320\277\321\214\321\216\321\202"
+  "\320\265\321\200\320\265 \321\203\321\201\321\202\320\260\320\275\320\276"
+  "\320\262\320\273\320\265\320\275\320\276 \320\277\321\200\320\260\320\262"
+  "\320\270\320\273\321\214\320\275\320\276\320\265 \320\262\321\200\320\265"
+  "\320\274\321\217, \320\274\321\213 \321\200\320\265\320\272\320\276"
+  "\320\274\320\265\320\275\320\264\321\203\320\265\320\274 \320\262\320\260"
+  "\320\274 \320\275\320\260\320\266\320\260\321\202\321\214 \320\272\320\275"
+  "\320\276\320\277\320\272\321\203 \"\320\236\321\202\320\274\320\265"
+  "\320\275\320\260\" \320\270 \320\275\320\265 \320\267\320\260\320\277"
+  "\321\203\321\201\320\272\320\260\321\202\321\214 \321\215\321\202\320\276 "
+  "\320\277\321\200\320\270\320\273\320\276\320\266\320\265\320\275\320\270"
+  "\320\265.",
+};
+
+
+const char *
+generic_error_cert_text[LI_COUNT] = {
+  // eng
+  "This Panda3D application has been signed, but there is a problem "
+  "with the certificate (OpenSSL error code %d).\n"
+  "\n"
+  "We recommend you click Cancel to avoid running this application.",
+
+  // nld
+  "Deze Panda3D applicatie is ondertekend, maar er is een probleem "
+  "met het certificaat opgetreden (OpenSSL foutcode %d).\n"
+  "\n"
+  "Het is aanbevolen om op Annuleren te klikken om de applicatie af te "
+  "sluiten.",
+
+  // deu
+  "Diese Panda3D-Anwendung wurde signiert, aber es gibt ein Problem mit "
+  "dem Zertifikat (OpenSSL Fehlercode %d).\n"
+  "\n"
+  "Wir empfehlen, dass Sie Abbrechen dr\303\274cken um diese Anwendung nicht "
+  "auszuf\303\274hren.",
+
+  // spa
+  "Esta aplicaci\303\263n de Panda3D esta firmada pero hay un problema con el "
+  "certificado (Error de OpenSSL %d).\n"
+  "\n"
+  "Se recomienda cancelar para evitar ejecutar esta aplicaci\303\263n.",
+
+  // ita
+  "Questa applicazione Panda3D \303\250 stata firmata, ma c'\303\250 un "
+  "problema col certificato (codice di errore OpenSSL %s).\n"
+  "\n"
+  "Raccomandiamo di cliccare su Cancella per evitare di lanciare questa "
+  "applicazione.",
+
+  // epo
+  "\304\210i tiu aplika\304\265o de Panda3D estas subskribita, sed la "
+  "atesto havas problemon (OpenSSL erarkodo %d).\n"
+  "\n"
+  "Ni rekomendas ke vi premas la butonon 'Nuligi' por eviti lan\304\211on "
+  "de \304\211i tiu aplika\304\265o.",
+
+  // rus
+  "\320\243 \321\215\321\202\320\276\320\263\320\276 \320\277\321\200\320\270"
+  "\320\273\320\276\320\266\320\265\320\275\320\270\321\217 \320\265\321\201"
+  "\321\202\321\214 \320\277\320\276\320\264\320\277\320\270\321\201\321\214,"
+  " \320\275\320\276 \320\262\320\276\320\267\320\275\320\270\320\272\320\273"
+  "\320\260 \320\277\321\200\320\276\320\261\320\273\320\265\320\274\320\260 "
+  "\321\201 \321\201\320\265\321\200\321\202\320\270\321\204\320\270\320\272"
+  "\320\260\321\202\320\276\320\274 (\320\232\320\276\320\264 \320\276"
+  "\321\210\320\270\320\261\320\272\320\270 OpenSSL %d).\n"
+  "\n"
+  "\320\234\321\213 \321\200\320\265\320\272\320\276\320\274\320\265\320\275"
+  "\320\264\321\203\320\265\320\274 \320\262\320\260\320\274 \320\275\320\260"
+  "\320\266\320\260\321\202\321\214 \"\320\236\321\202\320\274\320\265"
+  "\320\275\320\260\" \320\270 \320\275\320\265 \320\267\320\260\320\277"
+  "\321\203\321\201\320\272\320\260\321\202\321\214 \321\215\321\202\320\276 "
+  "\320\277\321\200\320\270\320\273\320\276\320\266\320\265\320\275\320\270"
+  "\320\265.",
+};
+
+
+const char *
+no_cert_text[LI_COUNT] = {
+  "This Panda3D application has not been signed.  This means you have "
+  "no way of knowing for sure who wrote it.\n"
+  "\n"
+  "Click Cancel to avoid running this application.",
+
+  // nld
+  "Deze Panda3D applicatie is niet ondertekend.  Dit betekent dat het niet "
+  "mogelijk is om de auteur te verifi\303\253ren.\n"
+  "\n"
+  "Klik op Annuleren om de applicatie af te sluiten.",
+
+  // deu
+  "Diese Panda3D-Anwendung wurde nicht signiert.  Es gibt keine "
+  "M\303\266glichkeit festzustellen, wer diese entwickelt hat.\n"
+  "\n"
+  "Dr\303\274cken Sie Abbrechen, um diese Anwendung nicht auszuf\303\274hren.",
+
+  // spa
+  "Esta aplicaci\303\263n de Panda3D no esta firmada, no hay forma de conocer "
+  "quien la ha escrito.\n"
+  "\n"
+  "Cancelar para evitar ejecutar la aplicaci\303\263n.",
+
+  // ita
+  "Questa applicazione Panda3D non \303\250 stata firmata.  Ci\303\262 "
+  "significa che non hai modo di assicurarti circa chi la abbia scritta.\n"
+  "\n"
+  "Clicca Cancella per evitare di lanciare questa applicazione.",
+
+  // epo
+  "\304\210i tiu aplika\304\265o de Panda3D ne estas subskribita.  Tio "
+  "signifas ke vi ne havas certan scimanieron pri la vera verkanto de "
+  "la aplika\304\265o.\n"
+  "\n"
+  "Premu la butonon 'Nuligi' por eviti lan\304\211on de \304\211i tiu "
+  "aplika\304\265o.",
+
+  // rus
+  "\320\243 \321\215\321\202\320\276\320\263\320\276 \320\277\321\200\320\270"
+  "\320\273\320\276\320\266\320\265\320\275\320\270\321\217 \320\275\320\265"
+  "\321\202 \320\277\320\276\320\264\320\277\320\270\321\201\320\270. "
+  "\320\255\321\202\320\276 \320\276\320\267\320\275\320\260\321\207\320\260"
+  "\320\265\321\202, \321\207\321\202\320\276 \320\262\321\213 \320\275"
+  "\320\265 \320\274\320\276\320\266\320\265\321\202\320\265 \320\267\320\275"
+  "\320\260\321\202\321\214, \320\272\321\202\320\276 \320\265\320\263"
+  "\320\276 \320\275\320\260\320\277\320\270\321\201\320\260\320\273.\n"
+  "\n"
+  "\320\235\320\260\320\266\320\274\320\270\321\202\320\265 \"\320\236"
+  "\321\202\320\274\320\265\320\275\320\260\", \321\207\321\202\320\276"
+  "\320\261\321\213 \320\275\320\265 \320\267\320\260\320\277\321\203\321\201"
+  "\320\272\320\260\321\202\321\214 \320\277\321\200\320\270\320\273\320\276"
+  "\320\266\320\265\320\275\320\270\320\265.",
+};

+ 45 - 0
direct/src/plugin/p3dCert_strings.h

@@ -0,0 +1,45 @@
+// Filename: p3dCert_strings.h
+// Created by:  rdb (25Mar15)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) Carnegie Mellon University.  All rights reserved.
+//
+// All use of this software is subject to the terms of the revised BSD
+// license.  You should have received a copy of this license along
+// with this source code in a file named "LICENSE."
+//
+////////////////////////////////////////////////////////////////////
+
+enum LanguageIndex {
+  LI_en, // English
+  LI_nl, // Dutch
+  LI_de, // German
+  LI_es, // Spanish
+  LI_it, // Italian
+  LI_eo, // Esperanto
+  LI_ru, // Russian
+  LI_COUNT,
+
+  LI_default = LI_en
+};
+
+extern const char *language_codes[LI_COUNT];
+extern const unsigned char language_ids[LI_COUNT];
+
+extern const char *run_title[LI_COUNT];
+extern const char *cancel_title[LI_COUNT];
+extern const char *show_cert_title[LI_COUNT];
+
+extern const char *new_application_title[LI_COUNT];
+extern const char *no_cert_title[LI_COUNT];
+extern const char *unverified_cert_title[LI_COUNT];
+extern const char *expired_cert_title[LI_COUNT];
+
+extern const char *self_signed_cert_text[LI_COUNT];
+extern const char *unknown_auth_cert_text[LI_COUNT];
+extern const char *verified_cert_text[LI_COUNT];
+extern const char *expired_cert_text[LI_COUNT];
+extern const char *generic_error_cert_text[LI_COUNT];
+extern const char *no_cert_text[LI_COUNT];

+ 91 - 3
direct/src/plugin/p3dInstance.cxx

@@ -1564,7 +1564,7 @@ uninstall_packages() {
   if (inst_mgr->get_verify_contents() != P3D_VC_never) {
     string start_dir_suffix = get_start_dir_suffix();
     if (!start_dir_suffix.empty()) {
-      string start_dir = inst_mgr->get_root_dir() + "/start" + start_dir_suffix;
+      string start_dir = inst_mgr->get_start_dir() + start_dir_suffix;
       nout << "Cleaning up start directory " << start_dir << "\n";
       inst_mgr->delete_directory_recursively(start_dir);
     }
@@ -2769,6 +2769,7 @@ make_splash_window() {
     int r, g, b;
     if (parse_color(r, g, b, _fparams.lookup_token("bgcolor"))) {
       _splash_window->set_bgcolor(r, g, b);
+      _splash_window->set_bar_bgcolor(r, g, b);
     } else {
       nout << "parse failure on bgcolor " << _fparams.lookup_token("bgcolor") << "\n";
     }
@@ -2781,11 +2782,98 @@ make_splash_window() {
       nout << "parse failure on barcolor " << _fparams.lookup_token("barcolor") << "\n";
     }
   }
+  if (_fparams.has_token("bar_bgcolor")) {
+    int r, g, b;
+    if (parse_color(r, g, b, _fparams.lookup_token("bar_bgcolor"))) {
+      _splash_window->set_bar_bgcolor(r, g, b);
+    } else {
+      nout << "parse failure on bar_bgcolor " << _fparams.lookup_token("bar_bgcolor") << "\n";
+    }
+  }
+  if (_fparams.has_token("bar_border")) {
+    int border = _fparams.lookup_token_int("bar_border");
+    _splash_window->set_bar_border(border);
+  }
+  if (_fparams.has_token("bar_bottom")) {
+    int bottom = _fparams.lookup_token_int("bar_bottom");
+    _splash_window->set_bar_bottom(bottom);
+  }
+  if (_fparams.has_token("bar_width")) {
+    string bar_width = _fparams.lookup_token("bar_width");
+    char *endptr;
+    long width = strtol(bar_width.c_str(), &endptr, 10);
+    bool percent = false;
+
+    if (*endptr == '%') {
+      percent = true;
+      ++endptr;
+    }
+    if (*endptr == '\0' && width >= 0 && width < 65536) {
+      _splash_window->set_bar_width((int)width, percent);
+    } else {
+      nout << "parse failure on bar_width " << bar_width << "\n";
+    }
+  }
+  if (_fparams.has_token("bar_height")) {
+    string bar_height = _fparams.lookup_token("bar_height");
+    char *endptr;
+    long height = strtol(bar_height.c_str(), &endptr, 10);
+    bool percent = false;
+
+    if (*endptr == '%') {
+      percent = true;
+      ++endptr;
+    }
+    if (*endptr == '\0' && height >= 0 && height < 65536) {
+      _splash_window->set_bar_height((int)height, percent);
+    } else {
+      nout << "parse failure on bar_height " << bar_height << "\n";
+    }
+  }
+  if (_fparams.has_token("font_family")) {
+    string family = _fparams.lookup_token("font_family");
+    _splash_window->set_font_family(family);
+  }
+  if (_fparams.has_token("font_size")) {
+    int size = _fparams.lookup_token_int("font_size");
+    _splash_window->set_font_size(size);
+  }
+  if (_fparams.has_token("font_style")) {
+    string style = _fparams.lookup_token("font_style");
+
+    if (style == "normal") {
+      _splash_window->set_font_style(P3DSplashWindow::FS_normal);
+    } else if (style == "oblique") {
+      _splash_window->set_font_style(P3DSplashWindow::FS_oblique);
+    } else if (style == "italic") {
+      _splash_window->set_font_style(P3DSplashWindow::FS_italic);
+    } else {
+      nout << "parse_failure on font_style " << style << "\n";
+    }
+  }
+  if (_fparams.has_token("font_weight")) {
+    string weight = _fparams.lookup_token("font_weight");
+
+    if (weight == "normal") {
+      _splash_window->set_font_weight(400);
+    } else if (weight == "bold") {
+      _splash_window->set_font_weight(700);
+    } else if (weight == "bolder") {
+      _splash_window->set_font_weight(700);
+    } else if (weight == "lighter") {
+      _splash_window->set_font_weight(100);
+    } else if (weight.size() == 3 &&
+               weight[0] >= '1' && weight[0] <= '9' &&
+               weight[1] == '0' && weight[2] == '0') {
+      _splash_window->set_font_weight(((int)weight[0] - 48) * 100);
+    } else {
+      nout << "parse_failure on font_weight " << weight << "\n";
+    }
+  }
 
   _splash_window->set_wparams(_wparams);
   _splash_window->set_install_label(_install_label);
-  
-    
+
   P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
 
   // Go get the required images.

+ 12 - 0
direct/src/plugin/p3dInstanceManager.I

@@ -111,6 +111,18 @@ get_root_dir() const {
   return _root_dir;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: P3DInstanceManager::get_start_dir
+//       Access: Public
+//  Description: Returns the directory that the .p3d file should be
+//               mounted to and run from.  This is usually the
+//               "start" subdirectory of the root_dir.
+////////////////////////////////////////////////////////////////////
+inline const string &P3DInstanceManager::
+get_start_dir() const {
+  return _start_dir;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: P3DInstanceManager::get_platform
 //       Access: Public

+ 23 - 6
direct/src/plugin/p3dInstanceManager.cxx

@@ -201,7 +201,8 @@ initialize(int api_version, const string &contents_filename,
            const string &platform, const string &log_directory,
            const string &log_basename, bool trusted_environment,
            bool console_environment,
-           const string &root_dir, const string &host_dir) {
+           const string &root_dir, const string &host_dir,
+           const string &start_dir) {
   _api_version = api_version;
   _host_url = host_url;
   _verify_contents = verify_contents;
@@ -258,14 +259,22 @@ initialize(int api_version, const string &contents_filename,
 
     // TODO: Linux multiplatform support.  Just add the
     // appropriate platform strings to _supported_platforms.
+  } else {
+    nout << "Platform string was set by plugin to " << _platform << "\n";
   }
 
   if (_supported_platforms.empty()) {
+    // Hack for older plug-ins, which should still remain compatible with
+    // newer versions of the runtime distribution.
+    if (_platform == "win32") {
+      _supported_platforms.push_back("win_i386");
+    }
+
     // We always support at least the specific platform on which we're
     // running.
     _supported_platforms.push_back(_platform);
   }
-      
+
 #ifdef P3D_PLUGIN_LOG_DIRECTORY
   if (_log_directory.empty()) {
     _log_directory = P3D_PLUGIN_LOG_DIRECTORY;
@@ -290,12 +299,18 @@ initialize(int api_version, const string &contents_filename,
   } else {
     _root_dir = root_dir;
   }
-  
+
   _host_dir = host_dir;
 
+  if (start_dir.empty()) {
+    _start_dir = _root_dir + "/start";
+  } else {
+    _start_dir = start_dir;
+  }
+
   // Allow the caller (e.g. panda3d.exe) to specify a log directory.
   // Or, allow the developer to compile one in.
-  
+  //
   // Failing that, we write logfiles to Panda3D/log.
   if (_log_directory.empty()) {
     _log_directory = _root_dir + "/log";
@@ -1507,8 +1522,10 @@ create_runtime_environment() {
 
   // Make the certificate directory.
   _certs_dir = _root_dir + "/certs";
-  if (!mkdir_complete(_certs_dir, nout)) {
-    nout << "Couldn't mkdir " << _certs_dir << "\n";
+  if (!get_trusted_environment()) {
+    if (!mkdir_complete(_certs_dir, nout)) {
+      nout << "Couldn't mkdir " << _certs_dir << "\n";
+    }
   }
 
   _created_runtime_environment = true;

+ 4 - 1
direct/src/plugin/p3dInstanceManager.h

@@ -59,7 +59,8 @@ public:
                   bool trusted_environment,
                   bool console_environment,
                   const string &root_dir = "",
-                  const string &host_dir = "");
+                  const string &host_dir = "",
+                  const string &start_dir = "");
 
   inline bool is_initialized() const;
   inline void reconsider_runtime_environment();
@@ -69,6 +70,7 @@ public:
   inline int get_api_version() const;
   inline const string &get_host_url() const;
   inline const string &get_root_dir() const;
+  inline const string &get_start_dir() const;
   inline const string &get_platform() const;
   inline const string &get_temp_directory() const;
   inline const string &get_log_directory() const;
@@ -173,6 +175,7 @@ private:
   string _host_url;
   string _root_dir;
   string _host_dir;
+  string _start_dir;
   string _certs_dir;
   P3D_verify_contents _verify_contents;
   string _platform;

+ 98 - 70
direct/src/plugin/p3dOsxSplashWindow.cxx

@@ -36,6 +36,7 @@ P3DOsxSplashWindow::
 P3DOsxSplashWindow(P3DInstance *inst, bool make_visible) : 
   P3DSplashWindow(inst, make_visible)
 {
+  _font_attribs = NULL;
   _install_progress = 0;
   _progress_known = true;
   _received_data = 0;
@@ -59,6 +60,9 @@ P3DOsxSplashWindow::
     DisposeWindow(_toplevel_window);
     _toplevel_window = NULL;
   }
+  if (_font_attribs != NULL) {
+    CFRelease(_font_attribs);
+  }
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -123,6 +127,41 @@ set_wparams(const P3DWindowParams &wparams) {
       }
     }
   }
+
+  // Determine the attributes of the font we're going to use.
+  int symbolic = 0;
+  if (_font_style != FS_normal) {
+    symbolic |= kCTFontItalicTrait;
+  }
+  if (_font_weight > 500) {
+    symbolic |= kCTFontBoldTrait;
+  }
+  CFNumberRef symbolic_ref = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &symbolic);
+
+  CFStringRef traits_keys[1] = { kCTFontSymbolicTrait };
+  CFTypeRef traits_values[1] = { symbolic_ref };
+  CFDictionaryRef traits = CFDictionaryCreate(kCFAllocatorDefault, (const void **)&traits_keys, (const void **)&traits_values, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+
+  CFStringRef family = CFStringCreateWithCString(NULL, _font_family.c_str(), kCFStringEncodingUTF8);
+
+  CFStringRef attribs_keys[2] = { kCTFontFamilyNameAttribute, kCTFontTraitsAttribute };
+  CFTypeRef attribs_values[2] = { family, traits };
+  CFDictionaryRef attribs = CFDictionaryCreate(kCFAllocatorDefault, (const void **)&attribs_keys, (const void **)&attribs_values, 2, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+
+  // Create the font object.
+  CTFontDescriptorRef font_desc = CTFontDescriptorCreateWithAttributes(attribs);
+  CTFontRef font = CTFontCreateWithFontDescriptor(font_desc, _font_size, NULL);
+
+  CFStringRef keys[1] = { kCTFontAttributeName };
+  CFTypeRef values[1] = { font };
+  _font_attribs = CFDictionaryCreate(kCFAllocatorDefault, (const void **)&keys, (const void **)&values, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+
+  CFRelease(font);
+  CFRelease(font_desc);
+  CFRelease(attribs);
+  CFRelease(traits);
+  CFRelease(family);
+  CFRelease(symbolic_ref);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -171,6 +210,9 @@ set_image_filename(const string &image_filename, ImagePlacement image_placement)
   case IP_button_click:
     load_image(_button_click_image, image_filename);
     break;
+
+  default:
+    return;
   }
 
   refresh();
@@ -333,10 +375,8 @@ paint_window_osx_cgcontext(CGContextRef context) {
   CGColorRef bg = CGColorCreate(rgb_space, bg_components);
 
   CGRect region = { { 0, 0 }, { _win_width, _win_height } };
-  CGContextBeginPath(context);
   CGContextSetFillColorWithColor(context, bg);
-  CGContextAddRect(context, region);
-  CGContextFillPath(context);
+  CGContextFillRect(context, region);
 
   CGColorRelease(bg);
   CGColorSpaceRelease(rgb_space);
@@ -445,8 +485,6 @@ handle_event_osx_event_record(const P3D_event_data &event) {
 ////////////////////////////////////////////////////////////////////
 bool P3DOsxSplashWindow::
 handle_event_osx_cocoa(const P3D_event_data &event) {
-  bool retval = false;
-
   assert(event._event_type == P3D_ET_osx_cocoa);
   const P3DCocoaEvent &ce = event._event._osx_cocoa._event;
 
@@ -455,33 +493,31 @@ handle_event_osx_cocoa(const P3D_event_data &event) {
     if (_visible) {
       CGContextRef context = ce.data.draw.context;
       paint_window_osx_cgcontext(context);
-      retval = true;
+      return true;
+    } else {
+      return false;
     }
-    break;
 
   case P3DCocoaEventMouseDown:
     set_mouse_data((int)ce.data.mouse.pluginX, (int)ce.data.mouse.pluginY, true);
-    retval = true;
-    break;
+    return true;
 
   case P3DCocoaEventMouseUp:
     set_mouse_data((int)ce.data.mouse.pluginX, (int)ce.data.mouse.pluginY, false);
-    retval = true;
-    break;
+    return true;
 
   case P3DCocoaEventMouseMoved:
   case P3DCocoaEventMouseDragged:
     set_mouse_data((int)ce.data.mouse.pluginX, (int)ce.data.mouse.pluginY, _mouse_down);
-    retval = true;
-    break;
+    return true;
 
   case P3DCocoaEventFocusChanged:
     _mouse_active = (ce.data.focus.hasFocus != 0);
-    retval = true;
-    break;
-  }
+    return true;
 
-  return retval;
+  default:
+    return false;
+  }
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -612,42 +648,33 @@ paint_image(CGContextRef context, const OsxImageData &image) {
 ////////////////////////////////////////////////////////////////////
 void P3DOsxSplashWindow::
 paint_progress_bar(CGContextRef context) {
-  // Get some colors we'll need.  We can't just use
-  // CGColorGetConstantColor(), since that requires 10.5.
   CGFloat fg_components[] = { _fgcolor_r / 255.0f, _fgcolor_g / 255.0f, _fgcolor_b / 255.0f, 1 };
   CGFloat bg_components[] = { _bgcolor_r / 255.0f, _bgcolor_g / 255.0f, _bgcolor_b / 255.0f, 1 };
   CGFloat bar_components[] = { _barcolor_r / 255.0f, _barcolor_g / 255.0f, _barcolor_b / 255.0f, 1 };
+  CGFloat bar_bg_components[] = { _bar_bgcolor_r / 255.0f, _bar_bgcolor_g / 255.0f, _bar_bgcolor_b / 255.0f, 1 };
   CGColorSpaceRef rgb_space = CGColorSpaceCreateDeviceRGB();
   CGColorRef fg = CGColorCreate(rgb_space, fg_components);
   CGColorRef bg = CGColorCreate(rgb_space, bg_components);
   CGColorRef bar = CGColorCreate(rgb_space, bar_components);
+  CGColorRef bar_bg = CGColorCreate(rgb_space, bar_bg_components);
 
+  // Get the proper placement for the progress bar.
   int bar_x, bar_y, bar_width, bar_height;
   get_bar_placement(bar_x, bar_y, bar_width, bar_height);
 
-  // We offset the bar by half a pixel, so we'll be drawing the
-  // one-pixel line through the middle of a pixel, and it won't try to
-  // antialias itself into a half-black two-pixel line.
-  float bar_xf = bar_x + 0.5;
-  float bar_yf = bar_y - 0.5;
-
-  CGRect bar_rect = { { bar_xf, bar_yf }, { bar_width, bar_height } };
+  CGRect bar_rect = { { bar_x, bar_y }, { bar_width, bar_height } };
 
   // Clear the entire progress bar to white (or the background color).
-  CGContextBeginPath(context);
-  CGContextSetFillColorWithColor(context, bg);
-  CGContextAddRect(context, bar_rect);
-  CGContextFillPath(context);
+  CGContextSetFillColorWithColor(context, bar_bg);
+  CGContextFillRect(context, bar_rect);
 
   // Draw the interior of the progress bar in blue (or the bar color).
   if (_progress_known) {
     int progress_width = (int)(bar_width * _install_progress + 0.5);
     if (progress_width != 0) {
-      CGRect prog = { { bar_xf, bar_yf }, { progress_width, bar_height } };
-      CGContextBeginPath(context);
+      CGRect prog = { { bar_x, bar_y }, { progress_width, bar_height } };
       CGContextSetFillColorWithColor(context, bar);
-      CGContextAddRect(context, prog);
-      CGContextFillPath(context);
+      CGContextFillRect(context, prog);
     }
   } else {
     // Progress is unknown.  Draw a moving block, not a progress bar
@@ -660,57 +687,58 @@ paint_progress_bar(CGContextRef context) {
       progress = block_travel * 2 - progress;
     }
 
-    CGRect prog = { { bar_xf + progress, bar_yf }, { block_width, bar_height } };
-    CGContextBeginPath(context);
+    CGRect prog = { { bar_x + progress, bar_y }, { block_width, bar_height } };
     CGContextSetFillColorWithColor(context, bar);
-    CGContextAddRect(context, prog);
-    CGContextFillPath(context);
+    CGContextFillRect(context, prog);
   }
-    
+
   // Draw the black stroke around the progress bar.
-  CGContextBeginPath(context);
-  CGContextSetLineWidth(context, 1);
-  CGContextSetStrokeColorWithColor(context, fg);
-  CGContextAddRect(context, bar_rect);
-  CGContextStrokePath(context);
+  if (_bar_border > 0) {
+    // We offset the border by half a pixel, so we'll be drawing the
+    // one-pixel line through the middle of a pixel, and it won't try to
+    // antialias itself into a half-black two-pixel line.
+    CGRect border_rect = { { bar_x - 0.5, bar_y - 0.5 },
+                           { bar_width + 1, bar_height + 1 } };
 
-  if (!_install_label.empty()) {
-    // Now draw the install_label right above it.
+    CGContextBeginPath(context);
+    CGContextSetLineWidth(context, 1);
+    CGContextSetStrokeColorWithColor(context, fg);
+
+    for (int i = 0; i < _bar_border; ++i) {
+      CGContextAddRect(context, border_rect);
+      border_rect.origin.x -= 1;
+      border_rect.origin.y -= 1;
+      border_rect.size.width += 2;
+      border_rect.size.height += 2;
+    }
+    CGContextStrokePath(context);
+  }
 
+  if (!_install_label.empty()) {
     // Need to invert the text so it won't be upside-down.
     CGAffineTransform text_xform = CGAffineTransformMakeScale(1, -1);
     CGContextSetTextMatrix(context, text_xform);
 
-    // Choose a suitable font.
-    float text_height = 15.0;
-    CGContextSelectFont(context, "Helvetica", text_height, kCGEncodingMacRoman);
-
-    // Measure the text, for centering.
-    CGContextSetTextPosition(context, 0, 0);
-    CGContextSetTextDrawingMode(context, kCGTextInvisible);
-    CGContextShowText(context, _install_label.data(), _install_label.length());
-    CGPoint end_point = CGContextGetTextPosition(context);
-    float text_width = end_point.x;
-
-    int text_x = (int)(_win_width - text_width) / 2;
-    int text_y = (int)(bar_y - text_height * 1.5);
-
-    // Clear the rectangle behind the text to bg.
-    CGRect text_rect = { { text_x - 2, text_y - 2 }, { text_width + 4, text_height + 4 } };
+    // Now draw the install_label right above it.
+    CFStringRef string = CFStringCreateWithCString(NULL, _install_label.c_str(), kCFStringEncodingUTF8);
+    CFAttributedStringRef attr_string = CFAttributedStringCreate(NULL, string, _font_attribs);
+    CTLineRef line = CTLineCreateWithAttributedString(attr_string);
 
-    CGContextBeginPath(context);
-    CGContextAddRect(context, text_rect);
-    CGContextSetFillColorWithColor(context, bg);
-    CGContextFillPath(context);
+    // Determine the placement based on the size of the text.
+    CGRect bounds = CTLineGetImageBounds(line, context);
+    float text_x = (_win_width - bounds.size.width) / 2.0f - bounds.origin.x;
+    float text_y = bar_y + bounds.origin.y - 4 - _bar_border;
 
     // And finally, draw the text.
-    CGContextSetTextDrawingMode(context, kCGTextFill);
-    CGContextSetFillColorWithColor(context, fg);
+    CGContextSetTextPosition(context, text_x, text_y);
+    CTLineDraw(line, context);
 
-    CGContextShowTextAtPoint(context, text_x, text_y + text_height, 
-                             _install_label.data(), _install_label.length());
+    CFRelease(line);
+    CFRelease(attr_string);
+    CFRelease(string);
   }
 
+  CGColorRelease(bar_bg);
   CGColorRelease(bar);
   CGColorRelease(bg);
   CGColorRelease(fg);

+ 2 - 0
direct/src/plugin/p3dOsxSplashWindow.h

@@ -83,6 +83,8 @@ private:
   OsxImageData _button_rollover_image;
   OsxImageData _button_click_image;
 
+  CFDictionaryRef _font_attribs;
+
   string _install_label;
   double _install_progress;
   bool _progress_known;

+ 4 - 4
direct/src/plugin/p3dPythonMain.cxx

@@ -150,10 +150,10 @@ main(int argc, char *argv[]) {
     }
   }
 
-  if (!run_p3dpython(program_name, archive_file, input_handle, output_handle, 
-                     NULL, interactive_console)) {
+  int status = run_p3dpython(program_name, archive_file, input_handle,
+                             output_handle, NULL, interactive_console);
+  if (status != 0) {
     cerr << "Failure on startup.\n";
-    return 1;
   }
-  return 0;
+  return status;
 }

+ 174 - 52
direct/src/plugin/p3dPythonRun.cxx

@@ -21,6 +21,11 @@
 
 #include "py_panda.h"
 
+extern "C" {
+  // This has been compiled-in by the build system, if all is well.
+  extern struct _frozen _PyImport_FrozenModules[];
+};
+
 // There is only one P3DPythonRun object in any given process space.
 // Makes the statics easier to deal with, and we don't need multiple
 // instances of this thing.
@@ -54,16 +59,26 @@ P3DPythonRun(const char *program_name, const char *archive_file,
   _interactive_console = interactive_console;
 
   if (program_name != NULL) {
+#if PY_MAJOR_VERSION >= 3
+    // Python 3 case: we have to convert it to a wstring.
+    TextEncoder enc;
+    enc.set_text(program_name);
+    _program_name = enc.get_wtext();
+#else
+    // Python 2 is happy with a regular string.
     _program_name = program_name;
+#endif
   }
   if (archive_file != NULL) {
     _archive_file = Filename::from_os_specific(archive_file);
   }
 
   _py_argc = 1;
-  _py_argv = (char **)malloc(2 * sizeof(char *));
-
+#if PY_MAJOR_VERSION >= 3
+  _py_argv[0] = (wchar_t *)_program_name.c_str();
+#else
   _py_argv[0] = (char *)_program_name.c_str();
+#endif
   _py_argv[1] = NULL;
 
 #ifdef NDEBUG
@@ -75,13 +90,30 @@ P3DPythonRun(const char *program_name, const char *archive_file,
   // Turn off the automatic load of site.py at startup.
   extern int Py_NoSiteFlag;
   Py_NoSiteFlag = 1;
+  Py_NoUserSiteDirectory = 1;
+
+  // Tell Python not to write bytecode files for loaded modules.
+  Py_DontWriteBytecodeFlag = 1;
+
+  // Prevent Python from complaining about finding the standard modules.
+  Py_FrozenFlag = 1;
+
+  // This contains the modules we need in order to call Py_Initialize,
+  // as well as the VFSImporter.
+  PyImport_FrozenModules = _PyImport_FrozenModules;
 
   // Initialize Python.  It appears to be important to do this before
   // we open the pipe streams and spawn the thread, below.
+#if PY_MAJOR_VERSION >= 3
+  Py_SetProgramName((wchar_t *)_program_name.c_str());
+  Py_SetPythonHome((wchar_t *)L"");
+#else
   Py_SetProgramName((char *)_program_name.c_str());
+  Py_SetPythonHome((char *)"");
+#endif
   Py_Initialize();
   PyEval_InitThreads();
-  PySys_SetArgv(_py_argc, _py_argv);
+  PySys_SetArgvEx(_py_argc, _py_argv, 0);
 
   // Open the error output before we do too much more.
   if (log_pathname != NULL && *log_pathname != '\0') {
@@ -137,8 +169,10 @@ P3DPythonRun::
 //       Access: Public
 //  Description: Runs the embedded Python process.  This method does
 //               not return until the plugin is ready to exit.
+//
+//               Returns the exit status, which will be 0 on success.
 ////////////////////////////////////////////////////////////////////
-bool P3DPythonRun::
+int P3DPythonRun::
 run_python() {
 #if defined(_WIN32) && defined(USE_DEBUG_PYTHON)
   // On Windows, in a debug build, we have to preload sys.dll_suffix =
@@ -154,36 +188,42 @@ run_python() {
   // setting __path__ of frozen modules properly.
   PyObject *panda3d_module = PyImport_AddModule("panda3d");
   if (panda3d_module == NULL) {
-    nout << "Failed to create panda3d module:\n";
+    nout << "Failed to add panda3d module:\n";
     PyErr_Print();
-    return false;
+    return 1;
   }
 
-  // Set the __path__ such that it can find panda3d/core.pyd, etc.
+  // Set the __path__ such that it can find panda3d/_core.pyd, etc.
   Filename panda3d_dir(dir, "panda3d");
   string dir_str = panda3d_dir.to_os_specific();
-  PyObject *panda3d_dict = PyModule_GetDict(panda3d_module);
-  PyObject *panda3d_path = Py_BuildValue("[s#]", dir_str.data(), dir_str.length());
-  PyDict_SetItemString(panda3d_dict, "__path__", panda3d_path);
-  Py_DECREF(panda3d_path);
-
-  // Now we can load _vfsimporter.pyd.  Since this is a magic frozen
-  // pyd, importing it automatically makes all of its frozen contents
-  // available to import as well.
-  PyObject *vfsimporter = PyImport_ImportModule("_vfsimporter");
-  if (vfsimporter == NULL) {
-    nout << "Failed to import _vfsimporter:\n";
-    PyErr_Print();
-    return false;
-  }
-  Py_DECREF(vfsimporter);
+  PyModule_AddObject(panda3d_module, "__path__", Py_BuildValue("[s#]", dir_str.data(), dir_str.length()));
+  PyModule_AddStringConstant(panda3d_module, "__package__", "panda3d");
 
-  // And now we can import the VFSImporter module that was so defined.
-  PyObject *vfsimporter_module = PyImport_ImportModule("VFSImporter");
+  // Import the VFSImporter module that was frozen in.
+  PyObject *vfsimporter_module = PyImport_ImportModule("direct.showbase.VFSImporter");
   if (vfsimporter_module == NULL) {
     nout << "Failed to import VFSImporter:\n";
     PyErr_Print();
-    return false;
+    return 1;
+  }
+
+  // Now repair the "direct" and "direct.showbase" trees, which were
+  // presumably frozen along with the VFSImporter, by setting their
+  // __path__ such that we can still find the other direct modules.
+  Filename direct_dir(dir, "direct");
+  PyObject *direct_module = PyImport_AddModule("direct");
+  if (direct_module != NULL) {
+    dir_str = direct_dir.to_os_specific();
+    PyModule_AddObject(direct_module, "__path__", Py_BuildValue("[s#]", dir_str.data(), dir_str.length()));
+    PyModule_AddStringConstant(direct_module, "__package__", "direct");
+  }
+
+  PyObject *showbase_module = PyImport_AddModule("direct.showbase");
+  if (showbase_module != NULL) {
+    Filename showbase_dir(direct_dir, "showbase");
+    dir_str = showbase_dir.to_os_specific();
+    PyModule_AddObject(showbase_module, "__path__", Py_BuildValue("[s#]", dir_str.data(), dir_str.length()));
+    PyModule_AddStringConstant(showbase_module, "__package__", "direct.showbase");
   }
 
   // And register the VFSImporter.
@@ -191,7 +231,7 @@ run_python() {
   if (result == NULL) {
     nout << "Failed to call VFSImporter.register():\n";
     PyErr_Print();
-    return false;
+    return 1;
   }
   Py_DECREF(result);
   Py_DECREF(vfsimporter_module);
@@ -203,12 +243,12 @@ run_python() {
   PT(Multifile) mf = new Multifile;
   if (!mf->open_read(_archive_file)) {
     nout << "Could not read " << _archive_file << "\n";
-    return false;
+    return 1;
   }
   VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
   if (!vfs->mount(mf, dir, VirtualFileSystem::MF_read_only)) {
     nout << "Could not mount " << _archive_file << "\n";
-    return false;
+    return 1;
   }
 
   // And finally, we can import the startup module.
@@ -216,7 +256,7 @@ run_python() {
   if (app_runner_module == NULL) {
     nout << "Failed to import direct.p3d.AppRunner\n";
     PyErr_Print();
-    return false;
+    return 1;
   }
 
   // Get the pointers to the objects needed within the module.
@@ -224,7 +264,7 @@ run_python() {
   if (app_runner_class == NULL) {
     nout << "Failed to get AppRunner class\n";
     PyErr_Print();
-    return false;
+    return 1;
   }
 
   // Construct an instance of AppRunner.
@@ -232,47 +272,55 @@ run_python() {
   if (_runner == NULL) {
     nout << "Failed to construct AppRunner instance\n";
     PyErr_Print();
-    return false;
+    return 1;
   }
   Py_DECREF(app_runner_class);
 
+  // Import the JavaScript module.
+  PyObject *javascript_module = PyImport_ImportModule("direct.p3d.JavaScript");
+  if (javascript_module == NULL) {
+    nout << "Failed to import direct.p3d.JavaScript\n";
+    PyErr_Print();
+    return false;
+  }
+
   // Get the UndefinedObject class.
-  _undefined_object_class = PyObject_GetAttrString(app_runner_module, "UndefinedObject");
+  _undefined_object_class = PyObject_GetAttrString(javascript_module, "UndefinedObject");
   if (_undefined_object_class == NULL) {
     PyErr_Print();
-    return false;
+    return 1;
   }
 
   // And the "Undefined" instance.
-  _undefined = PyObject_GetAttrString(app_runner_module, "Undefined");
+  _undefined = PyObject_GetAttrString(javascript_module, "Undefined");
   if (_undefined == NULL) {
     PyErr_Print();
-    return false;
+    return 1;
   }
 
   // Get the ConcreteStruct class.
-  _concrete_struct_class = PyObject_GetAttrString(app_runner_module, "ConcreteStruct");
+  _concrete_struct_class = PyObject_GetAttrString(javascript_module, "ConcreteStruct");
   if (_concrete_struct_class == NULL) {
     PyErr_Print();
-    return false;
+    return 1;
   }
 
   // Get the BrowserObject class.
-  _browser_object_class = PyObject_GetAttrString(app_runner_module, "BrowserObject");
+  _browser_object_class = PyObject_GetAttrString(javascript_module, "BrowserObject");
   if (_browser_object_class == NULL) {
     PyErr_Print();
-    return false;
+    return 1;
   }
 
   // Get the global TaskManager.
   _taskMgr = PyObject_GetAttrString(app_runner_module, "taskMgr");
   if (_taskMgr == NULL) {
     PyErr_Print();
-    return false;
+    return 1;
   }
 
   Py_DECREF(app_runner_module);
-
+  Py_DECREF(javascript_module);
 
   // Construct a Python wrapper around our methods we need to expose to Python.
   static PyMethodDef p3dpython_methods[] = {
@@ -282,15 +330,28 @@ run_python() {
       "Send an asynchronous request to the plugin host" },
     { NULL, NULL, 0, NULL }        /* Sentinel */
   };
+
+#if PY_MAJOR_VERSION >= 3
+  static PyModuleDef p3dpython_module = {
+    PyModuleDef_HEAD_INIT,
+    "p3dpython",
+    NULL,
+    -1,
+    p3dpython_methods,
+    NULL, NULL, NULL, NULL
+  };
+  PyObject *p3dpython = PyModule_Create(&p3dpython_module);
+#else
   PyObject *p3dpython = Py_InitModule("p3dpython", p3dpython_methods);
+#endif
   if (p3dpython == NULL) {
     PyErr_Print();
-    return false;
+    return 1;
   }
   PyObject *request_func = PyObject_GetAttrString(p3dpython, "request_func");
   if (request_func == NULL) {
     PyErr_Print();
-    return false;
+    return 1;
   }
 
   // Now pass that func pointer back to our AppRunner instance, so it
@@ -298,7 +359,7 @@ run_python() {
   result = PyObject_CallMethod(_runner, (char *)"setRequestFunc", (char *)"N", request_func);
   if (result == NULL) {
     PyErr_Print();
-    return false;
+    return 1;
   }
   Py_DECREF(result);
 
@@ -318,7 +379,7 @@ run_python() {
   PyObject *check_comm = PyObject_GetAttrString(p3dpython, "check_comm");
   if (check_comm == NULL) {
     PyErr_Print();
-    return false;
+    return 1;
   }
 
   // Add it to the task manager.  We do this instead of constructing a
@@ -326,7 +387,7 @@ run_python() {
   result = PyObject_CallMethod(_taskMgr, (char *)"add", (char *)"Ns", check_comm, "check_comm");
   if (result == NULL) {
     PyErr_Print();
-    return false;
+    return 1;
   }
   Py_DECREF(result);
 
@@ -334,18 +395,48 @@ run_python() {
   // taskMgr.run()).
   PyObject *done = PyObject_CallMethod(_runner, (char *)"run", (char *)"");
   if (done == NULL) {
+    int status = 1;
+
     // An uncaught application exception, and not handled by
-    // appRunner.exceptionHandler.
-    PyErr_Print();
+    // appRunner.exceptionHandler.  If it is a SystemExit, extract
+    // the exit status that we should return.
+    if (PyErr_ExceptionMatches(PyExc_SystemExit)) {
+      PyObject *ptype, *ptraceback;
+      PyObject *value = NULL;
+      PyErr_Fetch(&ptype, &value, &ptraceback);
+
+      if (value != NULL && PyExceptionInstance_Check(value)) {
+        PyObject *code = PyObject_GetAttrString(value, "code");
+        if (code) {
+          Py_DECREF(value);
+          value = code;
+        }
+      }
+
+      if (value == NULL || value == Py_None) {
+        status = 0;
+#if PY_MAJOR_VERSION >= 3
+      } else if (PyLong_Check(value)) {
+        status = (int)PyLong_AsLong(value);
+#else
+      } else if (PyInt_Check(value)) {
+        status = (int)PyInt_AsLong(value);
+#endif
+      } else {
+        status = 1;
+      }
+    } else {
+      PyErr_Print();
+    }
 
     if (_interactive_console) {
       // Give an interactive user a chance to explore the exception.
       run_interactive_console();
-      return true;
+      return 0;
     }
 
     // We're done.
-    return false;
+    return status;
   }
 
   // A normal exit from the taskManager.  We're presumably done.
@@ -355,7 +446,7 @@ run_python() {
     run_interactive_console();
   }
 
-  return true;
+  return 0;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -636,8 +727,11 @@ handle_pyobj_command(TiXmlElement *xcommand, bool needs_response,
           result = PyBool_FromLong(PyObject_IsTrue(obj));
 
         } else if (strcmp(method_name, "__int__") == 0) {
+#if PY_MAJOR_VERSION >= 3
+          result = PyNumber_Long(obj);
+#else
           result = PyNumber_Int(obj);
-
+#endif
         } else if (strcmp(method_name, "__float__") == 0) {
           result = PyNumber_Float(obj);
 
@@ -1502,10 +1596,12 @@ pyobj_to_xml(PyObject *value) {
     xvalue->SetAttribute("type", "bool");
     xvalue->SetAttribute("value", PyObject_IsTrue(value));
 
+#if PY_MAJOR_VERSION < 3
   } else if (PyInt_Check(value)) {
     // A plain integer value.
     xvalue->SetAttribute("type", "int");
     xvalue->SetAttribute("value", PyInt_AsLong(value));
+#endif
 
   } else if (PyLong_Check(value)) {
     // A long integer value.  This gets converted either as an integer
@@ -1527,9 +1623,21 @@ pyobj_to_xml(PyObject *value) {
     xvalue->SetAttribute("type", "float");
     xvalue->SetDoubleAttribute("value", PyFloat_AsDouble(value));
 
+
   } else if (PyUnicode_Check(value)) {
     // A unicode value.  Convert to utf-8 for the XML encoding.
     xvalue->SetAttribute("type", "string");
+
+#if PY_MAJOR_VERSION >= 3
+    // In Python 3, there are only unicode strings, and there is a
+    // handy function for getting the UTF-8 encoded version.
+    Py_ssize_t length = 0;
+    char *buffer = PyUnicode_AsUTF8AndSize(value, &length);
+    if (buffer != NULL) {
+      string str(buffer, length);
+      xvalue->SetAttribute("value", str);
+    }
+#else
     PyObject *as_str = PyUnicode_AsUTF8String(value);
     if (as_str != NULL) {
       char *buffer;
@@ -1563,6 +1671,7 @@ pyobj_to_xml(PyObject *value) {
       }
       Py_DECREF(ustr);
     }
+#endif  // PY_MAJOR_VERSION
 
   } else if (PyTuple_Check(value)) {
     // An immutable sequence.  Pass it as a concrete.
@@ -1601,7 +1710,11 @@ pyobj_to_xml(PyObject *value) {
             PyObject *as_str;
             if (PyUnicode_Check(a)) {
               // The key is a unicode value.
+#if PY_MAJOR_VERSION >= 3
+              as_str = a;
+#else
               as_str = PyUnicode_AsUTF8String(a);
+#endif
             } else {
               // The key is a string value or something else.  Make it
               // a string.
@@ -1609,7 +1722,12 @@ pyobj_to_xml(PyObject *value) {
             }
             char *buffer;
             Py_ssize_t length;
+#if PY_MAJOR_VERSION >= 3
+            buffer = PyUnicode_AsUTF8AndSize(as_str, &length);
+            if (buffer != NULL) {
+#else
             if (PyString_AsStringAndSize(as_str, &buffer, &length) != -1) {
+#endif
               string str(buffer, length);
               xitem->SetAttribute("key", str);
             }
@@ -1694,7 +1812,11 @@ xml_to_pyobj(TiXmlElement *xvalue) {
   } else if (strcmp(type, "int") == 0) {
     int value;
     if (xvalue->QueryIntAttribute("value", &value) == TIXML_SUCCESS) {
+#if PY_MAJOR_VERSION >= 3
+      return PyLong_FromLong(value);
+#else
       return PyInt_FromLong(value);
+#endif
     }
 
   } else if (strcmp(type, "float") == 0) {

+ 8 - 3
direct/src/plugin/p3dPythonRun.h

@@ -71,7 +71,7 @@ public:
                const char *log_pathname, bool interactive_console);
   ~P3DPythonRun();
 
-  bool run_python();
+  int run_python();
 
   void set_window_open(P3DCInstance *inst, bool is_open);
   void request_keyboard_focus(P3DCInstance *inst);
@@ -159,10 +159,15 @@ private:
 
   int _session_id;
 
-  string _program_name;
   Filename _archive_file;
   int _py_argc;
-  char **_py_argv;
+#if PY_MAJOR_VERSION >= 3
+  wchar_t *_py_argv[2];
+  wstring _program_name;
+#else
+  char *_py_argv[2];
+  string _program_name;
+#endif
   bool _interactive_console;
 
   PyObject *_runner;

+ 51 - 15
direct/src/plugin/p3dSession.cxx

@@ -100,6 +100,8 @@ void P3DSession::
 shutdown() {
   set_failed();
 
+  int exit_code = 0;
+
   if (_p3dpython_started) {
     // Tell the process we're going away.
     TiXmlDocument doc;
@@ -139,7 +141,14 @@ shutdown() {
         nout << "Force-killing python process.\n";
         TerminateProcess(_p3dpython_handle, 2);
       }
-      
+
+      DWORD dw_exit_code = 0;
+      if (!GetExitCodeProcess(_p3dpython_handle, &dw_exit_code)) {
+        nout << "GetExitCodeProcess failed, error: " << GetLastError() << "\n";
+      } else {
+        exit_code = (int)dw_exit_code;
+      }
+
       CloseHandle(_p3dpython_handle);
       _p3dpython_handle = INVALID_HANDLE_VALUE;
 
@@ -179,18 +188,23 @@ shutdown() {
         result = waitpid(_p3dpython_pid, &status, WNOHANG);
       }
       _p3dpython_pid = -1;
-      
+
       nout << "Python process has successfully stopped.\n";
       if (WIFEXITED(status)) {
-        nout << "  exited normally, status = "
-             << WEXITSTATUS(status) << "\n";
+        exit_code = WEXITSTATUS(status);
+        nout << "  exited normally, status = " << exit_code << "\n";
+
       } else if (WIFSIGNALED(status)) {
-        nout << "  signalled by " << WTERMSIG(status) << ", core = " 
+        nout << "  signalled by " << WTERMSIG(status) << ", core = "
              << WCOREDUMP(status) << "\n";
+
+        // This seems to be a popular shell convention.
+        exit_code = 128 + WTERMSIG(status);
+
       } else if (WIFSTOPPED(status)) {
         nout << "  stopped by " << WSTOPSIG(status) << "\n";
       }
-      
+
 #endif  // _WIN32
     }
 
@@ -211,6 +225,11 @@ shutdown() {
 
   // Close the pipe now.
   _pipe_read.close();
+
+  // If we had an exit status, pass it on to the runtime process.
+  if (exit_code != 0) {
+    _exit(exit_code);
+  }
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -717,7 +736,7 @@ start_p3dpython(P3DInstance *inst) {
     _keep_user_env = true;
   }
   if (!_keep_user_env) {
-    _start_dir = inst_mgr->get_root_dir() + "/start" + inst->get_start_dir_suffix();
+    _start_dir = inst_mgr->get_start_dir() + inst->get_start_dir_suffix();
     mkdir_complete(_start_dir, nout);
   }
   replace_slashes(_start_dir);
@@ -872,11 +891,13 @@ start_p3dpython(P3DInstance *inst) {
     // These are the enviroment variables we forward from the current
     // environment, if they are set.
     const char *keep[] = {
-      "HOME", "USER", 
+      "HOME", "USER",
 #ifdef _WIN32
       "SYSTEMROOT", "USERPROFILE", "COMSPEC",
       "SYSTEMDRIVE", "ALLUSERSPROFILE", "APPDATA", "COMMONPROGRAMFILES",
       "PROGRAMFILES", "WINDIR", "PROGRAMDATA", "USERDOMAIN",
+#else
+      "LANGUAGE", "LC_ALL", "LC_MESSAGES", "LANG",
 #endif
 #ifdef HAVE_X11
       "DISPLAY", "XAUTHORITY",
@@ -928,7 +949,11 @@ start_p3dpython(P3DInstance *inst) {
         const char *varc = var.c_str();
         bool found = false;
         for (int i = 0; dont_keep[i] != NULL && !found; ++i) {
+#ifdef _WIN32
+          found = (_stricmp(dont_keep[i], varc) == 0);
+#else
           found = (strcmp(dont_keep[i], varc) == 0);
+#endif
         }
         if (!found) {
           // This variable is OK, keep it.
@@ -1704,15 +1729,24 @@ posix_create_process() {
   // its process.  Report an error condition.
   nout << "Python process stopped immediately.\n";
   if (WIFEXITED(status)) {
-    nout << "  exited normally, status = "
-         << WEXITSTATUS(status) << "\n";
+    int code = WEXITSTATUS(status);
+
+    nout << "  exited normally, status = " << code << "\n";
+    if (code != 0) {
+      _exit(code);
+    }
+
   } else if (WIFSIGNALED(status)) {
-    nout << "  signalled by " << WTERMSIG(status) << ", core = " 
+    nout << "  signalled by " << WTERMSIG(status) << ", core = "
          << WCOREDUMP(status) << "\n";
+
+    // This seems to be a popular shell convention.
+    _exit(128 + WTERMSIG(status));
+
   } else if (WIFSTOPPED(status)) {
     nout << "  stopped by " << WSTOPSIG(status) << "\n";
   }
-  
+
   return -1;
 }
 #endif  // _WIN32
@@ -1794,10 +1828,12 @@ p3dpython_thread_run() {
     return;
   }
 
-  if (!run_p3dpython(libp3dpython.c_str(), _mf_filename.c_str(),
-                     _input_handle, _output_handle, _log_pathname.c_str(),
-                     _interactive_console)) {
+  int status = run_p3dpython(libp3dpython.c_str(), _mf_filename.c_str(),
+                             _input_handle, _output_handle, _log_pathname.c_str(),
+                             _interactive_console);
+  if (status != 0) {
     nout << "Failure on startup.\n";
+    _exit(status);
   }
 }
 

+ 186 - 6
direct/src/plugin/p3dSplashWindow.cxx

@@ -48,6 +48,19 @@ P3DSplashWindow(P3DInstance *inst, bool make_visible) :
   _barcolor_r = 0x6c;
   _barcolor_g = 0xa5;
   _barcolor_b = 0xe0;
+  _bar_bgcolor_r = _bgcolor_r;
+  _bar_bgcolor_g = _bgcolor_g;
+  _bar_bgcolor_b = _bgcolor_b;
+  _bar_border = 1;
+  _bar_bottom = 24;
+  _bar_width = 398;
+  _bar_height = 22;
+  _bar_width_ratio = 0.6;
+  _bar_height_ratio = 0.1;
+  _font_family = "Helvetica";
+  _font_size = 12;
+  _font_style = FS_normal;
+  _font_weight = FW_normal;
   _button_width = 0;
   _button_height = 0;
   _button_x = 0;
@@ -113,7 +126,7 @@ set_image_filename(const string &image_filename, ImagePlacement image_placement)
 
 ////////////////////////////////////////////////////////////////////
 //     Function: P3DSplashWindow::set_fgcolor
-//       Access: Public, Virtual
+//       Access: Public
 //  Description: Specifies the color that is used to display the text
 //               above the loading bar.
 //
@@ -129,7 +142,7 @@ set_fgcolor(int r, int g, int b) {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: P3DSplashWindow::set_bgcolor
-//       Access: Public, Virtual
+//       Access: Public
 //  Description: Specifies the solid color that is displayed behind
 //               the splash image, if any, or before the splash image
 //               is loaded.
@@ -146,7 +159,7 @@ set_bgcolor(int r, int g, int b) {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: P3DSplashWindow::set_barcolor
-//       Access: Public, Virtual
+//       Access: Public
 //  Description: Specifies the color that is used to fill the
 //               loading bar.
 //
@@ -160,6 +173,162 @@ set_barcolor(int r, int g, int b) {
   _barcolor_b = b;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: P3DSplashWindow::set_bar_bgcolor
+//       Access: Public
+//  Description: Specifies the solid color that is displayed behind
+//               the loading bar.
+//
+//               This may only be set before wparams is set.
+////////////////////////////////////////////////////////////////////
+void P3DSplashWindow::
+set_bar_bgcolor(int r, int g, int b) {
+  nout << "bar_bgcolor " << r << ", " << g << ", " << b << "\n";
+  _bar_bgcolor_r = r;
+  _bar_bgcolor_g = g;
+  _bar_bgcolor_b = b;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: P3DSplashWindow::set_bar_border
+//       Access: Public
+//  Description: Sets the width in pixels of the border around the
+//               loading bar, or 0 not to draw a bar at all.
+//
+//               This may only be set before wparams is set.
+////////////////////////////////////////////////////////////////////
+void P3DSplashWindow::
+set_bar_border(int border) {
+  nout << "bar_border " << border << "\n";
+  _bar_border = border;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: P3DSplashWindow::set_bar_bottom
+//       Access: Public
+//  Description: Sets the amount of background pixels between the
+//               bottom edge of the window and the bottom edge of
+//               the loading bar border.
+//
+//               This may only be set before wparams is set.
+////////////////////////////////////////////////////////////////////
+void P3DSplashWindow::
+set_bar_bottom(int bottom) {
+  nout << "bar_bottom " << bottom << "\n";
+  _bar_bottom = bottom;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: P3DSplashWindow::set_bar_width
+//       Access: Public
+//  Description: Sets the width of the loading bar.  If percent is
+//               true, it is interpreted as a percentage of the
+//               window width.  If false, it is interpreted as an
+//               absolute width in pixels.
+//
+//               This may only be set before wparams is set.
+////////////////////////////////////////////////////////////////////
+void P3DSplashWindow::
+set_bar_width(int width, bool percent) {
+  nout << "bar_width " << width;
+  if (percent) {
+    nout << '%';
+  }
+  nout << '\n';
+
+  if (percent) {
+    _bar_width = -1;
+    _bar_width_ratio = abs(width) / 100.0;
+  } else {
+    _bar_width = abs(width);
+    _bar_width_ratio = 1.0;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: P3DSplashWindow::set_bar_height
+//       Access: Public
+//  Description: Sets the height of the loading bar.  If percent is
+//               true, it is interpreted as a percentage of the
+//               window height.  If false, it is interpreted as an
+//               absolute height in pixels.
+//
+//               This may only be set before wparams is set.
+////////////////////////////////////////////////////////////////////
+void P3DSplashWindow::
+set_bar_height(int height, bool percent) {
+  nout << "bar_height " << height;
+  if (percent) {
+    nout << '%';
+  }
+  nout << '\n';
+
+  if (percent) {
+    _bar_height = -1;
+    _bar_height_ratio = abs(height) / 100.0;
+  } else {
+    _bar_height = abs(height);
+    _bar_height_ratio = 1.0;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: P3DSplashWindow::set_font_family
+//       Access: Public
+//  Description: Sets the font family of the text above the loading
+//               bar.
+//
+//               This may only be set before wparams is set.
+////////////////////////////////////////////////////////////////////
+void P3DSplashWindow::
+set_font_family(const string &family) {
+  nout << "font_family " << family << "\n";
+  _font_family = family;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: P3DSplashWindow::set_font_size
+//       Access: Public
+//  Description: Sets the font size in pixels of the text above the
+//               loading bar.  The default value is 12.
+//
+//               This may only be set before wparams is set.
+////////////////////////////////////////////////////////////////////
+void P3DSplashWindow::
+set_font_size(int size) {
+  nout << "font_size " << size << "\n";
+  _font_size = size;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: P3DSplashWindow::set_font_style
+//       Access: Public
+//  Description: Sets the font style of the text above the loading
+//               bar.
+//
+//               This may only be set before wparams is set.
+////////////////////////////////////////////////////////////////////
+void P3DSplashWindow::
+set_font_style(FontStyle style) {
+  nout << "font_style " << style << "\n";
+  _font_style = style;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: P3DSplashWindow::set_font_weight
+//       Access: Public
+//  Description: Sets the font weight of the text above the loading
+//               bar.  The default is FW_normal.  It should be
+//               a multiple of 100 in the range 100-900.
+//
+//               This may only be set before wparams is set.
+////////////////////////////////////////////////////////////////////
+void P3DSplashWindow::
+set_font_weight(int weight) {
+  nout << "font_weight " << weight << "\n";
+  _font_weight = weight;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: P3DSplashWindow::set_install_label
 //       Access: Public, Virtual
@@ -271,10 +440,21 @@ read_image_data(ImageData &image, string &data,
 void P3DSplashWindow::
 get_bar_placement(int &bar_x, int &bar_y,
                   int &bar_width, int &bar_height) {
-  bar_width = min((int)(_win_width * 0.6), 400);
-  bar_height = min((int)(_win_height * 0.1), 24);
+
+  bar_width = (int)(_win_width * _bar_width_ratio);
+  bar_height = (int)(_win_height * _bar_height_ratio);
+
+  if (_bar_width >= 0) {
+    bar_width = min(bar_width, _bar_width);
+  }
+  if (_bar_height >= 0) {
+    bar_height = min(bar_height, _bar_height);
+  }
+
+  // Horizontally center the bar, and set it at a fixed distance
+  // from the bottom edge of the splash window.
   bar_x = (_win_width - bar_width) / 2;
-  bar_y = (_win_height - bar_height * 2);
+  bar_y = _win_height - _bar_bottom - _bar_border - bar_height;
 }
 
 ////////////////////////////////////////////////////////////////////

+ 36 - 3
direct/src/plugin/p3dSplashWindow.h

@@ -51,12 +51,32 @@ public:
     IP_button_click,
     IP_none
   };
+  enum FontStyle {
+    FS_normal,
+    FS_italic,
+    FS_oblique
+  };
+  enum FontWeight {
+    FW_normal = 400,
+    FW_medium = 500,
+    FW_bold = 700,
+    FW_black = 900
+  };
 
   virtual void set_image_filename(const string &image_filename,
                                   ImagePlacement image_placement);
-  virtual void set_fgcolor(int r, int g, int b);
-  virtual void set_bgcolor(int r, int g, int b);
-  virtual void set_barcolor(int r, int g, int b);
+  void set_fgcolor(int r, int g, int b);
+  void set_bgcolor(int r, int g, int b);
+  void set_barcolor(int r, int g, int b);
+  void set_bar_bgcolor(int r, int g, int b);
+  void set_bar_border(int border);
+  void set_bar_bottom(int bottom);
+  void set_bar_width(int width, bool percent=false);
+  void set_bar_height(int height, bool percent=false);
+  void set_font_family(const string &family);
+  void set_font_size(int size);
+  void set_font_style(FontStyle style);
+  void set_font_weight(int weight);
   virtual void set_install_label(const string &install_label);
   virtual void set_install_progress(double install_progress,
                                     bool is_progress_known, size_t received_data);
@@ -105,6 +125,19 @@ protected:
   int _fgcolor_r, _fgcolor_g, _fgcolor_b;
   int _bgcolor_r, _bgcolor_g, _bgcolor_b;
   int _barcolor_r, _barcolor_g, _barcolor_b;
+  int _bar_bgcolor_r, _bar_bgcolor_g, _bar_bgcolor_b;
+  int _bar_border;
+
+private:
+  int _bar_bottom;
+  int _bar_width, _bar_height;
+  double _bar_width_ratio, _bar_height_ratio;
+
+protected:
+  string _font_family;
+  int _font_size;
+  FontStyle _font_style;
+  int _font_weight;
 
   // The region of the window for accepting button clicks.
   int _button_width, _button_height;

+ 34 - 8
direct/src/plugin/p3dWinSplashWindow.cxx

@@ -34,9 +34,11 @@ P3DWinSplashWindow(P3DInstance *inst, bool make_visible) :
   _thread = NULL;
   _thread_id = 0;
   _hwnd = NULL;
+  _font = NULL;
   _fg_brush = NULL;
   _bg_brush = NULL;
   _bar_brush = NULL;
+  _bar_bg_brush = NULL;
   _thread_running = false;
   _install_progress = 0.0;
   _progress_known = true;
@@ -497,9 +499,21 @@ make_window() {
     ShowWindow(_hwnd, SW_HIDE);
   }
 
+  // Load the requested font.
+  _font = CreateFontA(-_font_size, 0, 0, 0, _font_weight,
+                      (_font_style != FS_normal), FALSE, FALSE,
+                      ANSI_CHARSET, OUT_OUTLINE_PRECIS, CLIP_DEFAULT_PRECIS,
+                      CLEARTYPE_QUALITY, VARIABLE_PITCH, _font_family.c_str());
+
+  if (_font == NULL) {
+    nout << "CreateFont failed: " << GetLastError() << "\n";
+    _font = (HFONT)GetStockObject(ANSI_VAR_FONT);
+  }
+
   _fg_brush = CreateSolidBrush(RGB(_fgcolor_r, _fgcolor_g, _fgcolor_b));
   _bg_brush = CreateSolidBrush(RGB(_bgcolor_r, _bgcolor_g, _bgcolor_b));
   _bar_brush = CreateSolidBrush(RGB(_barcolor_r, _barcolor_g, _barcolor_b));
+  _bar_bg_brush = CreateSolidBrush(RGB(_bar_bgcolor_r, _bar_bgcolor_g, _bar_bgcolor_b));
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -635,6 +649,10 @@ close_window() {
     DeleteObject(_bar_brush);
     _bar_brush = NULL;
   }
+  if (_bar_bg_brush != NULL) {
+    DeleteObject(_bar_bg_brush);
+    _bar_bg_brush = NULL;
+  }
 
   _background_image.dump_image();
   _button_ready_image.dump_image();
@@ -794,7 +812,7 @@ paint_progress_bar(HDC dc) {
   RECT bar_rect = { bar_x, bar_y, bar_x + bar_width, bar_y + bar_height };
 
   // Clear the entire progress bar to white (or the background color).
-  FillRect(dc, &bar_rect, _bg_brush);
+  FillRect(dc, &bar_rect, _bar_bg_brush);
 
   // Draw the interior of the progress bar in blue (or the bar color).
   if (_drawn_progress_known) {
@@ -820,23 +838,31 @@ paint_progress_bar(HDC dc) {
   }
 
   // Now draw a black (or foreground) border around the progress bar.
-  FrameRect(dc, &bar_rect, _fg_brush);
+  if (_bar_border >= 0) {
+    RECT border_rect = bar_rect;
+
+    for (int i = 0; i < _bar_border; ++i) {
+      --border_rect.left;
+      --border_rect.top;
+      ++border_rect.right;
+      ++border_rect.bottom;
+      FrameRect(dc, &border_rect, _fg_brush);
+    }
+  }
 
   if (!_drawn_label.empty()) {
     // Now draw the install_label right above it.
-
     const char *text = _drawn_label.c_str();
-    HFONT font = (HFONT)GetStockObject(ANSI_VAR_FONT); 
 
     // Measure the text, for centering.
-    SelectObject(dc, font);
+    SelectObject(dc, _font);
     SIZE text_size;
     GetTextExtentPoint32(dc, text, strlen(text), &text_size);
 
     int text_width = text_size.cx;
     int text_height = text_size.cy;
     int text_x = (_win_width - text_width) / 2;
-    int text_y = bar_y - (int)(text_height * 1.5);
+    int text_y = bar_y - (int)(text_height * 1.5) - _bar_border;
 
     // Clear the rectangle behind the text to white.
     RECT text_rect = { text_x - 2, text_y - 2, text_x + text_width + 4, text_y + text_height + 4 };
@@ -856,7 +882,7 @@ paint_progress_bar(HDC dc) {
 //       Access: Private
 //  Description: The windows event-processing handler.
 ////////////////////////////////////////////////////////////////////
-LONG P3DWinSplashWindow::
+LRESULT P3DWinSplashWindow::
 window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
   switch (msg) {
   case WM_DESTROY:
@@ -937,7 +963,7 @@ window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
 //       Access: Private, Static
 //  Description: The windows event-processing handler, static version.
 ////////////////////////////////////////////////////////////////////
-LONG P3DWinSplashWindow::
+LRESULT P3DWinSplashWindow::
 st_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
   LONG_PTR self = GetWindowLongPtr(hwnd, GWLP_USERDATA);
   if (self == NULL) {

+ 4 - 2
direct/src/plugin/p3dWinSplashWindow.h

@@ -68,8 +68,8 @@ private:
   void paint_window(HDC dc);
   bool paint_image(HDC dc, const WinImageData &image, bool use_alpha);
   void paint_progress_bar(HDC dc);
-  LONG window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);
-  static LONG WINAPI st_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);
+  LRESULT window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);
+  static LRESULT WINAPI st_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);
 
 private:
   class WinImageData : public ImageData {
@@ -109,9 +109,11 @@ private:
   HANDLE _thread;
   DWORD _thread_id;
   HWND _hwnd;
+  HFONT _font;
   HBRUSH _fg_brush;
   HBRUSH _bg_brush;
   HBRUSH _bar_brush;
+  HBRUSH _bar_bg_brush;
 
   static bool _registered_window_class;
 };

+ 147 - 16
direct/src/plugin/p3dX11SplashWindow.cxx

@@ -47,11 +47,14 @@ P3DX11SplashWindow(P3DInstance *inst, bool make_visible) :
   _display = None;
   _window = None;
   _screen = 0;
+  _font = NULL;
   _graphics_context = None;
   _bar_context = None;
+  _bar_bg_context = None;
   _fg_pixel = -1;
   _bg_pixel = -1;
   _bar_pixel = -1;
+  _bar_bg_pixel = -1;
   _own_display = false;
   _install_progress = 0.0;
   _progress_known = true;
@@ -609,10 +612,15 @@ subprocess_run() {
       get_bar_placement(bar_x, bar_y, bar_width, bar_height);
 
       if (needs_draw_label) {
-        int text_width = _install_label.size() * 6;
-        int text_height = 10;
+        int direction, ascent, descent;
+        XCharStruct extents;
+        XTextExtents(_font, _install_label.c_str(), _install_label.size(),
+                     &direction, &ascent, &descent, &extents);
+
+        int text_width = extents.width;
+        int text_height = extents.ascent + extents.descent;
         int text_x = (_win_width - text_width) / 2;
-        int text_y = bar_y - 4;
+        int text_y = bar_y - descent - _bar_border - 4;
 
         XClearArea(_display, _window,
                    text_x - 2, text_y - text_height - 2,
@@ -624,25 +632,44 @@ subprocess_run() {
       }
 
       if (needs_redraw_progress) {
-        XClearArea(_display, _window,
-                   bar_x, bar_y, bar_width, bar_height, false);
-        XDrawRectangle(_display, _window, _graphics_context,
-                       bar_x, bar_y, bar_width, bar_height);
+        // Draw the bar background.
+        if (_bar_bg_context != None) {
+          XFillRectangle(_display, _window, _bar_bg_context,
+                         bar_x, bar_y, bar_width, bar_height);
+        } else {
+          XClearArea(_display, _window,
+                     bar_x, bar_y, bar_width, bar_height, false);
+        }
+
+        // Draw the border around the bar.
+        int border_x = bar_x - 1;
+        int border_y = bar_y - 1;
+        int border_width = bar_width + 1;
+        int border_height = bar_height + 1;
+
+        for (int i = 0; i < _bar_border; ++i) {
+          XDrawRectangle(_display, _window, _graphics_context,
+                         border_x, border_y, border_width, border_height);
+          border_x -= 1;
+          border_y -= 1;
+          border_width += 2;
+          border_height += 2;
+        }
         needs_update_progress = true;
         needs_redraw_progress = false;
       }
 
       if (needs_update_progress) {
         if (_progress_known) {
-          int progress_width = (int)((bar_width - 2) * _install_progress + 0.5);
+          int progress_width = (int)((bar_width - 1) * _install_progress + 0.5);
           XFillRectangle(_display, _window, _bar_context,
-                         bar_x + 1, bar_y + 1,
-                         progress_width + 1, bar_height - 1);
+                         bar_x, bar_y,
+                         progress_width + 1, bar_height);
         } else {
           // Progress is unknown.  Draw a moving block, not a progress bar
           // filling up.
           int block_width = (int)(bar_width * 0.1 + 0.5);
-          int block_travel = (bar_width - 2) - block_width;
+          int block_travel = (bar_width - 1) - block_width;
           int progress = (int)(_received_data * _unknown_progress_rate);
           progress = progress % (block_travel * 2);
           if (progress > block_travel) {
@@ -650,8 +677,8 @@ subprocess_run() {
           }
 
           XFillRectangle(_display, _window, _bar_context,
-                         bar_x + 1 + progress, bar_y + 1,
-                         block_width + 1, bar_height - 1);
+                         bar_x + progress, bar_y,
+                         block_width + 1, bar_height);
         }
         needs_update_progress = false;
       }
@@ -681,10 +708,13 @@ subprocess_run() {
       }
     }
 
-    if (input_ready) {
+    while (input_ready) {
+      // Empty the pipe of whatever is in it.
       receive_command();
+      input_ready = _pipe_read.has_gdata();
     }
 
+    // Sleep a good amount in order not to lock up the system.
     struct timespec req;
     req.tv_sec = 0;
     req.tv_nsec = 50000000;  // 50 ms
@@ -955,10 +985,82 @@ setup_gc() {
     return;
   }
 
-  XFontStruct* fs = XLoadQueryFont(_display, "6x13");
+  char style = 'r';
+  if (_font_style == FS_oblique) {
+    style = 'o';
+  } else if (_font_style == FS_italic) {
+    style = 'i';
+  }
+
+  // Determine the order at which to try the various weights.  From:
+  // https://developer.mozilla.org/en-US/docs/Web/CSS/font-weight
+  const char *const *try_weights;
+  const int num_weights = 7;
+
+  if (_font_weight > 700) {
+    static const char *const weights[] = {"black", "bold", "demibold", "semibold", "medium", "regular", "extralight"};
+    try_weights = weights;
+
+  } else if (_font_weight > 600) {
+    static const char *const weights[] = {"bold", "black", "demibold", "semibold", "medium", "regular", "extralight"};
+    try_weights = weights;
+
+  } else if (_font_weight > 500) {
+    static const char *const weights[] = {"demibold", "semibold", "bold", "black", "medium", "regular", "extralight"};
+    try_weights = weights;
+
+  } else if (_font_weight == 500) {
+    static const char *const weights[] = {"medium", "regular", "extralight", "demibold", "semibold", "bold", "black"};
+    try_weights = weights;
+
+  } else if (_font_weight >= 400) {
+    static const char *const weights[] = {"regular", "medium", "extralight", "demibold", "semibold", "bold", "black"};
+    try_weights = weights;
+
+  } else {
+    static const char *const weights[] = {"extralight", "regular", "medium", "demibold", "semibold", "bold", "black"};
+    try_weights = weights;
+  }
+
+  char font_name[1024];
+
+  // Go through the weights array to find the best matching font.
+  for (int i = 0; i < num_weights; ++i) {
+    const char *weight_name = try_weights[i];
+
+    // Compose the proper pattern for finding the desired font face.
+    snprintf(font_name, 1024, "-*-%s-%s-%c-normal--%d-*-*-*-*-*-iso8859-1",
+             _font_family.c_str(), weight_name, style, _font_size);
+
+    _font = XLoadQueryFont(_display, font_name);
+    if (_font != NULL) {
+      break;
+    }
+    nout << "Font " << font_name << " unavailable.\n";
+
+    if (style == 'i' || style == 'o') {
+      // If oblique is not found, try italic, and vice versa.
+      char style2 = 216 - style;
+      snprintf(font_name, 1024, "-*-%s-%s-%c-normal--%d-*-*-*-*-*-iso8859-1",
+               _font_family.c_str(), weight_name, style2, _font_size);
+
+      _font = XLoadQueryFont(_display, font_name);
+      if (_font != NULL) {
+        break;
+      }
+      nout << "Font " << font_name << " unavailable.\n";
+    }
+  }
+
+  if (_font != NULL) {
+    nout << "Loaded font " << font_name << "\n";
+  } else {
+    nout << "Using fallback font 6x13.\n";
+    _font = XLoadQueryFont(_display, "6x13");
+  }
 
   XGCValues gcval;
-  gcval.font = fs->fid;
+  gcval.font = _font->fid;
   gcval.function = GXcopy;
   gcval.plane_mask = AllPlanes;
   gcval.foreground = BlackPixel(_display, _screen);
@@ -988,6 +1090,24 @@ setup_gc() {
 
   _bar_context = XCreateGC(_display, _window,
     GCFont | GCFunction | GCPlaneMask | GCForeground | GCBackground, &gcval);
+
+  // And another for the background color of the bar.
+  if (_bar_bgcolor_r != _bgcolor_r || _bar_bgcolor_g != _bgcolor_g ||
+      _bar_bgcolor_b != _bgcolor_b) {
+    XColor bar_bg;
+    bar_bg.red = _bar_bgcolor_r * 0x101;
+    bar_bg.green = _bar_bgcolor_g * 0x101;
+    bar_bg.blue = _bar_bgcolor_b * 0x101;
+    bar_bg.flags = DoRed | DoGreen | DoBlue;
+
+    if (XAllocColor(_display, colormap, &bar_bg)) {
+      _bar_bg_pixel = bar_bg.pixel;
+      gcval.foreground = bar_bg.pixel;
+    }
+
+    _bar_bg_context = XCreateGC(_display, _window,
+      GCFont | GCFunction | GCPlaneMask | GCForeground | GCBackground, &gcval);
+  }
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -1013,6 +1133,17 @@ close_window() {
     XFreeColors(_display, colormap, &_bar_pixel, 1, 0);
   }
 
+  if (_bar_bg_context != None) {
+    if (_bar_bg_context != _graphics_context) {
+      XFreeGC(_display, _bar_bg_context);
+    }
+    _bar_bg_context = None;
+
+    // Also free the color we allocated.
+    Colormap colormap = DefaultColormap(_display, _screen);
+    XFreeColors(_display, colormap, &_bar_bg_pixel, 1, 0);
+  }
+
   if (_fg_pixel != -1) {
     Colormap colormap = DefaultColormap(_display, _screen);
     XFreeColors(_display, colormap, &_fg_pixel, 1, 0);

+ 6 - 3
direct/src/plugin/p3dX11SplashWindow.h

@@ -86,7 +86,7 @@ private:
   void setup_gc();
   void close_window();
 
-  void update_image(X11ImageData &image); 
+  void update_image(X11ImageData &image);
   void compose_image();
   bool scale_image(vector<unsigned char> &image0, int &image0_width, int &image0_height,
                    X11ImageData &image);
@@ -123,17 +123,20 @@ private:
   double _install_progress;
   bool _progress_known;
   size_t _received_data;
-  
+
   string _label_text;
 
   X11_Display *_display;
   int _screen;
+  XFontStruct *_font;
   GC _graphics_context;
   GC _bar_context;
+  GC _bar_bg_context;
   unsigned long _fg_pixel;
   unsigned long _bg_pixel;
   unsigned long _bar_pixel;
-  
+  unsigned long _bar_bg_pixel;
+
   X11_Window _window;
 };
 

+ 7 - 2
direct/src/plugin/p3d_plugin.cxx

@@ -39,7 +39,8 @@ P3D_initialize(int api_version, const char *contents_filename,
                const char *platform, const char *log_directory,
                const char *log_basename, bool trusted_environment,
                bool console_environment,
-               const char *root_dir, const char *host_dir) {
+               const char *root_dir, const char *host_dir,
+               const char *start_dir) {
   if (api_version < 10 || api_version > P3D_API_VERSION) {
     // Can't accept an incompatible version.
     return false;
@@ -89,12 +90,16 @@ P3D_initialize(int api_version, const char *contents_filename,
     host_dir = "";
   }
 
+  if (api_version < 17 || start_dir == NULL) {
+    start_dir = "";
+  }
+
   P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
   bool result = inst_mgr->initialize(api_version, contents_filename, host_url,
                                      verify_contents, platform,
                                      log_directory, log_basename,
                                      trusted_environment, console_environment,
-                                     root_dir, host_dir);
+                                     root_dir, host_dir, start_dir);
   RELEASE_LOCK(_api_lock);
   return result;
 }

+ 7 - 2
direct/src/plugin/p3d_plugin.h

@@ -53,7 +53,11 @@
 #endif
 
 #else  /* _WIN32 */
+#if defined(BUILDING_P3D_PLUGIN) && defined(__GNUC__)
+#define EXPCL_P3D_PLUGIN __attribute__((visibility("default")))
+#else
 #define EXPCL_P3D_PLUGIN
+#endif
 
 #endif  /* _WIN32 */
 
@@ -79,7 +83,7 @@ extern "C" {
    (below). This number will be incremented whenever there are changes
    to any of the interface specifications defined in this header
    file. */
-#define P3D_API_VERSION 16
+#define P3D_API_VERSION 17
 
 /************************ GLOBAL FUNCTIONS **************************/
 
@@ -161,7 +165,8 @@ P3D_initialize_func(int api_version, const char *contents_filename,
                     const char *platform,
                     const char *log_directory, const char *log_basename,
                     bool trusted_environment, bool console_environment,
-                    const char *root_dir, const char *host_dir);
+                    const char *root_dir, const char *host_dir,
+                    const char *start_dir);
 
 /* This function should be called to unload the core API.  It will
    release all internally-allocated memory and return the core API to

+ 7 - 6
direct/src/plugin/run_p3dpython.cxx

@@ -19,16 +19,17 @@
 //     Function: run_p3dpython
 //  Description: This externally-visible function is the main entry
 //               point to this DLL, and it starts the whole thing
-//               running.  Returns true on success, false on failure.
+//               running.  Returns the exit status, which will be
+//               0 on success, 1 or otherwise on failure.
 ////////////////////////////////////////////////////////////////////
-bool
+int
 run_p3dpython(const char *program_name, const char *archive_file,
-              FHandle input_handle, FHandle output_handle, 
+              FHandle input_handle, FHandle output_handle,
               const char *log_pathname, bool interactive_console) {
-  P3DPythonRun::_global_ptr = 
-    new P3DPythonRun(program_name, archive_file, input_handle, output_handle, 
+  P3DPythonRun::_global_ptr =
+    new P3DPythonRun(program_name, archive_file, input_handle, output_handle,
                      log_pathname, interactive_console);
-  bool result = P3DPythonRun::_global_ptr->run_python();
+  int result = P3DPythonRun::_global_ptr->run_python();
   delete P3DPythonRun::_global_ptr;
   P3DPythonRun::_global_ptr = NULL;
   return result;

+ 4 - 4
direct/src/plugin/run_p3dpython.h

@@ -26,14 +26,14 @@
 #define EXPCL_P3DPYTHON
 #endif
 
-typedef bool 
+typedef int
 run_p3dpython_func(const char *program_name, const char *archive_file,
-                   FHandle input_handle, FHandle output_handle, 
+                   FHandle input_handle, FHandle output_handle,
                    const char *log_pathname, bool interactive_console);
 
-extern "C" EXPCL_P3DPYTHON bool
+extern "C" EXPCL_P3DPYTHON int
 run_p3dpython(const char *program_name, const char *archive_file,
-              FHandle input_handle, FHandle output_handle, 
+              FHandle input_handle, FHandle output_handle,
               const char *log_pathname, bool interactive_console);
 
 #endif

+ 3 - 3
direct/src/plugin_activex/PPInstance.cpp

@@ -581,12 +581,12 @@ int PPInstance::LoadPlugin( const std::string& dllFilename )
         pathname = override_filename;
       }
 #endif  // P3D_PLUGIN_P3D_PLUGIN
-      
+
       nout << "Attempting to load core API from " << pathname << "\n";
       string contents_filename = m_rootDir + "/contents.xml";
       if (!load_plugin(pathname, contents_filename, PANDA_PACKAGE_HOST_URL,
-                       P3D_VC_normal, "", "", "", false, false, 
-                       m_rootDir, "", nout)) {
+                       P3D_VC_normal, "", "", "", false, false,
+                       m_rootDir, "", "", nout)) {
         nout << "Unable to launch core API in " << pathname << "\n";
         error = 1;
       } else {

+ 2 - 2
direct/src/plugin_npapi/ppInstance.cxx

@@ -1738,8 +1738,8 @@ do_load_plugin() {
   nout << "Attempting to load core API from " << pathname << "\n";
   string contents_filename = _root_dir + "/contents.xml";
   if (!load_plugin(pathname, contents_filename, PANDA_PACKAGE_HOST_URL,
-                   P3D_VC_normal, "", "", "", false, false, 
-                   _root_dir, "", nout)) {
+                   P3D_VC_normal, "", "", "", false, false,
+                   _root_dir, "", "", nout)) {
     nout << "Unable to launch core API in " << pathname << "\n";
     set_failed();
     return;

+ 23 - 6
direct/src/plugin_standalone/p3dEmbed.cxx

@@ -53,7 +53,8 @@ run_embedded(streampos read_offset, int argc, char *argv[]) {
   // the + 1 from the test.
   _read_offset_check = read_offset + (streampos)1;
   if (_read_offset_check == (streampos)0xFF3D3D01) {
-    cerr << "This program is not intended to be run directly.\nIt is used by pdeploy to construct an embedded Panda3D application.\n";
+    cerr << "This program is not intended to be run directly.\nIt is used "
+            "by pdeploy to construct an embedded Panda3D application.\n";
     return 1;
   }
 
@@ -86,22 +87,30 @@ run_embedded(streampos read_offset, int argc, char *argv[]) {
   string value;
   string root_dir;
   string host_dir;
+  string start_dir;
+
   while (true) {
     if (curchr == EOF) {
       cerr << "Truncated stream\n";
-      return(1);
+      return 1;
 
     } else if (curchr == 0) {
       // Two null bytes in a row means we've reached the end of the data.
       if (havenull) {
         break;
       }
-      
+
       // This means we haven't seen an '=' character yet.
       if (keyword == "") {
         if (curstr != "") {
           cerr << "Ignoring token '" << curstr << "' without value\n";
         }
+
+      } else if (keyword == "start_dir") {
+        // Don't pass this on as a token, since it has slightly different
+        // semantics when used as an HTML token.
+        start_dir = curstr;
+
       } else {
         value.assign(curstr);
         P3D_token token;
@@ -118,6 +127,8 @@ run_embedded(streampos read_offset, int argc, char *argv[]) {
           _got_win_size = true;
         } else if (keyword == "log_basename") {
           _log_basename = value;
+        } else if (keyword == "log_directory") {
+          _log_dirname = value;
         } else if (keyword == "root_dir") {
           root_dir = value;
         } else if (keyword == "host_dir") {
@@ -168,6 +179,13 @@ run_embedded(streampos read_offset, int argc, char *argv[]) {
     _host_dir = host_dir_f.to_os_specific();
   }
 
+  // Make the start directory absolute
+  if (!start_dir.empty()) {
+    Filename start_dir_f(start_dir);
+    start_dir_f.make_absolute(f.get_dirname());
+    _start_dir = start_dir_f.to_os_specific();
+  }
+
   // Initialize the core API by directly assigning all of the function
   // pointers.
   P3D_initialize_ptr = &P3D_initialize;
@@ -229,18 +247,17 @@ run_embedded(streampos read_offset, int argc, char *argv[]) {
   // function pointers.  This will also call P3D_initialize().
   if (!init_plugin("", _host_url, _verify_contents, _this_platform, 
                    _log_dirname, _log_basename, true, _console_environment,
-                   _root_dir, _host_dir, cerr)) {
+                   _root_dir, _host_dir, _start_dir, cerr)) {
     cerr << "Unable to launch core API\n";
     return 1;
   }
-  
+
   // Create a plugin instance and run the program
   P3D_instance *inst = create_instance(f, true, argv, argc, read_offset);
   _instances.insert(inst);
   
   run_main_loop();
 
-
   unload_plugin(cerr);
   return 0;
 }

+ 185 - 135
direct/src/plugin_standalone/panda3d.cxx

@@ -356,6 +356,7 @@ bool Panda3D::
 get_plugin() {
   // First, look for the existing contents.xml file.
   bool success = false;
+  bool is_fresh = false;
 
   Filename contents_filename = Filename(Filename::from_os_specific(_root_dir), "contents.xml");
   if (_verify_contents != P3D_VC_force) {
@@ -370,75 +371,100 @@ get_plugin() {
   if (!success) {
     // Couldn't read it (or it wasn't current enough), so go get a new
     // one.
-    HTTPClient *http = HTTPClient::get_global_ptr();
-    
-    // Try the super_mirror first.
-    if (!_super_mirror_url_prefix.empty()) {
-      // We don't bother putting a uniquifying query string when we're
-      // downloading this file from the super_mirror.  The super_mirror
-      // is by definition a cache, so it doesn't make sense to bust
-      // caches here.
-      string url = _super_mirror_url_prefix + "contents.xml";
-      PT(HTTPChannel) channel = http->make_channel(false);
-      channel->get_document(url);
-      
-      Filename tempfile = Filename::temporary("", "p3d_");
-      if (!channel->download_to_file(tempfile)) {
-        cerr << "Unable to download " << url << "\n";
-        tempfile.unlink();
-      } else {
-        // Successfully downloaded from the super_mirror; try to read it.
-        success = read_contents_file(tempfile, true);
-        tempfile.unlink();
-      }
+    if (!download_contents_file(contents_filename)) {
+      // We don't have a usable contents.xml file.
+      return false;
     }
+    is_fresh = true;
+  }
 
-    if (!success) {
-      // Go download contents.xml from the actual host.
-      ostringstream strm;
-      strm << _host_url_prefix << "contents.xml";
-      // Append a uniquifying query string to the URL to force the
-      // download to go all the way through any caches.  We use the time
-      // in seconds; that's unique enough.
-      strm << "?" << time(NULL);
-      string url = strm.str();
-      
-      // We might as well explicitly request the cache to be disabled too,
-      // since we have an interface for that via HTTPChannel.
-      DocumentSpec request(url);
-      request.set_cache_control(DocumentSpec::CC_no_cache);
-      
-      PT(HTTPChannel) channel = http->make_channel(false);
-      channel->get_document(request);
-      
-      // Since we have to download some of it, might as well ask the core
-      // API to check all of it.
-      if (_verify_contents == P3D_VC_none) {
-        _verify_contents = P3D_VC_normal;
-      }
-      
-      // First, download it to a temporary file.
-      Filename tempfile = Filename::temporary("", "p3d_");
-      if (!channel->download_to_file(tempfile)) {
-        cerr << "Unable to download " << url << "\n";
-        
-        // Couldn't download, but try to read the existing contents.xml
-        // file anyway.  Maybe it's good enough.
-        success = read_contents_file(contents_filename, false);
-        
-      } else {
-        // Successfully downloaded; read it and move it into place.
-        success = read_contents_file(tempfile, true);
-      }
+  // Now that we've downloaded the contents file successfully, start
+  // the Core API.
+  if (!get_core_api()) {
+      // We failed.  Make sure contents.xml is up-to-date and try again.
+    if (!is_fresh && download_contents_file(contents_filename) && get_core_api()) {
+      return true;
+    } else {
+      return false;
+    }
+  }
+
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Panda3D::download_contents_file
+//       Access: Protected
+//  Description: Redownloads the contents.xml file from the named
+//               URL without first checking if it is up to date.
+//               Returns true if we have a contents.xml file that
+//               might be usable, false otherwise.
+////////////////////////////////////////////////////////////////////
+bool Panda3D::
+download_contents_file(const Filename &contents_filename) {
+  bool success = false;
+  HTTPClient *http = HTTPClient::get_global_ptr();
+
+  // Try the super_mirror first.
+  if (!_super_mirror_url_prefix.empty()) {
+    // We don't bother putting a uniquifying query string when we're
+    // downloading this file from the super_mirror.  The super_mirror
+    // is by definition a cache, so it doesn't make sense to bust
+    // caches here.
+    string url = _super_mirror_url_prefix + "contents.xml";
+    PT(HTTPChannel) channel = http->make_channel(false);
+    channel->get_document(url);
 
+    Filename tempfile = Filename::temporary("", "p3d_");
+    if (!channel->download_to_file(tempfile)) {
+      cerr << "Unable to download " << url << "\n";
+      tempfile.unlink();
+    } else {
+      // Successfully downloaded from the super_mirror; try to read it.
+      success = read_contents_file(tempfile, true);
       tempfile.unlink();
     }
   }
 
-  if (success) {
-    // Now that we've downloaded the contents file successfully, start
-    // the Core API.
-    success = get_core_api();
+  if (!success) {
+    // Go download contents.xml from the actual host.
+    ostringstream strm;
+    strm << _host_url_prefix << "contents.xml";
+    // Append a uniquifying query string to the URL to force the
+    // download to go all the way through any caches.  We use the time
+    // in seconds; that's unique enough.
+    strm << "?" << time(NULL);
+    string url = strm.str();
+
+    // We might as well explicitly request the cache to be disabled too,
+    // since we have an interface for that via HTTPChannel.
+    DocumentSpec request(url);
+    request.set_cache_control(DocumentSpec::CC_no_cache);
+
+    PT(HTTPChannel) channel = http->make_channel(false);
+    channel->get_document(request);
+
+    // Since we have to download some of it, might as well ask the core
+    // API to check all of it.
+    if (_verify_contents == P3D_VC_none) {
+      _verify_contents = P3D_VC_normal;
+    }
+
+    // First, download it to a temporary file.
+    Filename tempfile = Filename::temporary("", "p3d_");
+    if (!channel->download_to_file(tempfile)) {
+      cerr << "Unable to download " << url << "\n";
+
+      // Couldn't download, but try to read the existing contents.xml
+      // file anyway.  Maybe it's good enough.
+      success = read_contents_file(contents_filename, false);
+
+    } else {
+      // Successfully downloaded; read it and move it into place.
+      success = read_contents_file(tempfile, true);
+    }
+
+    tempfile.unlink();
   }
 
   return success;
@@ -690,7 +716,6 @@ choose_random_mirrors(vector_string &result, int num_mirrors) {
   }
 }
 
-
 ////////////////////////////////////////////////////////////////////
 //     Function: Panda3D::get_core_api
 //       Access: Protected
@@ -700,78 +725,12 @@ choose_random_mirrors(vector_string &result, int num_mirrors) {
 ////////////////////////////////////////////////////////////////////
 bool Panda3D::
 get_core_api() {
+  bool is_fresh = false;
   if (!_coreapi_dll.quick_verify(_root_dir)) {
-    // The DLL file needs to be downloaded.  Build up our list of
-    // URL's to attempt to download it from, in reverse order.
-    string url;
-    vector_string core_urls;
-
-    // Our last act of desperation: hit the original host, with a
-    // query uniquifier, to break through any caches.
-    ostringstream strm;
-    strm << _download_url_prefix << _coreapi_dll.get_filename()
-         << "?" << time(NULL);
-    url = strm.str();
-    core_urls.push_back(url);
-
-    // Before we try that, we'll hit the original host, without a
-    // uniquifier.
-    url = _download_url_prefix;
-    url += _coreapi_dll.get_filename();
-    core_urls.push_back(url);
-
-    // And before we try that, we'll try two mirrors, at random.
-    vector_string mirrors;
-    choose_random_mirrors(mirrors, 2);
-    for (vector_string::iterator si = mirrors.begin();
-         si != mirrors.end(); 
-         ++si) {
-      url = (*si) + _coreapi_dll.get_filename();
-      core_urls.push_back(url);
-    }
-
-    // The very first thing we'll try is the super_mirror, if we have
-    // one.
-    if (!_super_mirror_url_prefix.empty()) {
-      url = _super_mirror_url_prefix + _coreapi_dll.get_filename();
-      core_urls.push_back(url);
-    }
-
-    // Now pick URL's off the list, and try them, until we have
-    // success.
-    Filename pathname = Filename::from_os_specific(_coreapi_dll.get_pathname(_root_dir));
-    pathname.make_dir();
-    HTTPClient *http = HTTPClient::get_global_ptr();
-
-    bool success = false;
-    while (!core_urls.empty()) {
-      url = core_urls.back();
-      core_urls.pop_back();
-    
-      PT(HTTPChannel) channel = http->get_document(url);
-      if (!channel->download_to_file(pathname)) {
-        cerr << "Unable to download " << url << "\n";
-
-      } else if (!_coreapi_dll.full_verify(_root_dir)) {
-        cerr << "Mismatched download for " << url << "\n";
-
-      } else {
-        // successfully downloaded!
-        success = true;
-        break;
-      }
-    }
-
-    if (!success) {
-      cerr << "Couldn't download core API.\n";
+    if (!download_core_api()) {
       return false;
     }
-
-    // Since we had to download some of it, might as well ask the core
-    // API to check all of it.
-    if (_verify_contents == P3D_VC_none) {
-      _verify_contents = P3D_VC_normal;
-    }
+    is_fresh = true;
   }
 
   // Now we've got the DLL.  Load it.
@@ -795,9 +754,19 @@ get_core_api() {
   if (!load_plugin(pathname, contents_filename.to_os_specific(),
                    _host_url, _verify_contents, _this_platform, _log_dirname,
                    _log_basename, trusted_environment, _console_environment,
-                   _root_dir, "", cerr)) {
-    cerr << "Unable to launch core API in " << pathname << "\n";
-    return false;
+                   _root_dir, _host_dir, _start_dir, cerr)) {
+
+    // If we're not sure this is the latest version, make sure it is
+    // up-to-date, and then try again.
+    if (is_fresh || !download_core_api() ||
+        !load_plugin(pathname, contents_filename.to_os_specific(),
+                     _host_url, _verify_contents, _this_platform, _log_dirname,
+                     _log_basename, trusted_environment, _console_environment,
+                     _root_dir, _host_dir, _start_dir, cerr)) {
+
+      cerr << "Unable to launch core API in " << pathname << "\n";
+      return false;
+    }
   }
 
   // Successfully loaded.
@@ -822,6 +791,87 @@ get_core_api() {
   return true;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: Panda3D::download_core_api
+//       Access: Protected
+//  Description: Downloads the latest version of the core API from
+//               the plug-in server.
+////////////////////////////////////////////////////////////////////
+bool Panda3D::
+download_core_api() {
+  // The DLL file needs to be downloaded.  Build up our list of
+  // URL's to attempt to download it from, in reverse order.
+  string url;
+  vector_string core_urls;
+
+  // Our last act of desperation: hit the original host, with a
+  // query uniquifier, to break through any caches.
+  ostringstream strm;
+  strm << _download_url_prefix << _coreapi_dll.get_filename()
+       << "?" << time(NULL);
+  url = strm.str();
+  core_urls.push_back(url);
+
+  // Before we try that, we'll hit the original host, without a
+  // uniquifier.
+  url = _download_url_prefix;
+  url += _coreapi_dll.get_filename();
+  core_urls.push_back(url);
+
+  // And before we try that, we'll try two mirrors, at random.
+  vector_string mirrors;
+  choose_random_mirrors(mirrors, 2);
+  for (vector_string::iterator si = mirrors.begin();
+       si != mirrors.end();
+       ++si) {
+    url = (*si) + _coreapi_dll.get_filename();
+    core_urls.push_back(url);
+  }
+
+  // The very first thing we'll try is the super_mirror, if we have
+  // one.
+  if (!_super_mirror_url_prefix.empty()) {
+    url = _super_mirror_url_prefix + _coreapi_dll.get_filename();
+    core_urls.push_back(url);
+  }
+
+  // Now pick URL's off the list, and try them, until we have
+  // success.
+  Filename pathname = Filename::from_os_specific(_coreapi_dll.get_pathname(_root_dir));
+  pathname.make_dir();
+  HTTPClient *http = HTTPClient::get_global_ptr();
+
+  bool success = false;
+  while (!core_urls.empty()) {
+    url = core_urls.back();
+    core_urls.pop_back();
+
+    PT(HTTPChannel) channel = http->get_document(url);
+    if (!channel->download_to_file(pathname)) {
+      cerr << "Unable to download " << url << "\n";
+
+    } else if (!_coreapi_dll.full_verify(_root_dir)) {
+      cerr << "Mismatched download for " << url << "\n";
+
+    } else {
+      // successfully downloaded!
+      success = true;
+      break;
+    }
+  }
+
+  if (!success) {
+    return false;
+  }
+
+  // Since we had to download some of it, might as well ask the core
+  // API to check all of it.
+  if (_verify_contents == P3D_VC_none) {
+    _verify_contents = P3D_VC_normal;
+  }
+  return true;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: Panda3D::usage
 //       Access: Protected

+ 2 - 0
direct/src/plugin_standalone/panda3d.h

@@ -42,12 +42,14 @@ public:
 protected:
   bool post_arg_processing();
   bool get_plugin();
+  bool download_contents_file(const Filename &contents_filename);
   bool read_contents_file(const Filename &contents_filename, bool fresh_download);
   void find_host(TiXmlElement *xcontents);
   void read_xhost(TiXmlElement *xhost);
   void add_mirror(string mirror_url);
   void choose_random_mirrors(vector_string &result, int num_mirrors);
   bool get_core_api();
+  bool download_core_api();
 
   void usage();
 

+ 5 - 5
direct/src/plugin_standalone/panda3dBase.cxx

@@ -342,7 +342,7 @@ make_parent_window() {
 ////////////////////////////////////////////////////////////////////
 P3D_instance *Panda3DBase::
 create_instance(const string &p3d, bool start_instance,
-                char **args, int num_args, const int &p3d_offset) {
+                char **args, int num_args, int p3d_offset) {
   // Check to see if the p3d filename we were given is a URL, or a
   // local file.
   Filename p3d_filename = Filename::from_os_specific(p3d);
@@ -356,9 +356,9 @@ create_instance(const string &p3d, bool start_instance,
 
     p3d_filename.make_absolute();
     os_p3d_filename = p3d_filename.to_os_specific();
-  } 
+  }
   if (is_local) {
-    read_p3d_info(p3d_filename);
+    read_p3d_info(p3d_filename, p3d_offset);
   }
 
   // Build up the token list.
@@ -454,9 +454,9 @@ delete_instance(P3D_instance *inst) {
 //               parameters (like width and height).
 ////////////////////////////////////////////////////////////////////
 bool Panda3DBase::
-read_p3d_info(const Filename &p3d_filename) {
+read_p3d_info(const Filename &p3d_filename, int p3d_offset) {
   PT(Multifile) mf = new Multifile;
-  if (!mf->open_read(p3d_filename)) {
+  if (!mf->open_read(p3d_filename, p3d_offset)) {
     return false;
   }
   int si = mf->find_subfile("p3d_info.xml");

+ 3 - 2
direct/src/plugin_standalone/panda3dBase.h

@@ -49,10 +49,10 @@ protected:
 
   P3D_instance *
   create_instance(const string &p3d, bool start_instance,
-                  char **args, int num_args, const int &p3d_offset = 0);
+                  char **args, int num_args, int p3d_offset = 0);
   void delete_instance(P3D_instance *instance);
 
-  bool read_p3d_info(const Filename &p3d_filename);
+  bool read_p3d_info(const Filename &p3d_filename, int p3d_offset = 0);
   bool parse_token(const char *arg);
   bool parse_int_pair(const char *arg, int &x, int &y);
   string lookup_token(const string &keyword) const;
@@ -74,6 +74,7 @@ protected:
   string _host_url;
   string _root_dir;
   string _host_dir;
+  string _start_dir;
   string _log_dirname;
   string _log_basename;
   string _this_platform;

+ 1 - 1
direct/src/showbase/Loader.py

@@ -855,7 +855,7 @@ class Loader(DirectObject):
     def loadShader (self, shaderPath, okMissing = False):
         shader = ShaderPool.loadShader (shaderPath)
         if not shader and not okMissing:
-            message = 'Could not shader file: %s' % (shaderPath)
+            message = 'Could not load shader file: %s' % (shaderPath)
             raise IOError, message
         return shader
 

+ 1 - 1
direct/src/showbase/PythonUtil.py

@@ -1993,7 +1993,7 @@ def pstatcollect(scope, level = None):
 
     try:
 
-        if not (__dev__ or config.GetBool('force-pstatcollect', 0)) or \
+        if not (__dev__ or ConfigVariableBool('force-pstatcollect', False)) or \
            not scope:
             return decorator
 

+ 8 - 6
direct/src/showbase/ShowBase.py

@@ -9,6 +9,8 @@ __all__ = ['ShowBase', 'WindowControls']
 
 from panda3d.core import *
 from panda3d.direct import get_config_showbase, throw_new_frame, init_app_for_gui
+from panda3d.direct import storeAccessibilityShortcutKeys, allowAccessibilityShortcutKeys
+
 
 # This needs to be available early for DirectGUI imports
 import __builtin__ as builtins
@@ -70,6 +72,8 @@ class ShowBase(DirectObject.DirectObject):
         if logStackDump or uploadStackDump:
             ExceptionVarDump.install(logStackDump, uploadStackDump)
 
+        self.__autoGarbageLogging = self.__dev__ and self.config.GetBool('auto-garbage-logging', False)
+
         ## The directory containing the main Python file of this application.
         self.mainDir = ExecutionEnvironment.getEnvironmentVariable("MAIN_DIR")
 
@@ -289,7 +293,6 @@ class ShowBase(DirectObject.DirectObject):
         self.physicsMgrEnabled = 0
         self.physicsMgrAngular = 0
 
-        self.createBaseAudioManagers()
         self.createStats()
 
         self.AppHasAudioFocus = 1
@@ -377,6 +380,8 @@ class ShowBase(DirectObject.DirectObject):
         else:
             ShowBase.notify.info('__dev__ == %s' % __dev__)
 
+        self.createBaseAudioManagers()
+
         # set up recording of Functor creation stacks in __dev__
         PythonUtil.recordFunctorCreationStacks()
 
@@ -526,9 +531,6 @@ class ShowBase(DirectObject.DirectObject):
             del self.winList
             del self.pipe
 
-        vfs = VirtualFileSystem.getGlobalPtr()
-        vfs.unmountAll()
-
     def makeDefaultPipe(self, printPipeTypes = True):
         """
         Creates the default GraphicsPipe, which will be used to make
@@ -2660,7 +2662,7 @@ class ShowBase(DirectObject.DirectObject):
             if not properties.getOpen():
                 # If the user closes the main window, we should exit.
                 self.notify.info("User closed main window.")
-                if __dev__ and config.GetBool('auto-garbage-logging', 0):
+                if self.__autoGarbageLogging:
                     GarbageReport.b_checkForGarbageLeaks()
                 self.userExit()
 
@@ -2668,7 +2670,7 @@ class ShowBase(DirectObject.DirectObject):
                 self.mainWinForeground = 1
             elif not properties.getForeground() and self.mainWinForeground:
                 self.mainWinForeground = 0
-                if __dev__ and config.GetBool('auto-garbage-logging', 0):
+                if self.__autoGarbageLogging:
                     GarbageReport.b_checkForGarbageLeaks()
 
             if properties.getMinimized() and not self.mainWinMinimized:

+ 1 - 1
direct/src/showbase/Transitions.py

@@ -166,7 +166,7 @@ class Transitions:
             self.loadFade()
             self.fade.reparentTo(aspect2d, FADE_SORT_INDEX)
             self.fade.setColor(self.alphaOn)
-        elif base.config.GetBool('no-loading-screen',0):
+        elif ConfigVariableBool('no-loading-screen', False):
             if finishIval:
                 self.transitionIval = finishIval
                 self.transitionIval.start()               

+ 60 - 49
direct/src/showbase/VFSImporter.py

@@ -1,7 +1,7 @@
 __all__ = ['register', 'sharedPackages',
            'reloadSharedPackage', 'reloadSharedPackages']
 
-from panda3d.core import Filename, VirtualFileSystem, VirtualFileMountSystem, OFileStream, copyStream
+from panda3d._core import Filename, VirtualFileSystem, VirtualFileMountSystem, OFileStream, copyStream
 import sys
 import marshal
 import imp
@@ -25,12 +25,6 @@ sharedPackages = {}
 
 vfs = VirtualFileSystem.getGlobalPtr()
 
-# Possible file types.
-FTPythonSource = 0
-FTPythonCompiled = 1
-FTExtensionModule = 2
-FTFrozenModule = 3
-
 compiledExtensions = [ 'pyc', 'pyo' ]
 if not __debug__:
     # In optimized mode, we prefer loading .pyo files over .pyc files.
@@ -44,7 +38,10 @@ class VFSImporter:
     (among other places). """
 
     def __init__(self, path):
-        self.dir_path = Filename.fromOsSpecific(path)
+        if isinstance(path, Filename):
+            self.dir_path = Filename(path)
+        else:
+            self.dir_path = Filename.fromOsSpecific(path)
 
     def find_module(self, fullname, path = None):
         if path is None:
@@ -60,7 +57,8 @@ class VFSImporter:
         filename.setExtension('py')
         vfile = vfs.getFile(filename, True)
         if vfile:
-            return VFSLoader(dir_path, vfile, filename, FTPythonSource)
+            return VFSLoader(dir_path, vfile, filename,
+                             desc=('.py', 'U', imp.PY_SOURCE))
 
         # If there's no .py file, but there's a .pyc file, load that
         # anyway.
@@ -69,33 +67,32 @@ class VFSImporter:
             filename.setExtension(ext)
             vfile = vfs.getFile(filename, True)
             if vfile:
-                return VFSLoader(dir_path, vfile, filename, FTPythonCompiled)
+                return VFSLoader(dir_path, vfile, filename,
+                                 desc=('.'+ext, 'rb', imp.PY_COMPILED))
 
         # Look for a C/C++ extension module.
         for desc in imp.get_suffixes():
             if desc[2] != imp.C_EXTENSION:
                 continue
 
-            filename = Filename(path)
-            filename.setExtension(desc[0][1:])
+            filename = Filename(path + desc[0])
             vfile = vfs.getFile(filename, True)
             if vfile:
-                return VFSLoader(dir_path, vfile, filename, FTExtensionModule,
-                                 desc = desc)
+                return VFSLoader(dir_path, vfile, filename, desc=desc)
 
         # Finally, consider a package, i.e. a directory containing
         # __init__.py.
         filename = Filename(path, '__init__.py')
         vfile = vfs.getFile(filename, True)
         if vfile:
-            return VFSLoader(dir_path, vfile, filename, FTPythonSource,
-                             packagePath = path)
+            return VFSLoader(dir_path, vfile, filename, packagePath=path,
+                             desc=('.py', 'U', imp.PY_SOURCE))
         for ext in compiledExtensions:
             filename = Filename(path, '__init__.' + ext)
             vfile = vfs.getFile(filename, True)
             if vfile:
-                return VFSLoader(dir_path, vfile, filename, FTPythonCompiled,
-                                 packagePath = path)
+                return VFSLoader(dir_path, vfile, filename, packagePath=path,
+                                 desc=('.'+ext, 'rb', imp.PY_COMPILED))
 
         #print >>sys.stderr, "not found."
         return None
@@ -104,22 +101,20 @@ class VFSLoader:
     """ The second part of VFSImporter, this is created for a
     particular .py file or directory. """
 
-    def __init__(self, dir_path, vfile, filename, fileType,
-                 desc = None, packagePath = None):
+    def __init__(self, dir_path, vfile, filename, desc, packagePath=None):
         self.dir_path = dir_path
         self.timestamp = None
         if vfile:
             self.timestamp = vfile.getTimestamp()
         self.filename = filename
-        self.fileType = fileType
         self.desc = desc
         self.packagePath = packagePath
 
     def load_module(self, fullname, loadingShared = False):
         #print >>sys.stderr, "load_module(%s), dir_path = %s, filename = %s" % (fullname, self.dir_path, self.filename)
-        if self.fileType == FTFrozenModule:
+        if self.desc[2] == imp.PY_FROZEN:
             return self._import_frozen_module(fullname)
-        if self.fileType == FTExtensionModule:
+        if self.desc[2] == imp.C_EXTENSION:
             return self._import_extension_module(fullname)
 
         # Check if this is a child of a shared package.
@@ -137,7 +132,7 @@ class VFSLoader:
 
         code = self._read_code()
         if not code:
-            raise ImportError, 'No Python code in %s' % (fullname)
+            raise ImportError('No Python code in %s' % (fullname))
 
         mod = sys.modules.setdefault(fullname, imp.new_module(fullname))
         mod.__file__ = self.filename.toOsSpecific()
@@ -153,7 +148,7 @@ class VFSLoader:
         path = Filename(self.dir_path, Filename.fromOsSpecific(path))
         vfile = vfs.getFile(path)
         if not vfile:
-            raise IOError
+            raise IOError("Could not find '%s'" % (path))
         return vfile.readFile(True)
 
     def is_package(self, fullname):
@@ -172,8 +167,8 @@ class VFSLoader:
         """ Returns the Python source for this file, if it is
         available, or None if it is not.  May raise IOError. """
 
-        if self.fileType == FTPythonCompiled or \
-           self.fileType == FTExtensionModule:
+        if self.desc[2] == imp.PY_COMPILED or \
+           self.desc[2] == imp.C_EXTENSION:
             return None
 
         filename = Filename(self.filename)
@@ -181,7 +176,7 @@ class VFSLoader:
         filename.setText()
         vfile = vfs.getFile(filename)
         if not vfile:
-            raise IOError
+            raise IOError("Could not find '%s'" % (filename))
         return vfile.readFile(True)
 
     def _import_extension_module(self, fullname):
@@ -231,6 +226,10 @@ class VFSLoader:
         #print >>sys.stderr, "importing frozen %s" % (fullname)
         module = imp.load_module(fullname, None, fullname,
                                  ('', '', imp.PY_FROZEN))
+
+        # Workaround for bug in Python 2.
+        if getattr(module, '__path__', None) == fullname:
+            module.__path__ = []
         return module
 
     def _read_code(self):
@@ -239,14 +238,14 @@ class VFSLoader:
         ValueError, SyntaxError, or a number of other errors generated
         by the low-level system. """
 
-        if self.fileType == FTPythonCompiled:
+        if self.desc[2] == imp.PY_COMPILED:
             # It's a pyc file; just read it directly.
             pycVfile = vfs.getFile(self.filename, False)
             if pycVfile:
                 return self._loadPyc(pycVfile, None)
-            raise IOError, 'Could not read %s' % (self.filename)
+            raise IOError('Could not read %s' % (self.filename))
 
-        elif self.fileType == FTExtensionModule:
+        elif self.desc[2] == imp.C_EXTENSION:
             return None
 
         # It's a .py file (or an __init__.py file; same thing).  Read
@@ -282,16 +281,21 @@ class VFSLoader:
 
         code = None
         data = vfile.readFile(True)
-        if data[:4] == imp.get_magic():
+        if data[:4] != imp.get_magic():
+            raise ValueError("Bad magic number in %s" % (vfile))
+
+        if sys.version_info >= (3, 0):
+            t = int.from_bytes(data[4:8], 'little')
+            data = data[12:]
+        else:
             t = ord(data[4]) + (ord(data[5]) << 8) + \
                (ord(data[6]) << 16) + (ord(data[7]) << 24)
-            if not timestamp or t == timestamp:
-                code = marshal.loads(data[8:])
-            else:
-                raise ValueError, 'Timestamp wrong on %s' % (vfile)
+            data = data[8:]
+
+        if not timestamp or t == timestamp:
+            return marshal.loads(data)
         else:
-            raise ValueError, 'Bad magic number in %s' % (vfile)
-        return code
+            raise ValueError("Timestamp wrong on %s" % (vfile))
 
 
     def _compile(self, filename, source):
@@ -311,15 +315,16 @@ class VFSLoader:
         except IOError:
             pass
         else:
-            f.write('\0\0\0\0')
-            f.write(chr(self.timestamp & 0xff) +
-                    chr((self.timestamp >> 8) & 0xff) +
-                    chr((self.timestamp >> 16) & 0xff) +
-                    chr((self.timestamp >> 24) & 0xff))
-            f.write(marshal.dumps(code))
-            f.flush()
-            f.seek(0, 0)
             f.write(imp.get_magic())
+            if sys.version_info >= (3, 0):
+                f.write((self.timestamp & 0xffffffff).to_bytes(4, 'little'))
+                f.write(b'\0\0\0\0')
+            else:
+                f.write(chr(self.timestamp & 0xff) +
+                        chr((self.timestamp >> 8) & 0xff) +
+                        chr((self.timestamp >> 16) & 0xff) +
+                        chr((self.timestamp >> 24) & 0xff))
+            f.write(marshal.dumps(code))
             f.close()
 
         return code
@@ -389,7 +394,7 @@ class VFSSharedImporter:
         """ Returns the directory name that the indicated
         conventionally-loaded module must have been loaded from. """
 
-        if not hasattr(mod, __file__) or mod.__file__ is None:
+        if not getattr(mod, '__file__', None):
             return None
 
         fullname = mod.__name__
@@ -434,6 +439,9 @@ class VFSSharedLoader:
         if self.reload:
             mod = sys.modules[fullname]
             path = mod.__path__ or []
+            if path == fullname:
+                # Work around Python bug setting __path__ of frozen modules.
+                path = []
             vfs_shared_path = getattr(mod, '_vfs_shared_path', [])
 
         for loader in self.loaders:
@@ -451,11 +459,12 @@ class VFSSharedLoader:
 
         if mod is None:
             # If all of them failed to load, raise ImportError.
-            raise ImportError, message
+            raise ImportError(message)
 
         # If at least one of them loaded successfully, return the
         # union of loaded modules.
         mod.__path__ = path
+        mod.__package__ = fullname
 
         # Also set this special symbol, which records that this is a
         # shared package, and also lists the paths we have already
@@ -516,7 +525,9 @@ def reloadSharedPackages():
 
     #print >> sys.stderr, "reloadSharedPackages, path = %s, sharedPackages = %s" % (sys.path, sharedPackages.keys())
 
-    for fullname in sharedPackages.keys():
+    # Sort the list, just to make sure parent packages are reloaded
+    # before child packages are.
+    for fullname in sorted(sharedPackages.keys()):
         mod = sys.modules.get(fullname, None)
         if not mod:
             continue

+ 92 - 91
direct/src/showutil/FreezeTool.py

@@ -8,7 +8,8 @@ import marshal
 import imp
 import platform
 import types
-from distutils.sysconfig import PREFIX, get_python_inc, get_python_version
+from StringIO import StringIO
+from distutils.sysconfig import PREFIX, get_python_inc, get_python_version, get_config_var
 
 # Temporary (?) try..except to protect against unbuilt p3extend_frozen.
 try:
@@ -17,7 +18,6 @@ except ImportError:
     p3extend_frozen = None
 
 from panda3d.core import *
-from pandac.extension_native_helpers import dll_suffix, dll_ext
 
 # Check to see if we are running python_d, which implies we have a
 # debug build, and we have to build the module with debug options.
@@ -31,14 +31,12 @@ isDebugBuild = (python.lower().endswith('_d'))
 # must be frozen in any main.exe.
 startupModules = [
     'site', 'sitecustomize', 'os', 'encodings.cp1252',
-    'org',
+    'encodings.latin_1', 'encodings.utf_8', 'io', 'org',
     ]
 
 # These are missing modules that we've reported already this session.
 reportedMissing = {}
 
-# Our own Python source trees to watch out for.
-sourceTrees = ['direct']
 
 class CompilationEnvironment:
     """ Create an instance of this class to record the commands to
@@ -64,7 +62,7 @@ class CompilationEnvironment:
         # Paths to Python stuff.
         self.Python = None
         self.PythonIPath = get_python_inc()
-        self.PythonVersion = get_python_version()
+        self.PythonVersion = get_config_var("LDVERSION") or get_python_version()
 
         # The VC directory of Microsoft Visual Studio (if relevant)
         self.MSVC = None
@@ -445,7 +443,7 @@ extend_frozen_modules(const struct _frozen *new_modules, int new_count) {
 }
 
 %(dllexport)svoid init%(moduleName)s() {
-  extend_frozen_modules(_PyImport_FrozenModules, %(newcount)s);
+  extend_frozen_modules(_PyImport_FrozenModules, sizeof(_PyImport_FrozenModules) / sizeof(struct _frozen));
   Py_InitModule("%(moduleName)s", nullMethods);
 }
 """
@@ -458,7 +456,7 @@ programFile = """
 
 %(moduleDefs)s
 
-static struct _frozen _PyImport_FrozenModules[] = {
+struct _frozen _PyImport_FrozenModules[] = {
 %(moduleList)s
   {NULL, NULL, 0}
 };
@@ -485,7 +483,7 @@ okMissing = [
     'Carbon.Folder', 'Carbon.Folders', 'HouseGlobals', 'Carbon.File',
     'MacOS', '_emx_link', 'ce', 'mac', 'org.python.core', 'os.path',
     'os2', 'posix', 'pwd', 'readline', 'riscos', 'riscosenviron',
-    'riscospath', 'dbm', 'fcntl', 'win32api',
+    'riscospath', 'dbm', 'fcntl', 'win32api', 'usercustomize',
     '_winreg', 'ctypes', 'ctypes.wintypes', 'nt','msvcrt',
     'EasyDialogs', 'SOCKS', 'ic', 'rourl2path', 'termios',
     'OverrideFrom23._Res', 'email', 'email.Utils', 'email.Generator',
@@ -498,7 +496,8 @@ class Freezer:
         def __init__(self, moduleName, filename = None,
                      implicit = False, guess = False,
                      exclude = False, forbid = False,
-                     allowChildren = False, fromSource = None):
+                     allowChildren = False, fromSource = None,
+                     text = None):
             # The Python module name.
             self.moduleName = moduleName
 
@@ -533,6 +532,9 @@ class Freezer:
             # record came from, supplied by the caller.
             self.fromSource = fromSource
 
+            # If this is set, it contains Python code of the module.
+            self.text = text
+
             # Some sanity checks.
             if not self.exclude:
                 self.allowChildren = True
@@ -609,20 +611,14 @@ class Freezer:
 
         self.mf = None
 
-        # Make sure we know how to find "direct".
-        for sourceTree in sourceTrees:
-            try:
-                module = __import__(sourceTree)
-            except:
-                pass
-
         # Actually, make sure we know how to find all of the
         # already-imported modules.  (Some of them might do their own
         # special path mangling.)
         for moduleName, module in sys.modules.items():
             if module and hasattr(module, '__path__'):
                 path = getattr(module, '__path__')
-                modulefinder.AddPackagePath(moduleName, path[0])
+                if path:
+                    modulefinder.AddPackagePath(moduleName, path[0])
 
     def excludeFrom(self, freezer):
         """ Excludes all modules that have already been processed by
@@ -749,7 +745,8 @@ class Freezer:
         return modules
 
     def addModule(self, moduleName, implicit = False, newName = None,
-                  filename = None, guess = False, fromSource = None):
+                  filename = None, guess = False, fromSource = None,
+                  text = None):
         """ Adds a module to the list of modules to be exported by
         this tool.  If implicit is true, it is OK if the module does
         not actually exist.
@@ -805,7 +802,7 @@ class Freezer:
                     # It's actually a regular module.
                     self.modules[newParentName] = self.ModuleDef(
                         parentName, implicit = implicit, guess = guess,
-                        fromSource = fromSource)
+                        fromSource = fromSource, text = text)
 
                 else:
                     # Now get all the py files in the parent directory.
@@ -820,9 +817,9 @@ class Freezer:
             # A normal, explicit module name.
             self.modules[newName] = self.ModuleDef(
                 moduleName, filename = filename, implicit = implicit,
-                guess = guess, fromSource = fromSource)
+                guess = guess, fromSource = fromSource, text = text)
 
-    def done(self, compileToExe = False):
+    def done(self, addStartupModules = False):
         """ Call this method after you have added all modules with
         addModule().  You may then call generateCode() or
         writeMultifile() to dump the resulting output.  After a call
@@ -833,7 +830,9 @@ class Freezer:
 
         # If we are building an exe, we also need to implicitly
         # bring in Python's startup modules.
-        if compileToExe:
+        if addStartupModules:
+            self.modules['_frozen_importlib'] = self.ModuleDef('importlib._bootstrap', implicit = True)
+
             for moduleName in startupModules:
                 if moduleName not in self.modules:
                     self.modules[moduleName] = self.ModuleDef(moduleName, implicit = True)
@@ -971,7 +970,10 @@ class Freezer:
                 stuff = ("", "rb", imp.PY_COMPILED)
                 self.mf.load_module(mdef.moduleName, fp, pathname, stuff)
             else:
-                fp = open(pathname, modulefinder.READ_MODE)
+                if mdef.text:
+                    fp = StringIO(mdef.text)
+                else:
+                    fp = open(pathname, 'U')
                 stuff = ("", "r", imp.PY_SOURCE)
                 self.mf.load_module(mdef.moduleName, fp, pathname, stuff)
 
@@ -1063,8 +1065,12 @@ class Freezer:
 
     def __addPyc(self, multifile, filename, code, compressionLevel):
         if code:
-            data = imp.get_magic() + '\0\0\0\0' + \
-                   marshal.dumps(code)
+            data = imp.get_magic() + b'\0\0\0\0'
+
+            if sys.version_info >= (3, 0):
+                data += b'\0\0\0\0'
+
+            data += marshal.dumps(code)
 
             stream = StringStream(data)
             multifile.addSubfile(filename, stream, compressionLevel)
@@ -1206,22 +1212,9 @@ class Freezer:
         multifile.flush()
         multifile.repack()
 
-    def generateCode(self, basename, compileToExe = False):
-        """ After a call to done(), this freezes all of the
-        accumulated python code into either an executable program (if
-        compileToExe is true) or a dynamic library (if compileToExe is
-        false).  The basename is the name of the file to write,
-        without the extension.
-
-        The return value is the newly-generated filename, including
-        the filename extension.  Additional extension modules are
-        listed in self.extras. """
-
-        if compileToExe:
-            # We must have a __main__ module to make an exe file.
-            if not self.__writingModule('__main__'):
-                message = "Can't generate an executable without a __main__ module."
-                raise StandardError, message
+    def writeCode(self, filename, initCode = ""):
+        """ After a call to done(), this freezes all of the accumulated
+        Python code into a C source file. """
 
         self.__replacePaths()
 
@@ -1239,38 +1232,57 @@ class Freezer:
                 # Allow importing this module.
                 module = self.mf.modules.get(origName, None)
                 code = getattr(module, "__code__", None)
-                if not code and moduleName in startupModules:
+                if code:
+                    code = marshal.dumps(code)
+
+                    mangledName = self.mangleName(moduleName)
+                    moduleDefs.append(self.makeModuleDef(mangledName, code))
+                    moduleList.append(self.makeModuleListEntry(mangledName, code, moduleName, module))
+
+                elif moduleName in startupModules:
                     # Forbid the loading of this startup module.
                     moduleList.append(self.makeForbiddenModuleListEntry(moduleName))
+
                 else:
-                    if origName in sourceTrees:
-                        # This is one of Panda3D's own Python source
-                        # trees.  These are a special case: we don't
-                        # compile the __init__.py files within them,
-                        # since their only purpose is to munge the
-                        # __path__ variable anyway.  Instead, we
-                        # pretend the __init__.py files are empty.
-                        code = compile('', moduleName, 'exec')
-
-                    if code:
-                        code = marshal.dumps(code)
-
-                        mangledName = self.mangleName(moduleName)
-                        moduleDefs.append(self.makeModuleDef(mangledName, code))
-                        moduleList.append(self.makeModuleListEntry(mangledName, code, moduleName, module))
+                    # This is a module with no associated Python
+                    # code.  It must be an extension module.  Get the
+                    # filename.
+                    extensionFilename = getattr(module, '__file__', None)
+                    if extensionFilename:
+                        self.extras.append((moduleName, extensionFilename))
                     else:
+                        # It doesn't even have a filename; it must
+                        # be a built-in module.  No worries about
+                        # this one, then.
+                        pass
 
-                        # This is a module with no associated Python
-                        # code.  It must be an extension module.  Get the
-                        # filename.
-                        extensionFilename = getattr(module, '__file__', None)
-                        if extensionFilename:
-                            self.extras.append((moduleName, extensionFilename))
-                        else:
-                            # It doesn't even have a filename; it must
-                            # be a built-in module.  No worries about
-                            # this one, then.
-                            pass
+        text = programFile % {
+            'moduleDefs': '\n'.join(moduleDefs),
+            'moduleList': '\n'.join(moduleList),
+            'initCode': initCode
+            }
+
+        if filename is not None:
+            file = open(filename, 'w')
+            file.write(text)
+            file.close()
+
+    def generateCode(self, basename, compileToExe = False):
+        """ After a call to done(), this freezes all of the
+        accumulated python code into either an executable program (if
+        compileToExe is true) or a dynamic library (if compileToExe is
+        false).  The basename is the name of the file to write,
+        without the extension.
+
+        The return value is the newly-generated filename, including
+        the filename extension.  Additional extension modules are
+        listed in self.extras. """
+
+        if compileToExe:
+            # We must have a __main__ module to make an exe file.
+            if not self.__writingModule('__main__'):
+                message = "Can't generate an executable without a __main__ module."
+                raise StandardError, message
 
         filename = basename + self.sourceExtension
 
@@ -1309,21 +1321,12 @@ class Freezer:
 
             initCode = dllInitCode % {
                 'moduleName' : os.path.basename(basename),
-                'newcount' : len(moduleList),
                 'dllexport' : dllexport,
                 'dllimport' : dllimport,
                 }
             compileFunc = self.cenv.compileDll
 
-        text = programFile % {
-            'moduleDefs' : '\n'.join(moduleDefs),
-            'moduleList' : '\n'.join(moduleList),
-            'initCode' : initCode,
-            }
-
-        file = open(filename, 'w')
-        file.write(text)
-        file.close()
+        self.writeCode(filename, initCode=initCode)
 
         try:
             compileFunc(filename, basename)
@@ -1342,7 +1345,10 @@ class Freezer:
         for i in range(0, len(code), 16):
             result += '\n  '
             for c in code[i:i+16]:
-                result += ('%d,' % ord(c))
+                if isinstance(c, int): # Python 3
+                    result += ('%d,' % c)
+                else: # Python 2
+                    result += ('%d,' % ord(c))
         result += '\n};\n'
         return result
 
@@ -1379,9 +1385,13 @@ class PandaModuleFinder(modulefinder.ModuleFinder):
     def __init__(self, *args, **kw):
         modulefinder.ModuleFinder.__init__(self, *args, **kw)
 
-    def find_module(self, name, path, parent=None):
+    def find_module(self, name, path, *args, **kwargs):
+        if imp.is_frozen(name):
+            # Don't pick up modules that are frozen into p3dpython.
+            raise ImportError("'%s' is a frozen module" % (name))
+
         try:
-            return modulefinder.ModuleFinder.find_module(self, name, path, parent = parent)
+            return modulefinder.ModuleFinder.find_module(self, name, path, *args, **kwargs)
         except ImportError:
             # It wasn't found through the normal channels.  Maybe it's
             # one of ours, or maybe it's frozen?
@@ -1394,15 +1404,6 @@ class PandaModuleFinder(modulefinder.ModuleFinder):
                 # It's a frozen module.
                 return (None, name, ('', '', imp.PY_FROZEN))
 
-        # Look for a dtool extension.  This loop is roughly lifted
-        # from extension_native_helpers.Dtool_PreloadDLL().
-        filename = name + dll_suffix + dll_ext
-        for dir in sys.path + [sys.prefix]:
-            lib = os.path.join(dir, filename)
-            if os.path.exists(lib):
-                file = open(lib, 'rb')
-                return (file, lib, (dll_ext, 'rb', imp.C_EXTENSION))
-
         message = "DLL loader cannot find %s." % (name)
         raise ImportError, message
 

+ 5 - 1
direct/src/showutil/Rope.py

@@ -10,7 +10,8 @@ class Rope(NodePath):
     thick lines built from triangle strips.
     """
 
-    showRope = base.config.GetBool('show-rope', 1)
+    showRope = ConfigVariableBool('show-rope', True, \
+      "Set this to false to deactivate the display of ropes.")
     
     def __init__(self, name = 'Rope'):
         self.ropeNode = RopeNode(name)
@@ -18,6 +19,9 @@ class Rope(NodePath):
         self.ropeNode.setCurve(self.curve)
         NodePath.__init__(self, self.ropeNode)
         self.name = name
+        self.order = 0
+        self.verts = []
+        self.knots = None
         
     def setup(self, order, verts, knots = None):
         """This must be called to define the shape of the curve

+ 32 - 17
direct/src/showutil/pfreeze.py

@@ -40,6 +40,11 @@ Options:
      of the __path__ variable, and thus must be actually imported to
      determine the true value of __path__.
 
+  -s
+     Adds the standard set of modules that are necessary for embedding
+     the Python interpreter.  Implicitly set if an executable is
+     generated.
+
 """
 
 import getopt
@@ -58,9 +63,10 @@ def usage(code, msg = ''):
 freezer = FreezeTool.Freezer()
 
 basename = None
+addStartupModules = False
 
 try:
-    opts, args = getopt.getopt(sys.argv[1:], 'o:i:x:p:h')
+    opts, args = getopt.getopt(sys.argv[1:], 'o:i:x:p:sh')
 except getopt.error, msg:
     usage(1, msg)
 
@@ -76,48 +82,57 @@ for opt, arg in opts:
     elif opt == '-p':
         for module in arg.split(','):
             freezer.handleCustomPath(module)
+    elif opt == '-s':
+        addStartupModules = True
     elif opt == '-h':
         usage(0)
     else:
         print 'illegal option: ' + flag
         sys.exit(1)
 
-if not args:
-    usage(0)
-
 if not basename:
     usage(1, 'You did not specify an output file.')
 
-if len(args) != 1:
+if len(args) > 1:
     usage(1, 'Only one main file may be specified.')
 
 outputType = 'exe'
 bl = basename.lower()
 if bl.endswith('.mf'):
     outputType = 'mf'
+elif bl.endswith('.c'):
+    outputType = 'c'
 elif bl.endswith('.dll') or bl.endswith('.pyd') or bl.endswith('.so'):
     basename = os.path.splitext(basename)[0]
     outputType = 'dll'
 elif bl.endswith('.exe'):
     basename = os.path.splitext(basename)[0]
 
-startfile = args[0]
-startmod = startfile
-if startfile.endswith('.py') or startfile.endswith('.pyw') or \
-   startfile.endswith('.pyc') or startfile.endswith('.pyo'):
-    startmod = os.path.splitext(startfile)[0]
-
 compileToExe = False
-if outputType == 'dll':
-    freezer.addModule(startmod, filename = startfile)
-else:
-    freezer.addModule('__main__', filename = startfile)
-    compileToExe = True
+if args:
+    startfile = args[0]
+    startmod = startfile
+    if startfile.endswith('.py') or startfile.endswith('.pyw') or \
+       startfile.endswith('.pyc') or startfile.endswith('.pyo'):
+        startmod = os.path.splitext(startfile)[0]
+
+    if outputType == 'dll' or outputType == 'c':
+        freezer.addModule(startmod, filename = startfile)
+    else:
+        freezer.addModule('__main__', filename = startfile)
+        compileToExe = True
+        addStartupModules = True
+
+elif outputType == 'exe':
+    # We must have a main module when making an executable.
+    usage(0)
 
-freezer.done(compileToExe = compileToExe)
+freezer.done(addStartupModules = addStartupModules)
 
 if outputType == 'mf':
     freezer.writeMultifile(basename)
+elif outputType == 'c':
+    freezer.writeCode(basename)
 else:
     freezer.generateCode(basename, compileToExe = compileToExe)
 

+ 213 - 203
direct/src/stdpy/file.py

@@ -5,150 +5,208 @@ SIMPLE_THREADS model, by avoiding blocking all threads while waiting
 for I/O to complete. """
 
 __all__ = [
-    'file', 'open', 'listdir', 'walk', 'join',
+    'open', 'listdir', 'walk', 'join',
     'isfile', 'isdir', 'exists', 'lexists', 'getmtime', 'getsize',
     'execfile',
     ]
 
-from panda3d import core
-import types
+import panda3d._core as core
+import sys
+import os
+import io
+import encodings
+from posixpath import join
 
 _vfs = core.VirtualFileSystem.getGlobalPtr()
 
-class file:
-    def __init__(self, filename, mode = 'r', bufsize = None,
-                 autoUnwrap = False):
-        self.__stream = None
-        self.__needsVfsClose = False
-        self.__reader = None
-        self.__writer = None
-        self.closed = True
-        self.encoding = None
-        self.errors = None
-        self.__lastWrite = False
+if sys.version_info < (3, 0):
+    # Python 3 defines these subtypes of IOError, but Python 2 doesn't.
+    FileNotFoundError = IOError
+    IsADirectoryError = IOError
+    FileExistsError = IOError
+    PermissionError = IOError
+
+
+def open(file, mode='r', buffering=-1, encoding=None, errors=None, newline=None, closefd=True):
+    if sys.version_info >= (3, 0):
+        # Python 3 is much stricter than Python 2, which lets
+        # unknown flags fall through.
+        for ch in mode:
+            if ch not in 'rwxabt+U':
+                raise ValueError("invalid mode: '%s'" % (mode))
+
+    creating = 'x' in mode
+    writing = 'w' in mode
+    appending = 'a' in mode
+    updating = '+' in mode
+    binary = 'b' in mode
+    universal = 'U' in mode
+    reading = universal or 'r' in mode
+
+    if binary and 't' in mode:
+        raise ValueError("can't have text and binary mode at once")
+
+    if creating + reading + writing + appending > 1:
+        raise ValueError("must have exactly one of create/read/write/append mode")
+
+    if binary:
+        if encoding:
+            raise ValueError("binary mode doesn't take an encoding argument")
+        if errors:
+            raise ValueError("binary mode doesn't take an errors argument")
+        if newline:
+            raise ValueError("binary mode doesn't take a newline argument")
+
+    if isinstance(file, core.Istream) or isinstance(file, core.Ostream):
+        # If we were given a stream instead of a filename, assign
+        # it directly.
+        raw = StreamIOWrapper(file)
+        raw.mode = mode
+
+    else:
+        vfile = None
+
+        if isinstance(file, core.VirtualFile):
+            # We can also "open" a VirtualFile object for reading.
+            vfile = file
+            filename = vfile.getFilename()
+        elif isinstance(file, unicode):
+            # If a raw string is given, assume it's an os-specific
+            # filename.
+            filename = core.Filename.fromOsSpecificW(file)
+        elif isinstance(file, str):
+            filename = core.Filename.fromOsSpecific(file)
+        else:
+            # If a Filename is given, make a writable copy anyway.
+            filename = core.Filename(file)
 
-        self.mode = mode
-        self.name = None
-        self.filename = None
-        self.newlines = None
-        self.softspace = False
+        if binary or sys.version_info >= (3, 0):
+            filename.setBinary()
+        else:
+            filename.setText()
 
-        readMode = False
-        writeMode = False
+        if not vfile:
+            vfile = _vfs.getFile(filename)
 
-        if isinstance(filename, core.Istream) or isinstance(filename, core.Ostream):
-            # If we were given a stream instead of a filename, assign
-            # it directly.
-            self.__stream = filename
-            readMode = isinstance(filename, core.Istream)
-            writeMode = isinstance(filename, core.Ostream)
+        if not vfile:
+            if reading:
+                raise FileNotFoundError("No such file or directory: '%s'" % (filename))
 
-        elif isinstance(filename, core.VirtualFile):
-            # We can also "open" a VirtualFile object for reading.
-            self.__stream = filename.openReadFile(autoUnwrap)
-            if not self.__stream:
-                message = 'Could not read virtual file %s' % (repr(filename))
-                raise IOError, message
-            self.__needsVfsClose = True
-            readMode = True
+            vfile = _vfs.createFile(filename)
+            if not vfile:
+                raise IOError("Failed to create file: '%s'" % (filename))
 
-        else:
-            # Otherwise, we must have been given a filename.  Open it.
-            if isinstance(filename, types.StringTypes):
-                # If a raw string is given, assume it's an os-specific
-                # filename.
-                filename = core.Filename.fromOsSpecific(filename)
+        elif creating:
+            # In 'creating' mode, we have to raise FileExistsError
+            # if the file already exists.  Otherwise, it's the same
+            # as 'writing' mode.
+            raise FileExistsError("File exists: '%s'" % (filename))
+
+        elif vfile.isDirectory():
+            raise IsADirectoryError("Is a directory: '%s'" % (filename))
+
+        # Actually open the streams.
+        if reading:
+            if updating:
+                stream = vfile.openReadWriteFile(False)
             else:
-                # If a Filename is given, make a writable copy anyway.
-                filename = core.Filename(filename)
-
-            self.filename = filename
-            self.name = filename.toOsSpecific()
-
-            binary = False
-            if 'b' in mode:
-                # Strip 'b'.  This means a binary file.
-                i = mode.index('b')
-                mode = mode[:i] + mode[i + 1:]
-                binary = True
-
-            if 'U' in mode:
-                # Strip 'U'.  We don't use it; universal-newline support
-                # is built into Panda, and can't be changed at runtime.
-                i = mode.index('U')
-                mode = mode[:i] + mode[i + 1:]
-                binary = False
-
-            if mode == '':
-                mode = 'r'
-
-            # Per Python docs, we insist this is true.
-            assert mode[0] in 'rwa'
-
-            if binary:
-                filename.setBinary()
+                stream = vfile.openReadFile(False)
+
+            if not stream:
+                raise IOError("Could not open %s for reading" % (filename))
+
+        elif writing or creating:
+            if updating:
+                stream = vfile.openReadWriteFile(True)
             else:
-                filename.setText()
-
-            # Actually open the streams.
-            if mode == 'w':
-                self.__stream = _vfs.openWriteFile(filename, autoUnwrap, True)
-                if not self.__stream:
-                    message = 'Could not open %s for writing' % (filename)
-                    raise IOError, message
-                writeMode = True
-
-            elif mode == 'a':
-                self.__stream = _vfs.openAppendFile(filename)
-                if not self.__stream:
-                    message = 'Could not open %s for writing' % (filename)
-                    raise IOError, message
-                writeMode = True
-
-            elif mode == 'w+':
-                self.__stream = _vfs.openReadWriteFile(filename, True)
-                if not self.__stream:
-                    message = 'Could not open %s for writing' % (filename)
-                    raise IOError, message
-                readMode = True
-                writeMode = True
-
-            elif mode == 'a+':
-                self.__stream = _vfs.openReadAppendFile(filename)
-                if not self.__stream:
-                    message = 'Could not open %s for writing' % (filename)
-                    raise IOError, message
-                readMode = True
-                writeMode = True
-
-            elif mode == 'r+':
-                self.__stream = _vfs.openReadWriteFile(filename, False)
-                if not self.__stream:
-                    message = 'Could not open %s for writing' % (filename)
-                    raise IOError, message
-                readMode = True
-                writeMode = True
-
-            elif mode == 'r':
-                self.__stream = _vfs.openReadFile(filename, autoUnwrap)
-                if not self.__stream:
-                    if not _vfs.exists(filename):
-                        message = 'No such file: %s' % (filename)
-                    else:
-                        message = 'Could not open %s for reading' % (filename)
-                    raise IOError, message
-                readMode = True
-
-            self.__needsVfsClose = True
-
-        if readMode:
-            self.__reader = core.StreamReader(self.__stream, False)
-        if writeMode:
-            self.__writer = core.StreamWriter(self.__stream, False)
+                stream = vfile.openWriteFile(False, True)
+
+            if not stream:
+                raise IOError("Could not open %s for writing" % (filename))
+
+        elif appending:
+            if updating:
+                stream = vfile.openReadAppendFile()
+            else:
+                stream = vfile.openAppendFile()
+
+            if not stream:
+                raise IOError("Could not open %s for appending" % (filename))
+
+        else:
+            raise ValueError("Must have exactly one of create/read/write/append mode and at most one plus")
+
+        raw = StreamIOWrapper(stream, needsVfsClose=True)
+        raw.mode = mode
+        raw.name = vfile.getFilename().toOsSpecific()
+
+    # If a binary stream was requested, return the stream we've created.
+    if binary:
+        return raw
+
+    # If we're in Python 2, we don't decode unicode strings by default.
+    if not encoding and sys.version_info < (3, 0):
+        return raw
+
+    line_buffering = False
+    if buffering == 1:
+        line_buffering = True
+    elif buffering == 0:
+        raise ValueError("can't have unbuffered text I/O")
+
+    # Otherwise, create a TextIOWrapper object to wrap it.
+    wrapper = io.TextIOWrapper(raw, encoding, errors, newline, line_buffering)
+    wrapper.mode = mode
+    return wrapper
+
+
+if sys.version_info < (3, 0):
+    # Python 2 had an alias for open() called file().
+    __all__.append('file')
+    file = open
+
+
+class StreamIOWrapper(io.IOBase):
+    """ This is a file-like object that wraps around a C++ istream and/or
+    ostream object.  It only deals with binary data; to work with text I/O,
+    create an io.TextIOWrapper object around this, or use the open()
+    function that is also provided with this module. """
+
+    def __init__(self, stream, needsVfsClose=False):
+        self.__stream = stream
+        self.__needsVfsClose = needsVfsClose
+        self.__reader = None
+        self.__writer = None
+        self.__lastWrite = False
+
+        if isinstance(stream, core.Istream):
+            self.__reader = core.StreamReader(stream, False)
+
+        if isinstance(stream, core.Ostream):
+            self.__writer = core.StreamWriter(stream, False)
             self.__lastWrite = True
+            if sys.version_info >= (3, 0):
+                # In Python 3, we use appendData, which only accepts bytes.
+                self.__write = self.__writer.appendData
+            else:
+                # In Python 2.7, we also accept unicode objects, which are
+                # implicitly converted to C++ strings.
+                self.__write = self.__writer.write
+
+    def __repr__(self):
+        s = "<direct.stdpy.file.StreamIOWrapper"
+        if hasattr(self, 'name'):
+            s += " name='%s'" % (self.name)
+        if hasattr(self, 'mode'):
+            s += " mode='%s'" % (self.mode)
+        s += ">"
+        return s
 
-    def __del__(self):
-        self.close()
+    def readable(self):
+        return self.__reader is not None
+
+    def writable(self):
+        return self.__writer is not None
 
     def close(self):
         if self.__needsVfsClose:
@@ -160,70 +218,51 @@ class file:
                 _vfs.closeWriteFile(self.__stream)
 
             self.__needsVfsClose = False
+
         self.__stream = None
-        self.__needsVfsClose = False
         self.__reader = None
         self.__writer = None
 
     def flush(self):
-        if self.__stream:
+        if self.__writer:
             self.__stream.clear()  # clear eof flag
             self.__stream.flush()
 
-    def __iter__(self):
-        return self
-
-    def next(self):
-        line = self.readline()
-        if line:
-            return line
-        raise StopIteration
-
-    def read(self, size = -1):
+    def read(self, size=-1):
         if not self.__reader:
             if not self.__writer:
                 # The stream is not even open at all.
-                message = 'I/O operation on closed file'
-                raise ValueError, message
+                raise ValueError("I/O operation on closed file")
+
             # The stream is open only in write mode.
-            message = 'Attempt to read from write-only stream'
-            raise IOError, message
+            raise IOError("Attempt to read from write-only stream")
 
         self.__stream.clear()  # clear eof flag
         self.__lastWrite = False
-        if size >= 0:
+        if size is not None and size >= 0:
             result = self.__reader.extractBytes(size)
         else:
             # Read to end-of-file.
-            result = ''
+            result = b''
             while not self.__stream.eof():
-                result += self.__reader.extractBytes(1024)
+                result += self.__reader.extractBytes(512)
         return result
 
-    def readline(self, size = -1):
+    read1 = read
+
+    def readline(self, size=-1):
         if not self.__reader:
             if not self.__writer:
                 # The stream is not even open at all.
-                message = 'I/O operation on closed file'
-                raise ValueError, message
+                raise ValueError("I/O operation on closed file")
+
             # The stream is open only in write mode.
-            message = 'Attempt to read from write-only stream'
-            raise IOError, message
+            raise IOError("Attempt to read from write-only stream")
 
         self.__stream.clear()  # clear eof flag
         self.__lastWrite = False
         return self.__reader.readline()
 
-    def readlines(self, sizehint = -1):
-        lines = []
-        line = self.readline()
-        while line:
-            lines.append(line)
-            line = self.readline()
-        return lines
-
-    xreadlines = readlines
-
     def seek(self, offset, whence = 0):
         if self.__stream:
             self.__stream.clear()  # clear eof flag
@@ -239,58 +278,43 @@ class file:
         else:
             if self.__reader:
                 return self.__stream.tellg()
-        message = 'I/O operation on closed file'
-        raise ValueError, message
+        raise ValueError("I/O operation on closed file")
 
-    def truncate(self):
-        """ Sorry, this isn't supported by Panda's low-level I/O,
-        because it isn't supported by the standard C++ library. """
-        raise NotImplementedError
-
-    def write(self, str):
+    def write(self, b):
         if not self.__writer:
             if not self.__reader:
                 # The stream is not even open at all.
-                message = 'I/O operation on closed file'
-                raise ValueError, message
+                raise ValueError("I/O operation on closed file")
+
             # The stream is open only in read mode.
-            message = 'Attempt to write to read-only stream'
-            raise IOError, message
+            raise IOError("Attempt to write to read-only stream")
 
         self.__stream.clear()  # clear eof flag
-        self.__writer.appendData(str)
+        self.__write(b)
         self.__lastWrite = True
 
     def writelines(self, lines):
         if not self.__writer:
             if not self.__reader:
                 # The stream is not even open at all.
-                message = 'I/O operation on closed file'
-                raise ValueError, message
+                raise ValueError("I/O operation on closed file")
+
             # The stream is open only in read mode.
-            message = 'Attempt to write to read-only stream'
-            raise IOError, message
+            raise IOError("Attempt to write to read-only stream")
 
         self.__stream.clear()  # clear eof flag
         for line in lines:
-            self.__writer.appendData(line)
+            self.__write(line)
         self.__lastWrite = True
 
-    def __enter__(self):
-        return self
-
-    def __exit__(self, t, v, tb):
-        self.close()
-
-open = file
 
 def listdir(path):
     """ Implements os.listdir over vfs. """
     files = []
     dirlist = _vfs.scanDirectory(core.Filename.fromOsSpecific(path))
     if dirlist is None:
-        message = 'No such file or directory: %s' % (path)
-        raise OSError, message
+        raise OSError("No such file or directory: '%s'" % (path))
+
     for file in dirlist:
         files.append(file.getFilename().getBasename())
     return files
@@ -323,20 +347,6 @@ def walk(top, topdown = True, onerror = None, followlinks = True):
     if not topdown:
         yield (top, dirnames, filenames)
 
-def join(path, *args):
-    for part in args:
-        if part == '':
-            continue
-
-        if part.startswith('/'):
-            path = part
-        elif path.endswith('/'):
-            path = path + part
-        else:
-            path = '/'.join((path, part))
-
-    return path
-
 def isfile(path):
     return _vfs.isRegularFile(core.Filename.fromOsSpecific(path))
 

+ 2 - 0
direct/src/tkpanels/ParticlePanel.py

@@ -19,6 +19,8 @@ import Pmw, os,Placer
 
 from panda3d.core import *
 from panda3d.physics import *
+from panda3d.direct import getParticlePath
+
 
 class ParticlePanel(AppShell):
     # Override class variables

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


File diff suppressed because it is too large
+ 541 - 495
dtool/src/cppparser/cppBison.cxx.prebuilt


+ 220 - 200
dtool/src/cppparser/cppBison.h.prebuilt

@@ -49,214 +49,234 @@ extern int cppyydebug;
      REAL = 258,
      INTEGER = 259,
      CHAR_TOK = 260,
-     STRING = 261,
+     SIMPLE_STRING = 261,
      SIMPLE_IDENTIFIER = 262,
-     IDENTIFIER = 263,
-     TYPENAME_IDENTIFIER = 264,
-     SCOPING = 265,
-     TYPEDEFNAME = 266,
-     ELLIPSIS = 267,
-     OROR = 268,
-     ANDAND = 269,
-     EQCOMPARE = 270,
-     NECOMPARE = 271,
-     LECOMPARE = 272,
-     GECOMPARE = 273,
-     LSHIFT = 274,
-     RSHIFT = 275,
-     POINTSAT_STAR = 276,
-     DOT_STAR = 277,
-     UNARY = 278,
-     UNARY_NOT = 279,
-     UNARY_NEGATE = 280,
-     UNARY_MINUS = 281,
-     UNARY_STAR = 282,
-     UNARY_REF = 283,
-     POINTSAT = 284,
-     SCOPE = 285,
-     PLUSPLUS = 286,
-     MINUSMINUS = 287,
-     TIMESEQUAL = 288,
-     DIVIDEEQUAL = 289,
-     MODEQUAL = 290,
-     PLUSEQUAL = 291,
-     MINUSEQUAL = 292,
-     OREQUAL = 293,
-     ANDEQUAL = 294,
-     XOREQUAL = 295,
-     LSHIFTEQUAL = 296,
-     RSHIFTEQUAL = 297,
-     KW_BEGIN_PUBLISH = 298,
-     KW_BLOCKING = 299,
-     KW_BOOL = 300,
-     KW_CATCH = 301,
-     KW_CHAR = 302,
-     KW_CHAR16_T = 303,
-     KW_CHAR32_T = 304,
-     KW_CLASS = 305,
-     KW_CONST = 306,
-     KW_DELETE = 307,
-     KW_DOUBLE = 308,
-     KW_DYNAMIC_CAST = 309,
-     KW_ELSE = 310,
-     KW_END_PUBLISH = 311,
-     KW_ENUM = 312,
-     KW_EXTENSION = 313,
-     KW_EXTERN = 314,
-     KW_EXPLICIT = 315,
-     KW_PUBLISHED = 316,
-     KW_FALSE = 317,
-     KW_FLOAT = 318,
-     KW_FRIEND = 319,
-     KW_FOR = 320,
-     KW_GOTO = 321,
-     KW_IF = 322,
-     KW_INLINE = 323,
-     KW_INT = 324,
-     KW_LONG = 325,
-     KW_LONGLONG = 326,
-     KW_MAKE_PROPERTY = 327,
-     KW_MAKE_SEQ = 328,
-     KW_MUTABLE = 329,
-     KW_NAMESPACE = 330,
-     KW_NEW = 331,
-     KW_NOEXCEPT = 332,
-     KW_OPERATOR = 333,
-     KW_PRIVATE = 334,
-     KW_PROTECTED = 335,
-     KW_PUBLIC = 336,
-     KW_REGISTER = 337,
-     KW_RETURN = 338,
-     KW_SHORT = 339,
-     KW_SIGNED = 340,
-     KW_SIZEOF = 341,
-     KW_STATIC = 342,
-     KW_STATIC_CAST = 343,
-     KW_STRUCT = 344,
-     KW_TEMPLATE = 345,
-     KW_THROW = 346,
-     KW_TRUE = 347,
-     KW_TRY = 348,
-     KW_TYPEDEF = 349,
-     KW_TYPENAME = 350,
-     KW_UNION = 351,
-     KW_UNSIGNED = 352,
-     KW_USING = 353,
-     KW_VIRTUAL = 354,
-     KW_VOID = 355,
-     KW_VOLATILE = 356,
-     KW_WCHAR_T = 357,
-     KW_WHILE = 358,
-     START_CPP = 359,
-     START_CONST_EXPR = 360,
-     START_TYPE = 361
+     STRING_LITERAL = 263,
+     CUSTOM_LITERAL = 264,
+     IDENTIFIER = 265,
+     TYPENAME_IDENTIFIER = 266,
+     SCOPING = 267,
+     TYPEDEFNAME = 268,
+     ELLIPSIS = 269,
+     OROR = 270,
+     ANDAND = 271,
+     EQCOMPARE = 272,
+     NECOMPARE = 273,
+     LECOMPARE = 274,
+     GECOMPARE = 275,
+     LSHIFT = 276,
+     RSHIFT = 277,
+     POINTSAT_STAR = 278,
+     DOT_STAR = 279,
+     UNARY = 280,
+     UNARY_NOT = 281,
+     UNARY_NEGATE = 282,
+     UNARY_MINUS = 283,
+     UNARY_STAR = 284,
+     UNARY_REF = 285,
+     POINTSAT = 286,
+     SCOPE = 287,
+     PLUSPLUS = 288,
+     MINUSMINUS = 289,
+     TIMESEQUAL = 290,
+     DIVIDEEQUAL = 291,
+     MODEQUAL = 292,
+     PLUSEQUAL = 293,
+     MINUSEQUAL = 294,
+     OREQUAL = 295,
+     ANDEQUAL = 296,
+     XOREQUAL = 297,
+     LSHIFTEQUAL = 298,
+     RSHIFTEQUAL = 299,
+     KW_ALIGNAS = 300,
+     KW_ALIGNOF = 301,
+     KW_AUTO = 302,
+     KW_BEGIN_PUBLISH = 303,
+     KW_BLOCKING = 304,
+     KW_BOOL = 305,
+     KW_CATCH = 306,
+     KW_CHAR = 307,
+     KW_CHAR16_T = 308,
+     KW_CHAR32_T = 309,
+     KW_CLASS = 310,
+     KW_CONST = 311,
+     KW_CONSTEXPR = 312,
+     KW_DECLTYPE = 313,
+     KW_DEFAULT = 314,
+     KW_DELETE = 315,
+     KW_DOUBLE = 316,
+     KW_DYNAMIC_CAST = 317,
+     KW_ELSE = 318,
+     KW_END_PUBLISH = 319,
+     KW_ENUM = 320,
+     KW_EXTENSION = 321,
+     KW_EXTERN = 322,
+     KW_EXPLICIT = 323,
+     KW_PUBLISHED = 324,
+     KW_FALSE = 325,
+     KW_FLOAT = 326,
+     KW_FRIEND = 327,
+     KW_FOR = 328,
+     KW_GOTO = 329,
+     KW_IF = 330,
+     KW_INLINE = 331,
+     KW_INT = 332,
+     KW_LONG = 333,
+     KW_LONGLONG = 334,
+     KW_MAKE_PROPERTY = 335,
+     KW_MAKE_SEQ = 336,
+     KW_MUTABLE = 337,
+     KW_NAMESPACE = 338,
+     KW_NEW = 339,
+     KW_NOEXCEPT = 340,
+     KW_NULLPTR = 341,
+     KW_OPERATOR = 342,
+     KW_PRIVATE = 343,
+     KW_PROTECTED = 344,
+     KW_PUBLIC = 345,
+     KW_REGISTER = 346,
+     KW_RETURN = 347,
+     KW_SHORT = 348,
+     KW_SIGNED = 349,
+     KW_SIZEOF = 350,
+     KW_STATIC = 351,
+     KW_STATIC_ASSERT = 352,
+     KW_STATIC_CAST = 353,
+     KW_STRUCT = 354,
+     KW_TEMPLATE = 355,
+     KW_THROW = 356,
+     KW_TRUE = 357,
+     KW_TRY = 358,
+     KW_TYPEDEF = 359,
+     KW_TYPENAME = 360,
+     KW_UNION = 361,
+     KW_UNSIGNED = 362,
+     KW_USING = 363,
+     KW_VIRTUAL = 364,
+     KW_VOID = 365,
+     KW_VOLATILE = 366,
+     KW_WCHAR_T = 367,
+     KW_WHILE = 368,
+     START_CPP = 369,
+     START_CONST_EXPR = 370,
+     START_TYPE = 371
    };
 #endif
 /* Tokens.  */
 #define REAL 258
 #define INTEGER 259
 #define CHAR_TOK 260
-#define STRING 261
+#define SIMPLE_STRING 261
 #define SIMPLE_IDENTIFIER 262
-#define IDENTIFIER 263
-#define TYPENAME_IDENTIFIER 264
-#define SCOPING 265
-#define TYPEDEFNAME 266
-#define ELLIPSIS 267
-#define OROR 268
-#define ANDAND 269
-#define EQCOMPARE 270
-#define NECOMPARE 271
-#define LECOMPARE 272
-#define GECOMPARE 273
-#define LSHIFT 274
-#define RSHIFT 275
-#define POINTSAT_STAR 276
-#define DOT_STAR 277
-#define UNARY 278
-#define UNARY_NOT 279
-#define UNARY_NEGATE 280
-#define UNARY_MINUS 281
-#define UNARY_STAR 282
-#define UNARY_REF 283
-#define POINTSAT 284
-#define SCOPE 285
-#define PLUSPLUS 286
-#define MINUSMINUS 287
-#define TIMESEQUAL 288
-#define DIVIDEEQUAL 289
-#define MODEQUAL 290
-#define PLUSEQUAL 291
-#define MINUSEQUAL 292
-#define OREQUAL 293
-#define ANDEQUAL 294
-#define XOREQUAL 295
-#define LSHIFTEQUAL 296
-#define RSHIFTEQUAL 297
-#define KW_BEGIN_PUBLISH 298
-#define KW_BLOCKING 299
-#define KW_BOOL 300
-#define KW_CATCH 301
-#define KW_CHAR 302
-#define KW_CHAR16_T 303
-#define KW_CHAR32_T 304
-#define KW_CLASS 305
-#define KW_CONST 306
-#define KW_DELETE 307
-#define KW_DOUBLE 308
-#define KW_DYNAMIC_CAST 309
-#define KW_ELSE 310
-#define KW_END_PUBLISH 311
-#define KW_ENUM 312
-#define KW_EXTENSION 313
-#define KW_EXTERN 314
-#define KW_EXPLICIT 315
-#define KW_PUBLISHED 316
-#define KW_FALSE 317
-#define KW_FLOAT 318
-#define KW_FRIEND 319
-#define KW_FOR 320
-#define KW_GOTO 321
-#define KW_IF 322
-#define KW_INLINE 323
-#define KW_INT 324
-#define KW_LONG 325
-#define KW_LONGLONG 326
-#define KW_MAKE_PROPERTY 327
-#define KW_MAKE_SEQ 328
-#define KW_MUTABLE 329
-#define KW_NAMESPACE 330
-#define KW_NEW 331
-#define KW_NOEXCEPT 332
-#define KW_OPERATOR 333
-#define KW_PRIVATE 334
-#define KW_PROTECTED 335
-#define KW_PUBLIC 336
-#define KW_REGISTER 337
-#define KW_RETURN 338
-#define KW_SHORT 339
-#define KW_SIGNED 340
-#define KW_SIZEOF 341
-#define KW_STATIC 342
-#define KW_STATIC_CAST 343
-#define KW_STRUCT 344
-#define KW_TEMPLATE 345
-#define KW_THROW 346
-#define KW_TRUE 347
-#define KW_TRY 348
-#define KW_TYPEDEF 349
-#define KW_TYPENAME 350
-#define KW_UNION 351
-#define KW_UNSIGNED 352
-#define KW_USING 353
-#define KW_VIRTUAL 354
-#define KW_VOID 355
-#define KW_VOLATILE 356
-#define KW_WCHAR_T 357
-#define KW_WHILE 358
-#define START_CPP 359
-#define START_CONST_EXPR 360
-#define START_TYPE 361
+#define STRING_LITERAL 263
+#define CUSTOM_LITERAL 264
+#define IDENTIFIER 265
+#define TYPENAME_IDENTIFIER 266
+#define SCOPING 267
+#define TYPEDEFNAME 268
+#define ELLIPSIS 269
+#define OROR 270
+#define ANDAND 271
+#define EQCOMPARE 272
+#define NECOMPARE 273
+#define LECOMPARE 274
+#define GECOMPARE 275
+#define LSHIFT 276
+#define RSHIFT 277
+#define POINTSAT_STAR 278
+#define DOT_STAR 279
+#define UNARY 280
+#define UNARY_NOT 281
+#define UNARY_NEGATE 282
+#define UNARY_MINUS 283
+#define UNARY_STAR 284
+#define UNARY_REF 285
+#define POINTSAT 286
+#define SCOPE 287
+#define PLUSPLUS 288
+#define MINUSMINUS 289
+#define TIMESEQUAL 290
+#define DIVIDEEQUAL 291
+#define MODEQUAL 292
+#define PLUSEQUAL 293
+#define MINUSEQUAL 294
+#define OREQUAL 295
+#define ANDEQUAL 296
+#define XOREQUAL 297
+#define LSHIFTEQUAL 298
+#define RSHIFTEQUAL 299
+#define KW_ALIGNAS 300
+#define KW_ALIGNOF 301
+#define KW_AUTO 302
+#define KW_BEGIN_PUBLISH 303
+#define KW_BLOCKING 304
+#define KW_BOOL 305
+#define KW_CATCH 306
+#define KW_CHAR 307
+#define KW_CHAR16_T 308
+#define KW_CHAR32_T 309
+#define KW_CLASS 310
+#define KW_CONST 311
+#define KW_CONSTEXPR 312
+#define KW_DECLTYPE 313
+#define KW_DEFAULT 314
+#define KW_DELETE 315
+#define KW_DOUBLE 316
+#define KW_DYNAMIC_CAST 317
+#define KW_ELSE 318
+#define KW_END_PUBLISH 319
+#define KW_ENUM 320
+#define KW_EXTENSION 321
+#define KW_EXTERN 322
+#define KW_EXPLICIT 323
+#define KW_PUBLISHED 324
+#define KW_FALSE 325
+#define KW_FLOAT 326
+#define KW_FRIEND 327
+#define KW_FOR 328
+#define KW_GOTO 329
+#define KW_IF 330
+#define KW_INLINE 331
+#define KW_INT 332
+#define KW_LONG 333
+#define KW_LONGLONG 334
+#define KW_MAKE_PROPERTY 335
+#define KW_MAKE_SEQ 336
+#define KW_MUTABLE 337
+#define KW_NAMESPACE 338
+#define KW_NEW 339
+#define KW_NOEXCEPT 340
+#define KW_NULLPTR 341
+#define KW_OPERATOR 342
+#define KW_PRIVATE 343
+#define KW_PROTECTED 344
+#define KW_PUBLIC 345
+#define KW_REGISTER 346
+#define KW_RETURN 347
+#define KW_SHORT 348
+#define KW_SIGNED 349
+#define KW_SIZEOF 350
+#define KW_STATIC 351
+#define KW_STATIC_ASSERT 352
+#define KW_STATIC_CAST 353
+#define KW_STRUCT 354
+#define KW_TEMPLATE 355
+#define KW_THROW 356
+#define KW_TRUE 357
+#define KW_TRY 358
+#define KW_TYPEDEF 359
+#define KW_TYPENAME 360
+#define KW_UNION 361
+#define KW_UNSIGNED 362
+#define KW_USING 363
+#define KW_VIRTUAL 364
+#define KW_VOID 365
+#define KW_VOLATILE 366
+#define KW_WCHAR_T 367
+#define KW_WHILE 368
+#define START_CPP 369
+#define START_CONST_EXPR 370
+#define START_TYPE 371
 
 
 

+ 274 - 108
dtool/src/cppparser/cppBison.yxx

@@ -57,17 +57,22 @@ int yyparse();
 
 static void
 yyerror(const string &msg) {
-  current_lexer->error(msg);
+  current_lexer->error(msg, current_lexer->_last_token_loc);
+}
+
+static void
+yyerror(YYLTYPE *loc, const string &msg) {
+  current_lexer->error(msg, *loc);
 }
 
 static void
 yyerror(const string &msg, YYLTYPE &loc) {
-  current_lexer->error(msg, loc.first_line, loc.first_column);
+  current_lexer->error(msg, loc);
 }
 
 static void
 yywarning(const string &msg, YYLTYPE &loc) {
-  current_lexer->warning(msg, loc.first_line, loc.first_column);
+  current_lexer->warning(msg, loc);
 }
 
 static int
@@ -194,13 +199,18 @@ pop_struct() {
 
 /* This is a bison-specific declaration to enable recursive calls to
    yyparse().  It changes the calling sequence to yylex(), passing
-   pointers to the current yylval and yylloc. */
-%pure_parser
+   pointers to the current yylval and yylloc.  Bison 2.7 introduces a
+   different syntax that will also pass the current yylloc to yyerror,
+   but we have to support Bison versions as old as 2.5 for now. */
+/*%define api.pure full*/
+%pure-parser
+%locations
 
 %token <u.real> REAL
 %token <u.integer> INTEGER
 %token <u.integer> CHAR_TOK
-%token <str> STRING SIMPLE_IDENTIFIER
+%token <str> SIMPLE_STRING SIMPLE_IDENTIFIER
+%token <u.expr> STRING_LITERAL CUSTOM_LITERAL
 %token <u.identifier> IDENTIFIER TYPENAME_IDENTIFIER SCOPING
 %token <u.type> TYPEDEFNAME
 
@@ -236,6 +246,9 @@ pop_struct() {
 %token LSHIFTEQUAL
 %token RSHIFTEQUAL
 
+%token KW_ALIGNAS
+%token KW_ALIGNOF
+%token KW_AUTO
 %token KW_BEGIN_PUBLISH
 %token KW_BLOCKING
 %token KW_BOOL
@@ -245,6 +258,9 @@ pop_struct() {
 %token KW_CHAR32_T
 %token KW_CLASS
 %token KW_CONST
+%token KW_CONSTEXPR
+%token KW_DECLTYPE
+%token KW_DEFAULT
 %token KW_DELETE
 %token KW_DOUBLE
 %token KW_DYNAMIC_CAST
@@ -271,6 +287,7 @@ pop_struct() {
 %token KW_NAMESPACE
 %token KW_NEW
 %token KW_NOEXCEPT
+%token KW_NULLPTR
 %token KW_OPERATOR
 %token KW_PRIVATE
 %token KW_PROTECTED
@@ -281,6 +298,7 @@ pop_struct() {
 %token KW_SIGNED
 %token KW_SIZEOF
 %token KW_STATIC
+%token KW_STATIC_ASSERT
 %token KW_STATIC_CAST
 %token KW_STRUCT
 %token KW_TEMPLATE
@@ -338,7 +356,7 @@ pop_struct() {
 %type <u.type> enum_element_type
 /*%type <u.type> typedefname*/
 %type <u.identifier> name
-%type <str> string
+%type <u.expr> string_literal
 
 /* We need to treat KW_OPERATOR as a scopable keyword. */
 %type <u.identifier> KW_OPERATOR
@@ -511,10 +529,31 @@ declaration:
     current_scope->add_declaration(make_property, global_scope, current_lexer, @1);
   }
 }
-         | KW_MAKE_SEQ '(' name ',' name ',' name ')' ';'
+        | KW_MAKE_SEQ '(' name ',' name ',' name ')' ';'
 {
   CPPMakeSeq *make_seq = new CPPMakeSeq($3->get_simple_name(), $5->get_simple_name(), $7->get_simple_name(), @1.file);
   current_scope->add_declaration(make_seq, global_scope, current_lexer, @1);
+}
+        | KW_STATIC_ASSERT '(' const_expr ',' string_literal ')'
+{
+  CPPExpression::Result result = $3->evaluate();
+  if (result._type == CPPExpression::RT_error) {
+    yywarning("static_assert requires a constant expression", @3);
+  } else if (!result.as_boolean()) {
+    stringstream str;
+    str << *$5;
+    yywarning("static_assert failed: " + str.str(), @3);
+  }
+}
+        | KW_STATIC_ASSERT '(' const_expr ')'
+{
+  // This alternative version of static_assert was introduced in C++17.
+  CPPExpression::Result result = $3->evaluate();
+  if (result._type == CPPExpression::RT_error) {
+    yywarning("static_assert requires a constant expression", @3);
+  } else if (!result.as_boolean()) {
+    yywarning("static_assert failed", @3);
+  }
 }
         ;
 
@@ -538,56 +577,60 @@ storage_class:
 {
   $$ = 0;
 }
-        | storage_class KW_EXTERN
+        | KW_EXTERN storage_class
 {
-  $$ = $1 | (int)CPPInstance::SC_extern;
+  $$ = $2 | (int)CPPInstance::SC_extern;
 }
-        | storage_class KW_EXTERN string
+        | KW_EXTERN SIMPLE_STRING storage_class
 {
-  $$ = $1 | (int)CPPInstance::SC_extern;
-  if ($3 == "C") {
+  $$ = $3 | (int)CPPInstance::SC_extern;
+  if ($2 == "C") {
     $$ |= (int)CPPInstance::SC_c_binding;
-  } else if ($3 == "C++") {
+  } else if ($2 == "C++") {
     $$ &= ~(int)CPPInstance::SC_c_binding;
   } else {
-    yywarning("Ignoring unknown linkage type \"" + $3 + "\"", @3);
+    yywarning("Ignoring unknown linkage type \"" + $2 + "\"", @2);
   }
 }
-        | storage_class KW_STATIC
+        | KW_STATIC storage_class
 {
-  $$ = $1 | (int)CPPInstance::SC_static;
+  $$ = $2 | (int)CPPInstance::SC_static;
 }
-        | storage_class KW_INLINE
+        | KW_INLINE storage_class
 {
-  $$ = $1 | (int)CPPInstance::SC_inline;
+  $$ = $2 | (int)CPPInstance::SC_inline;
 }
-        | storage_class KW_VIRTUAL
+        | KW_VIRTUAL storage_class
 {
-  $$ = $1 | (int)CPPInstance::SC_virtual;
+  $$ = $2 | (int)CPPInstance::SC_virtual;
 }
-        | storage_class KW_EXPLICIT
+        | KW_EXPLICIT storage_class
 {
-  $$ = $1 | (int)CPPInstance::SC_explicit;
+  $$ = $2 | (int)CPPInstance::SC_explicit;
 }
-        | storage_class KW_VOLATILE
+        | KW_REGISTER storage_class
 {
-  $$ = $1 | (int)CPPInstance::SC_volatile;
+  $$ = $2 | (int)CPPInstance::SC_register;
 }
-        | storage_class KW_MUTABLE
+        | KW_VOLATILE storage_class
 {
-  $$ = $1 | (int)CPPInstance::SC_mutable;
+  $$ = $2 | (int)CPPInstance::SC_volatile;
 }
-        | storage_class KW_REGISTER
+        | KW_MUTABLE storage_class
 {
-  $$ = $1 | (int)CPPInstance::SC_register;
+  $$ = $2 | (int)CPPInstance::SC_mutable;
 }
-        | storage_class KW_BLOCKING
+        | KW_CONSTEXPR storage_class
 {
-  $$ = $1 | (int)CPPInstance::SC_blocking;
+  $$ = $2 | (int)CPPInstance::SC_constexpr;
 }
-        | storage_class KW_EXTENSION
+        | KW_BLOCKING storage_class
 {
-  $$ = $1 | (int)CPPInstance::SC_extension;
+  $$ = $2 | (int)CPPInstance::SC_blocking;
+}
+        | KW_EXTENSION storage_class
+{
+  $$ = $2 | (int)CPPInstance::SC_extension;
 }
         ;
 
@@ -771,16 +814,18 @@ function_prototype:
 }
         formal_parameter_list ')' function_post
 {
-  pop_scope();
   CPPType *type;
-  if ($1->get_simple_name() == current_scope->get_simple_name()) {
+  if ($1->get_simple_name() == current_scope->get_simple_name() ||
+      $1->get_simple_name() == string("~") + current_scope->get_simple_name()) {
     // This is a constructor, and has no return.
     type = new CPPSimpleType(CPPSimpleType::T_void);
   } else {
     // This isn't a constructor, so it has an implicit return type of
     // int.
+    yywarning("function has no return type, assuming int", @1);
     type = new CPPSimpleType(CPPSimpleType::T_int);
   }
+  pop_scope();
 
   CPPInstanceIdentifier *ii = new CPPInstanceIdentifier($1);
   ii->add_func_modifier($4, $6);
@@ -822,7 +867,7 @@ function_prototype:
     yyerror("Invalid destructor name: ~" + $2->get_fully_scoped_name(), @2);
   } else {
     CPPIdentifier *ident =
-      new CPPIdentifier("~" + $2->get_simple_name(), @2.file);
+      new CPPIdentifier("~" + $2->get_simple_name(), @2);
     delete $2;
 
     CPPType *type;
@@ -849,6 +894,9 @@ function_prototype:
 {
   pop_scope();
   CPPType *type = $1->find_type(current_scope, global_scope, false, current_lexer);
+  if (type == NULL) {
+    yyerror(string("internal error resolving type ") + $1->get_fully_scoped_name(), @1);
+  }
   assert(type != NULL);
 
   CPPInstanceIdentifier *ii = $4;
@@ -864,6 +912,9 @@ function_prototype:
 {
   pop_scope();
   CPPType *type = $1->find_type(current_scope, global_scope, false, current_lexer);
+  if (type == NULL) {
+    yyerror(string("internal error resolving type ") + $1->get_fully_scoped_name(), @1);
+  }
   assert(type != NULL);
 
   CPPInstanceIdentifier *ii = $5;
@@ -897,7 +948,7 @@ function_prototype:
   string name = "operator typecast " + $2->get_simple_name();
   CPPIdentifier *ident = $1;
   if (ident == NULL) {
-    ident = new CPPIdentifier(name, @1.file);
+    ident = new CPPIdentifier(name, @2);
   } else {
     ident->add_name(name);
   }
@@ -918,7 +969,7 @@ function_prototype:
 
   CPPIdentifier *ident = $1;
   if (ident == NULL) {
-    ident = new CPPIdentifier("operator typecast", @1.file);
+    ident = new CPPIdentifier("operator typecast", @4);
   } else {
     ident->add_name("operator typecast");
   }
@@ -1138,6 +1189,7 @@ template_declaration:
 {
   pop_scope();
 }
+	| KW_TEMPLATE type_like_declaration
         ;
 
 template_formal_parameters:
@@ -1205,6 +1257,9 @@ template_formal_parameter_type:
         | TYPENAME_IDENTIFIER
 {
   $$ = $1->find_type(current_scope, global_scope, false, current_lexer);
+  if ($$ == NULL) {
+    yyerror(string("internal error resolving type ") + $1->get_fully_scoped_name(), @1);
+  }
   assert($$ != NULL);
 }
         ;
@@ -1222,17 +1277,37 @@ instance_identifier:
   // like a regular function.
   CPPIdentifier *ident = $1;
   if (ident == NULL) {
-    ident = new CPPIdentifier("operator "+$2, @2.file);
+    ident = new CPPIdentifier("operator "+$2, @2);
   } else {
     ident->_names.push_back("operator "+$2);
   }
 
+  $$ = new CPPInstanceIdentifier(ident);
+}
+        | KW_OPERATOR SIMPLE_STRING IDENTIFIER
+{
+  // A C++11 literal operator.
+  if (!$2.empty()) {
+    yyerror("expected empty string", @2);
+  }
+  CPPIdentifier *ident = $1;
+  if (ident == NULL) {
+    ident = new CPPIdentifier("operator \"\" "+$3->get_simple_name(), @3);
+  } else {
+    ident->_names.push_back("operator \"\" "+$3->get_simple_name());
+  }
+
   $$ = new CPPInstanceIdentifier(ident);
 }
         | KW_CONST instance_identifier  %prec UNARY
 {
   $$ = $2;
   $$->add_modifier(IIT_const);
+}
+        | KW_VOLATILE instance_identifier  %prec UNARY
+{
+  $$ = $2;
+  $$->add_modifier(IIT_volatile);
 }
         | '*' instance_identifier  %prec UNARY
 {
@@ -1394,20 +1469,22 @@ formal_parameter:
 {
   $$ = new CPPInstance($1, $2, 0, @2.file);
   $$->set_initializer($3);
-}
-        | IDENTIFIER formal_parameter_identifier maybe_initialize
-{
-  yywarning("Not a type: " + $1->get_fully_scoped_name(), @1);
-  CPPType *type =
-    CPPType::new_type(new CPPSimpleType(CPPSimpleType::T_unknown));
-  $$ = new CPPInstance(type, $2, 0, @2.file);
-  $$->set_initializer($3);
 }
         | KW_CONST type formal_parameter_identifier maybe_initialize
 {
   $3->add_modifier(IIT_const);
   $$ = new CPPInstance($2, $3, 0, @3.file);
   $$->set_initializer($4);
+}
+        | KW_CONST KW_REGISTER type formal_parameter_identifier maybe_initialize
+{
+  $4->add_modifier(IIT_const);
+  $$ = new CPPInstance($3, $4, 0, @3.file);
+  $$->set_initializer($5);
+}
+        | KW_REGISTER formal_parameter
+{
+  $$ = $2;
 }
         | formal_const_expr
 {
@@ -1435,6 +1512,11 @@ not_paren_formal_parameter_identifier:
 {
   $$ = $2;
   $$->add_modifier(IIT_const);
+}
+        | KW_VOLATILE not_paren_formal_parameter_identifier  %prec UNARY
+{
+  $$ = $2;
+  $$->add_modifier(IIT_volatile);
 }
         | '*' not_paren_formal_parameter_identifier  %prec UNARY
 {
@@ -1480,6 +1562,11 @@ formal_parameter_identifier:
 {
   $$ = $2;
   $$->add_modifier(IIT_const);
+}
+        | KW_VOLATILE formal_parameter_identifier  %prec UNARY
+{
+  $$ = $2;
+  $$->add_modifier(IIT_volatile);
 }
         | '*' formal_parameter_identifier  %prec UNARY
 {
@@ -1528,6 +1615,11 @@ empty_instance_identifier:
 {
   $$ = $2;
   $$->add_modifier(IIT_const);
+}
+        | KW_VOLATILE empty_instance_identifier  %prec UNARY
+{
+  $$ = $2;
+  $$->add_modifier(IIT_volatile);
 }
         | '*' empty_instance_identifier  %prec UNARY
 {
@@ -1570,6 +1662,9 @@ type:
         | TYPENAME_IDENTIFIER
 {
   $$ = $1->find_type(current_scope, global_scope, false, current_lexer);
+  if ($$ == NULL) {
+    yyerror(string("internal error resolving type ") + $1->get_fully_scoped_name(), @1);
+  }
   assert($$ != NULL);
 }
         | KW_TYPENAME name
@@ -1619,6 +1714,19 @@ type:
     }
     $$ = et;
   }
+}
+        | KW_DECLTYPE '(' const_expr ')'
+{
+  $$ = $3->determine_type();
+  if ($$ == (CPPType *)NULL) {
+    stringstream str;
+    str << *$3;
+    yyerror("could not determine type of " + str.str(), @3);
+  }
+}
+        | KW_AUTO
+{
+  $$ = new CPPSimpleType(CPPSimpleType::T_auto);
 }
         ;
 
@@ -1630,6 +1738,9 @@ type_decl:
         | TYPENAME_IDENTIFIER
 {
   $$ = $1->find_type(current_scope, global_scope, false, current_lexer);
+  if ($$ == NULL) {
+    yyerror(string("internal error resolving type ") + $1->get_fully_scoped_name(), @1);
+  }
   assert($$ != NULL);
 }
         | KW_TYPENAME name
@@ -1697,6 +1808,19 @@ type_decl:
     }
     $$ = et;
   }
+}
+        | KW_DECLTYPE '(' const_expr ')'
+{
+  $$ = $3->determine_type();
+  if ($$ == (CPPType *)NULL) {
+    stringstream str;
+    str << *$3;
+    yyerror("could not determine type of " + str.str(), @3);
+  }
+}
+        | KW_AUTO
+{
+  $$ = new CPPSimpleType(CPPSimpleType::T_auto);
 }
         ;
 
@@ -1708,6 +1832,9 @@ predefined_type:
         | TYPENAME_IDENTIFIER
 {
   $$ = $1->find_type(current_scope, global_scope, false, current_lexer);
+  if ($$ == NULL) {
+    yyerror(string("internal error resolving type ") + $1->get_fully_scoped_name(), @1);
+  }
   assert($$ != NULL);
 }
         | KW_TYPENAME name
@@ -1980,8 +2107,32 @@ namespace_declaration:
         cpp '}'
 {
   pop_scope();
+}
+        | KW_INLINE KW_NAMESPACE name '{'
+{
+  CPPScope *scope = $3->find_scope(current_scope, global_scope, current_lexer);
+  if (scope == NULL) {
+    // This must be a new namespace declaration.
+    CPPScope *parent_scope =
+      $3->get_scope(current_scope, global_scope, current_lexer);
+    if (parent_scope == NULL) {
+      parent_scope = current_scope;
+    }
+    scope = new CPPScope(parent_scope, $3->_names.back(), V_public);
+  }
+
+  CPPNamespace *nspace = new CPPNamespace($3, scope, @2.file);
+  nspace->_is_inline = true;
+  current_scope->add_declaration(nspace, global_scope, current_lexer, @2);
+  current_scope->define_namespace(nspace);
+  push_scope(scope);
+}
+        cpp '}'
+{
+  pop_scope();
 }
         | KW_NAMESPACE '{' cpp '}'
+        | KW_INLINE KW_NAMESPACE '{' cpp '}'
         ;
 
 using_declaration:
@@ -2134,48 +2285,34 @@ code_block:
 
 element:
         REAL
-{
-}
         | INTEGER
-{
-}
-        | STRING
-{
-}
+        | SIMPLE_STRING
+        | STRING_LITERAL
+        | CUSTOM_LITERAL
         | CHAR_TOK
-{
-}
         | IDENTIFIER
-{
-}
         | TYPENAME_IDENTIFIER
-{
-}
         | SCOPING
-{
-}
         | SIMPLE_IDENTIFIER
-{
-}
         | ELLIPSIS | OROR | ANDAND
         | EQCOMPARE | NECOMPARE | LECOMPARE | GECOMPARE
         | LSHIFT | RSHIFT | POINTSAT_STAR | DOT_STAR | POINTSAT
         | SCOPE | PLUSPLUS | MINUSMINUS
         | TIMESEQUAL | DIVIDEEQUAL | MODEQUAL | PLUSEQUAL | MINUSEQUAL
         | OREQUAL | ANDEQUAL | XOREQUAL | LSHIFTEQUAL | RSHIFTEQUAL
-        | KW_BOOL | KW_CATCH | KW_CHAR | KW_CHAR16_T | KW_CHAR32_T
-        | KW_WCHAR_T | KW_CLASS | KW_CONST
+        | KW_ALIGNAS | KW_ALIGNOF | KW_AUTO | KW_BOOL | KW_CATCH
+        | KW_CHAR | KW_CHAR16_T | KW_CHAR32_T
+        | KW_CLASS | KW_CONST | KW_CONSTEXPR | KW_DECLTYPE | KW_DEFAULT
         | KW_DELETE | KW_DOUBLE | KW_DYNAMIC_CAST | KW_ELSE | KW_ENUM
         | KW_EXTERN | KW_EXPLICIT | KW_FALSE
         | KW_FLOAT | KW_FRIEND | KW_FOR | KW_GOTO
-        | KW_IF | KW_INLINE | KW_INT
-        | KW_LONG | KW_MUTABLE | KW_NEW | KW_PRIVATE | KW_PROTECTED
+        | KW_IF | KW_INLINE | KW_INT | KW_LONG | KW_MUTABLE
+        | KW_NEW | KW_NULLPTR | KW_OPERATOR | KW_PRIVATE | KW_PROTECTED
         | KW_PUBLIC | KW_PUBLISHED | KW_REGISTER | KW_RETURN
-        | KW_SHORT | KW_SIGNED | KW_SIZEOF | KW_STATIC | KW_STATIC_CAST
-        | KW_STRUCT | KW_THROW | KW_TRUE | KW_TRY | KW_TYPEDEF | KW_TYPENAME
-        | KW_UNION | KW_UNSIGNED | KW_VIRTUAL | KW_VOID | KW_VOLATILE
-        | KW_WHILE
-        | KW_OPERATOR
+        | KW_SHORT | KW_SIGNED | KW_SIZEOF | KW_STATIC | KW_STATIC_ASSERT
+        | KW_STATIC_CAST | KW_STRUCT | KW_THROW | KW_TRUE | KW_TRY
+        | KW_TYPEDEF | KW_TYPENAME | KW_UNION | KW_UNSIGNED | KW_VIRTUAL
+        | KW_VOID | KW_VOLATILE | KW_WCHAR_T | KW_WHILE
 {
 }
         | '+' | '-' | '*' | '/' | '&' | '|' | '^' | '!' | '~' | '=' | '%'
@@ -2236,6 +2373,10 @@ no_angle_bracket_const_expr:
         | KW_SIZEOF '(' full_type ')' %prec UNARY
 {
   $$ = new CPPExpression(CPPExpression::sizeof_func($3));
+}
+        | KW_ALIGNOF '(' full_type ')' %prec UNARY
+{
+  $$ = new CPPExpression(CPPExpression::alignof_func($3));
 }
         | '!' no_angle_bracket_const_expr    %prec UNARY
 {
@@ -2247,15 +2388,7 @@ no_angle_bracket_const_expr:
 }
         | '-' no_angle_bracket_const_expr    %prec UNARY
 {
-  if ($2->_type == CPPExpression::T_integer) {
-    $$ = $2;
-    $$->_u._integer = -$$->_u._integer;
-  } else if ($2->_type == CPPExpression::T_real) {
-    $$ = $2;
-    $$->_u._real = -$$->_u._real;
-  } else {
-    $$ = new CPPExpression(UNARY_MINUS, $2);
-  }
+  $$ = new CPPExpression(UNARY_MINUS, $2);
 }
         | '*' no_angle_bracket_const_expr    %prec UNARY
 {
@@ -2381,6 +2514,9 @@ const_expr:
 {
   // A constructor call.
   CPPType *type = $1->find_type(current_scope, global_scope, false, current_lexer);
+  if (type == NULL) {
+    yyerror(string("internal error resolving type ") + $1->get_fully_scoped_name(), @1);
+  }
   assert(type != NULL);
   $$ = new CPPExpression(CPPExpression::construct_op(type, $3));
 }
@@ -2463,6 +2599,10 @@ const_expr:
         | KW_SIZEOF '(' full_type ')' %prec UNARY
 {
   $$ = new CPPExpression(CPPExpression::sizeof_func($3));
+}
+        | KW_ALIGNOF '(' full_type ')' %prec UNARY
+{
+  $$ = new CPPExpression(CPPExpression::alignof_func($3));
 }
         | KW_NEW predefined_type %prec UNARY
 {
@@ -2482,15 +2622,7 @@ const_expr:
 }
         | '-' const_expr    %prec UNARY
 {
-  if ($2->_type == CPPExpression::T_integer) {
-    $$ = $2;
-    $$->_u._integer = -$$->_u._integer;
-  } else if ($2->_type == CPPExpression::T_real) {
-    $$ = $2;
-    $$->_u._real = -$$->_u._real;
-  } else {
-    $$ = new CPPExpression(UNARY_MINUS, $2);
-  }
+  $$ = new CPPExpression(UNARY_MINUS, $2);
 }
         | '*' const_expr    %prec UNARY
 {
@@ -2623,13 +2755,21 @@ const_operand:
 {
   $$ = new CPPExpression($1);
 }
-        | string
+        | string_literal
 {
-  $$ = new CPPExpression($1);
+  $$ = $1;
+}
+        | CUSTOM_LITERAL
+{
+  $$ = $1;
 }
         | IDENTIFIER
 {
   $$ = new CPPExpression($1, current_scope, global_scope, current_lexer);
+}
+        | KW_NULLPTR
+{
+  $$ = new CPPExpression(CPPExpression::get_nullptr());
 }
         ;
 
@@ -2660,6 +2800,10 @@ formal_const_expr:
         | KW_SIZEOF '(' full_type ')' %prec UNARY
 {
   $$ = new CPPExpression(CPPExpression::sizeof_func($3));
+}
+        | KW_ALIGNOF '(' full_type ')' %prec UNARY
+{
+  $$ = new CPPExpression(CPPExpression::alignof_func($3));
 }
         | KW_NEW predefined_type %prec UNARY
 {
@@ -2679,15 +2823,7 @@ formal_const_expr:
 }
         | '-' const_expr    %prec UNARY
 {
-  if ($2->_type == CPPExpression::T_integer) {
-    $$ = $2;
-    $$->_u._integer = -$$->_u._integer;
-  } else if ($2->_type == CPPExpression::T_real) {
-    $$ = $2;
-    $$->_u._real = -$$->_u._real;
-  } else {
-    $$ = new CPPExpression(UNARY_MINUS, $2);
-  }
+  $$ = new CPPExpression(UNARY_MINUS, $2);
 }
         | '&' const_expr    %prec UNARY
 {
@@ -2816,9 +2952,21 @@ formal_const_operand:
 {
   $$ = new CPPExpression($1);
 }
-        | string
+        | string_literal
 {
-  $$ = new CPPExpression($1);
+  $$ = $1;
+}
+        | CUSTOM_LITERAL
+{
+  $$ = $1;
+}
+        | IDENTIFIER
+{
+  $$ = new CPPExpression($1, current_scope, global_scope, current_lexer);
+}
+        | KW_NULLPTR
+{
+  $$ = new CPPExpression(CPPExpression::get_nullptr());
 }
         ;
 
@@ -2850,6 +2998,9 @@ typedefname:
         TYPENAME_IDENTIFIER
 {
   CPPType *type = $1->find_type(current_scope, global_scope, false, current_lexer);
+  if (type == NULL) {
+    yyerror(string("internal error resolving type ") + $1->get_fully_scoped_name(), @1);
+  }
   assert(type != NULL);
   $$ = type;
 }
@@ -2872,14 +3023,29 @@ name:
 }
         ;
 
-string:
-        STRING
+string_literal:
+        SIMPLE_STRING
 {
+  $$ = new CPPExpression($1);
+}
+        | STRING_LITERAL
+{
+  $$ = $1;
+}
+        | string_literal SIMPLE_STRING
+{
+  // The right string takes on the literal type of the left.
   $$ = $1;
+  $$->_str += $2;
 }
-        | string STRING
+        | string_literal STRING_LITERAL
 {
-  $$ = $1 + $2;
+  // We have to check that the two literal types match up.
+  $$ = $1;
+  if ($2->_type != CPPExpression::T_string && $2->_type != $1->_type) {
+    yywarning("cannot concatenate two string literals of different types", @$);
+  }
+  $$->_str += $2->_str;
 }
         ;
 

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

@@ -68,8 +68,8 @@ class cppyystype {
 public:
   string str;
   union {
-    int integer;
-    double real;
+    unsigned long long integer;
+    long double real;
     CPPScope *scope;
     CPPDeclaration *decl;
     CPPInstance *instance;

+ 3 - 1
dtool/src/cppparser/cppEnumType.cxx

@@ -19,6 +19,7 @@
 #include "cppConstType.h"
 #include "cppScope.h"
 #include "cppParser.h"
+#include "cppIdentifier.h"
 #include "indent.h"
 
 ////////////////////////////////////////////////////////////////////
@@ -82,6 +83,7 @@ add_element(const string &name, CPPExpression *value) {
     inst = new CPPInstance(CPPType::new_type(new CPPConstType(_element_type)), ident);
   }
 
+  inst->_storage_class |= CPPInstance::SC_constexpr;
   _elements.push_back(inst);
 
   if (value == (CPPExpression *)NULL) {
@@ -176,7 +178,7 @@ substitute_decl(CPPDeclaration::SubstDecl &subst,
 
   bool any_changed = false;
 
-  for (int i = 0; i < _elements.size(); ++i) {
+  for (size_t i = 0; i < _elements.size(); ++i) {
     CPPInstance *elem_rep =
       _elements[i]->substitute_decl(subst, current_scope, global_scope)
       ->as_instance();

+ 318 - 23
dtool/src/cppparser/cppExpression.cxx

@@ -201,6 +201,19 @@ output(ostream &out) const {
   }
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: CPPExpression::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+CPPExpression::
+CPPExpression(unsigned long long value) :
+  CPPDeclaration(CPPFile())
+{
+  _type = T_integer;
+  _u._integer = value;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: CPPExpression::Constructor
 //       Access: Public
@@ -220,7 +233,7 @@ CPPExpression(int value) :
 //  Description:
 ////////////////////////////////////////////////////////////////////
 CPPExpression::
-CPPExpression(double value) :
+CPPExpression(long double value) :
   CPPDeclaration(CPPFile())
 {
   _type = T_real;
@@ -397,6 +410,89 @@ sizeof_func(CPPType *type) {
   return expr;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: CPPExpression::named alignof_func constructor
+//       Access: Public, Static
+//  Description:
+////////////////////////////////////////////////////////////////////
+CPPExpression CPPExpression::
+alignof_func(CPPType *type) {
+  CPPExpression expr(0);
+  expr._type = T_alignof;
+  expr._u._typecast._to = type;
+  expr._u._typecast._op1 = NULL;
+  return expr;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CPPExpression::named literal constructor
+//       Access: Public, Static
+//  Description:
+////////////////////////////////////////////////////////////////////
+CPPExpression CPPExpression::
+literal(unsigned long long value, CPPInstance *lit_op) {
+  CPPExpression expr(0);
+  expr._type = T_literal;
+  expr._u._literal._value = new CPPExpression(value);
+  expr._u._literal._operator = lit_op;
+  return expr;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CPPExpression::named literal constructor
+//       Access: Public, Static
+//  Description:
+////////////////////////////////////////////////////////////////////
+CPPExpression CPPExpression::
+literal(long double value, CPPInstance *lit_op) {
+  CPPExpression expr(0);
+  expr._type = T_literal;
+  expr._u._literal._value = new CPPExpression(value);
+  expr._u._literal._operator = lit_op;
+  return expr;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CPPExpression::named literal constructor
+//       Access: Public, Static
+//  Description:
+////////////////////////////////////////////////////////////////////
+CPPExpression CPPExpression::
+literal(CPPExpression *value, CPPInstance *lit_op) {
+  CPPExpression expr(0);
+  expr._type = T_literal;
+  expr._u._literal._value = value;
+  expr._u._literal._operator = lit_op;
+  return expr;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CPPExpression::named raw_literal constructor
+//       Access: Public, Static
+//  Description:
+////////////////////////////////////////////////////////////////////
+CPPExpression CPPExpression::
+raw_literal(const string &raw, CPPInstance *lit_op) {
+  CPPExpression expr(0);
+  expr._type = T_raw_literal;
+  expr._str = raw;
+  expr._u._literal._value = (CPPExpression *)NULL;
+  expr._u._literal._operator = lit_op;
+  return expr;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CPPExpression::get_nullptr
+//       Access: Public, Static
+//  Description:
+////////////////////////////////////////////////////////////////////
+const CPPExpression &CPPExpression::
+get_nullptr() {
+  static CPPExpression expr(0);
+  expr._type = T_nullptr;
+  return expr;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: CPPExpression::Destructor
 //       Access: Public
@@ -416,18 +512,29 @@ evaluate() const {
   Result r1, r2;
 
   switch (_type) {
+  case T_nullptr:
+    return Result((void *)0);
+
   case T_integer:
-    return Result(_u._integer);
+    return Result((int)_u._integer);
 
   case T_real:
-    return Result(_u._real);
+    return Result((double)_u._real);
 
   case T_string:
+  case T_wstring:
+  case T_u8string:
+  case T_u16string:
+  case T_u32string:
     return Result();
 
   case T_variable:
     if (_u._variable->_type != NULL &&
         _u._variable->_initializer != NULL) {
+      // A constexpr variable, which is treated as const.
+      if (_u._variable->_storage_class & CPPInstance::SC_constexpr) {
+        return _u._variable->_initializer->evaluate();
+      }
       // A const variable.  Fetch its assigned value.
       CPPConstType *const_type = _u._variable->_type->as_const_type();
       if (const_type != NULL) {
@@ -468,6 +575,17 @@ evaluate() const {
   case T_sizeof:
     return Result();
 
+  case T_alignof:
+    if (_u._typecast._to != NULL) {
+      // Check if the type is defined with an alignas.  TODO: this should
+      // probably be moved to a virtual getter on CPPType.
+      CPPExtensionType *etype = _u._typecast._to->as_extension_type();
+      if (etype != NULL && etype->_alignment != NULL) {
+        return etype->_alignment->evaluate();
+      }
+    }
+    return Result();
+
   case T_binary_operation:
     assert(_u._op._op2 != NULL);
     r2 = _u._op._op2->evaluate();
@@ -503,23 +621,39 @@ evaluate() const {
       // long as we can evaluate the second one *and* that comes out
       // to be true.
       if (_u._op._operator == OROR && r2._type == RT_integer &&
-          r2.as_integer() != 0) {
+          r2.as_boolean()) {
         return r2;
       }
 
       // Ditto for the operator being && and the second one coming out
       // false.
       if (_u._op._operator == ANDAND && r2._type == RT_integer &&
-          r2.as_integer() == 0) {
+          !r2.as_boolean()) {
         return r2;
       }
 
+      // Also for the operator being [] and the operand being a string.
+      if (_u._op._operator == '[' && r2._type == RT_integer &&
+          (_u._op._op1->_type == T_string ||
+           _u._op._op1->_type == T_u8string)) {
+
+        int index = (int)r2.as_integer();
+        if ((size_t)index == _u._op._op1->_str.size()) {
+          return Result(0);
+        } else if (index >= 0 && (size_t)index < _u._op._op1->_str.size()) {
+          return Result(_u._op._op1->_str[(size_t)index]);
+        } else {
+          cerr << "array index " << index << " out of bounds of string literal "
+               << *_u._op._op1 << "\n";
+        }
+      }
+
       return r1;
     }
 
     switch (_u._op._operator) {
     case UNARY_NOT:
-      return Result(!r1.as_integer());
+      return Result(!r1.as_boolean());
 
     case UNARY_NEGATE:
       return Result(~r1.as_integer());
@@ -569,14 +703,14 @@ evaluate() const {
       return Result(r1.as_integer() & r2.as_integer());
 
     case OROR:
-      if (r1.as_integer()) {
+      if (r1.as_boolean()) {
         return r1;
       } else {
         return r2;
       }
 
     case ANDAND:
-      if (r1.as_integer()) {
+      if (r1.as_boolean()) {
         return r2;
       } else {
         return r1;
@@ -652,6 +786,10 @@ evaluate() const {
       abort();
     }
 
+  case T_literal:
+  case T_raw_literal:
+    return Result();
+
   default:
     cerr << "**invalid operand**\n";
     abort();
@@ -671,25 +809,51 @@ determine_type() const {
   CPPType *t1 = (CPPType *)NULL;
   CPPType *t2 = (CPPType *)NULL;
 
-  CPPType *int_type =
+  static CPPType *nullptr_type =
+    CPPType::new_type(new CPPSimpleType(CPPSimpleType::T_nullptr));
+
+  static CPPType *int_type =
     CPPType::new_type(new CPPSimpleType(CPPSimpleType::T_int));
 
-  CPPType *bool_type =
+  static CPPType *unsigned_long_type =
+    CPPType::new_type(new CPPSimpleType(CPPSimpleType::T_int,
+                                        CPPSimpleType::F_unsigned |
+                                        CPPSimpleType::F_long));
+
+  static CPPType *bool_type =
     CPPType::new_type(new CPPSimpleType(CPPSimpleType::T_bool));
 
-  CPPType *float_type =
+  static CPPType *float_type =
     CPPType::new_type(new CPPSimpleType(CPPSimpleType::T_double));
 
-  CPPType *char_type =
+  static CPPType *char_type =
     CPPType::new_type(new CPPSimpleType(CPPSimpleType::T_char));
 
-  CPPType *const_char_type =
-    CPPType::new_type(new CPPConstType(char_type));
+  static CPPType *wchar_type =
+    CPPType::new_type(new CPPSimpleType(CPPSimpleType::T_wchar_t));
+
+  static CPPType *char16_type =
+    CPPType::new_type(new CPPSimpleType(CPPSimpleType::T_char16_t));
+
+  static CPPType *char32_type =
+    CPPType::new_type(new CPPSimpleType(CPPSimpleType::T_char32_t));
+
+  static CPPType *char_str_type = CPPType::new_type(
+    new CPPPointerType(CPPType::new_type(new CPPConstType(char_type))));
+
+  static CPPType *wchar_str_type = CPPType::new_type(
+    new CPPPointerType(CPPType::new_type(new CPPConstType(wchar_type))));
 
-  CPPType *char_star_type =
-    CPPType::new_type(new CPPPointerType(const_char_type));
+  static CPPType *char16_str_type = CPPType::new_type(
+    new CPPPointerType(CPPType::new_type(new CPPConstType(char16_type))));
+
+  static CPPType *char32_str_type = CPPType::new_type(
+    new CPPPointerType(CPPType::new_type(new CPPConstType(char32_type))));
 
   switch (_type) {
+  case T_nullptr:
+    return nullptr_type;
+
   case T_integer:
     return int_type;
 
@@ -697,7 +861,19 @@ determine_type() const {
     return float_type;
 
   case T_string:
-    return char_star_type;
+    return char_str_type;
+
+  case T_wstring:
+    return wchar_str_type;
+
+  case T_u8string:
+    return char_str_type;
+
+  case T_u16string:
+    return char16_str_type;
+
+  case T_u32string:
+    return char32_str_type;
 
   case T_variable:
     return _u._variable->_type;
@@ -725,7 +901,11 @@ determine_type() const {
     return CPPType::new_type(new CPPPointerType(_u._typecast._to));
 
   case T_sizeof:
-    return int_type;
+  case T_alignof:
+    // Note: this should actually be size_t, but that is defined as a
+    // typedef in parser-inc.  We could try to resolve it, but that's
+    // hacky.  Eh, it's probably not worth the effort to get this right.
+    return unsigned_long_type;
 
   case T_binary_operation:
   case T_trinary_operation:
@@ -819,6 +999,18 @@ determine_type() const {
       abort();
     }
 
+  case T_literal:
+  case T_raw_literal:
+    if (_u._literal._operator != NULL) {
+      CPPType *type = _u._literal._operator->_type;
+
+      CPPFunctionType *ftype = type->as_function_type();
+      if (ftype != (CPPFunctionType *)NULL) {
+        return ftype->_return_type;
+      }
+    }
+    return NULL;
+
   default:
     cerr << "**invalid operand**\n";
     abort();
@@ -842,9 +1034,14 @@ is_fully_specified() const {
   }
 
   switch (_type) {
+  case T_nullptr:
   case T_integer:
   case T_real:
   case T_string:
+  case T_wstring:
+  case T_u8string:
+  case T_u16string:
+  case T_u32string:
     return false;
 
   case T_variable:
@@ -865,6 +1062,7 @@ is_fully_specified() const {
   case T_default_construct:
   case T_default_new:
   case T_sizeof:
+  case T_alignof:
     return _u._typecast._to->is_fully_specified();
 
   case T_trinary_operation:
@@ -882,6 +1080,13 @@ is_fully_specified() const {
   case T_unary_operation:
     return _u._op._op1->is_fully_specified();
 
+  case T_literal:
+    return _u._literal._value->is_fully_specified() &&
+      _u._literal._operator->is_fully_specified();
+
+  case T_raw_literal:
+    return _u._literal._value->is_fully_specified();
+
   default:
     return true;
   }
@@ -972,6 +1177,7 @@ substitute_decl(CPPDeclaration::SubstDecl &subst,
   case T_default_construct:
   case T_default_new:
   case T_sizeof:
+  case T_alignof:
     rep->_u._typecast._to =
       _u._typecast._to->substitute_decl(subst, current_scope, global_scope)
       ->as_type();
@@ -1025,6 +1231,9 @@ is_tbd() const {
   case T_variable:
     if (_u._variable->_type != NULL &&
         _u._variable->_initializer != NULL) {
+      if (_u._variable->_storage_class & CPPInstance::SC_constexpr) {
+        return false;
+      }
       CPPConstType *const_type = _u._variable->_type->as_const_type();
       if (const_type != NULL) {
         return false;
@@ -1042,6 +1251,7 @@ is_tbd() const {
   case T_default_construct:
   case T_default_new:
   case T_sizeof:
+  case T_alignof:
     return _u._typecast._to->is_tbd();
 
   case T_trinary_operation:
@@ -1075,6 +1285,10 @@ is_tbd() const {
 void CPPExpression::
 output(ostream &out, int indent_level, CPPScope *scope, bool) const {
   switch (_type) {
+  case T_nullptr:
+    out << "nullptr";
+    break;
+
   case T_integer:
     out << _u._integer;
     break;
@@ -1090,8 +1304,29 @@ output(ostream &out, int indent_level, CPPScope *scope, bool) const {
     break;
 
   case T_string:
-    out << '"';
+  case T_wstring:
+  case T_u8string:
+  case T_u16string:
+  case T_u32string:
     {
+      switch (_type) {
+      case T_wstring:
+        out << 'L';
+        break;
+      case T_u8string:
+        out << "u8";
+        break;
+      case T_u16string:
+        out << "u";
+        break;
+      case T_u32string:
+        out << "U";
+        break;
+      default:
+        break;
+      }
+      // We don't really care about preserving the encoding for now.
+      out << '"';
       string::const_iterator si;
       for (si = _str.begin(); si != _str.end(); ++si) {
         switch (*si) {
@@ -1135,9 +1370,10 @@ output(ostream &out, int indent_level, CPPScope *scope, bool) const {
     if (_u._variable->_type != NULL &&
         _u._variable->_initializer != NULL &&
         _u._variable->_vis > V_public) {
-      // A const variable.  Fetch its assigned value.
+      // A constexpr or const variable.  Fetch its assigned value.
       CPPConstType *const_type = _u._variable->_type->as_const_type();
-      if (const_type != NULL) {
+      if ((_u._variable->_storage_class & CPPInstance::SC_constexpr) != 0 ||
+          const_type != NULL) {
         _u._variable->_initializer->output(out, indent_level, scope, false);
         break;
       }
@@ -1193,6 +1429,12 @@ output(ostream &out, int indent_level, CPPScope *scope, bool) const {
     out << ")";
     break;
 
+  case T_alignof:
+    out << "alignof(";
+    _u._typecast._to->output(out, indent_level, scope, false);
+    out << ")";
+    break;
+
   case T_unary_operation:
     switch (_u._op._operator) {
     case UNARY_NOT:
@@ -1208,9 +1450,8 @@ output(ostream &out, int indent_level, CPPScope *scope, bool) const {
       break;
 
     case UNARY_MINUS:
-      out << "(- ";
+      out << '-';
       _u._op._op1->output(out, indent_level, scope, false);
-      out << ")";
       break;
 
     case UNARY_STAR:
@@ -1362,6 +1603,24 @@ output(ostream &out, int indent_level, CPPScope *scope, bool) const {
     out << ")";
     break;
 
+  case T_literal:
+    _u._literal._value->output(out, indent_level, scope, false);
+    if (_u._literal._operator != NULL) {
+      string name = _u._literal._operator->get_simple_name();
+      assert(name.substr(0, 12) == "operator \"\" ");
+      out << name.substr(12);
+    }
+    break;
+
+  case T_raw_literal:
+    out << _str;
+    if (_u._literal._operator != NULL) {
+      string name = _u._literal._operator->get_simple_name();
+      assert(name.substr(0, 12) == "operator \"\" ");
+      out << name.substr(12);
+    }
+    break;
+
   default:
     out << "(** invalid operand type " << (int)_type << " **)";
   }
@@ -1456,6 +1715,9 @@ is_equal(const CPPDeclaration *other) const {
   }
 
   switch (_type) {
+  case T_nullptr:
+    return true;
+
   case T_integer:
     return _u._integer == ot->_u._integer;
 
@@ -1463,6 +1725,10 @@ is_equal(const CPPDeclaration *other) const {
     return _u._real == ot->_u._real;
 
   case T_string:
+  case T_wstring:
+  case T_u8string:
+  case T_u16string:
+  case T_u32string:
     return _str == ot->_str;
 
   case T_variable:
@@ -1483,6 +1749,7 @@ is_equal(const CPPDeclaration *other) const {
   case T_default_construct:
   case T_default_new:
   case T_sizeof:
+  case T_alignof:
     return _u._typecast._to == ot->_u._typecast._to;
 
   case T_unary_operation:
@@ -1496,6 +1763,14 @@ is_equal(const CPPDeclaration *other) const {
     return *_u._op._op1 == *ot->_u._op._op1 &&
       *_u._op._op2 == *ot->_u._op._op2;
 
+  case T_literal:
+    return *_u._literal._value == *ot->_u._literal._value &&
+      _u._literal._operator == ot->_u._literal._operator;
+
+  case T_raw_literal:
+    return _str == ot->_str &&
+      _u._literal._operator == ot->_u._literal._operator;
+
   default:
     cerr << "(** invalid operand type " << (int)_type << " **)";
   }
@@ -1520,6 +1795,9 @@ is_less(const CPPDeclaration *other) const {
   }
 
   switch (_type) {
+  case T_nullptr:
+    return false;
+
   case T_integer:
     return _u._integer < ot->_u._integer;
 
@@ -1527,6 +1805,10 @@ is_less(const CPPDeclaration *other) const {
     return _u._real < ot->_u._real;
 
   case T_string:
+  case T_wstring:
+  case T_u8string:
+  case T_u16string:
+  case T_u32string:
     return _str < ot->_str;
 
   case T_variable:
@@ -1549,6 +1831,7 @@ is_less(const CPPDeclaration *other) const {
   case T_default_construct:
   case T_default_new:
   case T_sizeof:
+  case T_alignof:
     return _u._typecast._to < ot->_u._typecast._to;
 
   case T_trinary_operation:
@@ -1566,6 +1849,18 @@ is_less(const CPPDeclaration *other) const {
   case T_unary_operation:
     return *_u._op._op1 < *ot->_u._op._op1;
 
+  case T_literal:
+    if (_u._literal._operator != ot->_u._literal._operator) {
+      return _u._literal._operator < ot->_u._literal._operator;
+    }
+    return *_u._literal._value < *ot->_u._literal._value;
+
+  case T_raw_literal:
+    if (_u._literal._operator != ot->_u._literal._operator) {
+      return _u._literal._operator < ot->_u._literal._operator;
+    }
+    return _str < ot->_str;
+
   default:
     cerr << "(** invalid operand type " << (int)_type << " **)";
   }

+ 25 - 5
dtool/src/cppparser/cppExpression.h

@@ -30,9 +30,10 @@ class CPPFunctionGroup;
 ////////////////////////////////////////////////////////////////////
 class CPPExpression : public CPPDeclaration {
 public:
+  CPPExpression(unsigned long long value);
   CPPExpression(int value);
   CPPExpression(const string &value);
-  CPPExpression(double value);
+  CPPExpression(long double value);
   CPPExpression(CPPIdentifier *ident, CPPScope *current_scope,
                 CPPScope *global_scope, CPPPreprocessor *error_sink = NULL);
   CPPExpression(int unary_operator, CPPExpression *op1);
@@ -43,6 +44,14 @@ public:
   static CPPExpression construct_op(CPPType *type, CPPExpression *op1);
   static CPPExpression new_op(CPPType *type, CPPExpression *op1 = NULL);
   static CPPExpression sizeof_func(CPPType *type);
+  static CPPExpression alignof_func(CPPType *type);
+
+  static CPPExpression literal(unsigned long long value, CPPInstance *lit_op);
+  static CPPExpression literal(long double value, CPPInstance *lit_op);
+  static CPPExpression literal(CPPExpression *value, CPPInstance *lit_op);
+  static CPPExpression raw_literal(const string &raw, CPPInstance *lit_op);
+
+  static const CPPExpression &get_nullptr();
 
   ~CPPExpression();
 
@@ -92,9 +101,14 @@ public:
 
 
   enum Type {
+    T_nullptr,
     T_integer,
     T_real,
     T_string,
+    T_wstring,
+    T_u8string,
+    T_u16string,
+    T_u32string,
     T_variable,
     T_function,
     T_unknown_ident,
@@ -104,16 +118,19 @@ public:
     T_new,
     T_default_new,
     T_sizeof,
+    T_alignof,
     T_unary_operation,
     T_binary_operation,
     T_trinary_operation,
+    T_literal,
+    T_raw_literal,
   };
 
   Type _type;
   string _str;
   union {
-    int _integer;
-    double _real;
+    unsigned long long _integer;
+    long double _real;
     CPPInstance *_variable;
     CPPFunctionGroup *_fgroup;
     CPPIdentifier *_ident;
@@ -131,6 +148,11 @@ public:
       CPPExpression *_op2;
       CPPExpression *_op3;
     } _op;
+    class {
+    public:
+      CPPInstance *_operator;
+      CPPExpression *_value;
+    } _literal;
   } _u;
 
 protected:
@@ -146,5 +168,3 @@ operator << (ostream &out, const CPPExpression::Result &result) {
 }
 
 #endif
-
-

+ 2 - 1
dtool/src/cppparser/cppExtensionType.cxx

@@ -29,7 +29,8 @@ CPPExtensionType(CPPExtensionType::Type type,
                  CPPIdentifier *ident, CPPScope *current_scope,
                  const CPPFile &file) :
   CPPType(file),
-  _type(type), _ident(ident)
+  _type(type), _ident(ident),
+  _alignment(NULL)
 {
   if (_ident != NULL) {
     _ident->_native_scope = current_scope;

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

@@ -65,9 +65,9 @@ public:
 
   virtual CPPExtensionType *as_extension_type();
 
-
   Type _type;
   CPPIdentifier *_ident;
+  CPPExpression *_alignment;
 };
 
 ostream &operator << (ostream &out, CPPExtensionType::Type type);

+ 56 - 6
dtool/src/cppparser/cppIdentifier.cxx

@@ -28,9 +28,14 @@
 //  Description:
 ////////////////////////////////////////////////////////////////////
 CPPIdentifier::
-CPPIdentifier(const string &name, const CPPFile &file) : _file(file) {
+CPPIdentifier(const string &name, const CPPFile &file) {
   _names.push_back(CPPNameComponent(name));
   _native_scope = (CPPScope *)NULL;
+  _loc.first_line = 0;
+  _loc.first_column = 0;
+  _loc.last_line = 0;
+  _loc.last_column = 0;
+  _loc.file = file;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -39,7 +44,34 @@ CPPIdentifier(const string &name, const CPPFile &file) : _file(file) {
 //  Description:
 ////////////////////////////////////////////////////////////////////
 CPPIdentifier::
-CPPIdentifier(const CPPNameComponent &name, const CPPFile &file) : _file(file) {
+CPPIdentifier(const CPPNameComponent &name, const CPPFile &file) {
+  _names.push_back(name);
+  _native_scope = (CPPScope *)NULL;
+  _loc.first_line = 0;
+  _loc.first_column = 0;
+  _loc.last_line = 0;
+  _loc.last_column = 0;
+  _loc.file = file;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CPPIdentifier::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+CPPIdentifier::
+CPPIdentifier(const string &name, const cppyyltype &loc) : _loc(loc) {
+  _names.push_back(CPPNameComponent(name));
+  _native_scope = (CPPScope *)NULL;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CPPIdentifier::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+CPPIdentifier::
+CPPIdentifier(const CPPNameComponent &name, const cppyyltype &loc) : _loc(loc) {
   _names.push_back(name);
   _native_scope = (CPPScope *)NULL;
 }
@@ -155,6 +187,12 @@ get_local_name(CPPScope *scope) const {
     // last name.
     CPPScope *my_scope = get_scope(scope, NULL);
 
+    // Strip off template scopes, since they don't add anything
+    // particularly meaningful to the local name.
+    while (my_scope != NULL && my_scope->as_template_scope() != NULL) {
+      my_scope = my_scope->get_parent_scope();
+    }
+
     if (my_scope == NULL) {
       result = get_fully_scoped_name();
     } else if (my_scope == scope) {
@@ -256,7 +294,8 @@ get_scope(CPPScope *current_scope, CPPScope *global_scope,
       if (error_sink != NULL) {
         error_sink->error("Symbol " + _names[i].get_name() +
                           " is not a known scope in " +
-                          scope->get_fully_scoped_name());
+                          scope->get_fully_scoped_name(),
+                          _loc);
       }
       return (CPPScope *)NULL;
     }
@@ -302,7 +341,8 @@ get_scope(CPPScope *current_scope, CPPScope *global_scope,
       if (error_sink != NULL) {
         error_sink->error("Symbol " + _names[i].get_name() +
                           " is not a known scope in " +
-                          scope->get_fully_scoped_name());
+                          scope->get_fully_scoped_name(),
+                          _loc);
       }
       return (CPPScope *)NULL;
     }
@@ -429,7 +469,12 @@ find_symbol(CPPScope *current_scope, CPPScope *global_scope,
 
   CPPDeclaration *sym;
   if (!_names.back().has_templ()) {
-    sym = scope->find_symbol(get_simple_name());
+    if (_names.size() > 1 && scope->get_simple_name() == get_simple_name()) {
+      // An identifier like Class::Class always refers to the class constructor.
+      sym = scope->get_struct_type()->get_constructor();
+    } else {
+      sym = scope->find_symbol(get_simple_name());
+    }
 
   } else {
     sym = scope->find_template(get_simple_name());
@@ -465,7 +510,12 @@ find_symbol(CPPScope *current_scope, CPPScope *global_scope,
 
   CPPDeclaration *sym;
   if (!_names.back().has_templ()) {
-    sym = scope->find_symbol(get_simple_name());
+    if (_names.size() > 1 && scope->get_simple_name() == get_simple_name()) {
+      // An identifier like Class::Class always refers to the class constructor.
+      sym = scope->get_struct_type()->get_constructor();
+    } else {
+      sym = scope->find_symbol(get_simple_name());
+    }
 
   } else {
     sym = scope->find_template(get_simple_name());

+ 4 - 1
dtool/src/cppparser/cppIdentifier.h

@@ -20,6 +20,7 @@
 #include "cppDeclaration.h"
 #include "cppNameComponent.h"
 #include "cppFile.h"
+#include "cppBisonDefs.h"
 
 #include <string>
 #include <vector>
@@ -37,6 +38,8 @@ class CPPIdentifier {
 public:
   CPPIdentifier(const string &name, const CPPFile &file = CPPFile());
   CPPIdentifier(const CPPNameComponent &name, const CPPFile &file = CPPFile());
+  CPPIdentifier(const string &name, const cppyyltype &loc);
+  CPPIdentifier(const CPPNameComponent &name, const cppyyltype &loc);
   void add_name(const string &name);
   void add_name(const CPPNameComponent &name);
 
@@ -91,7 +94,7 @@ public:
   typedef vector<CPPNameComponent> Names;
   Names _names;
   CPPScope *_native_scope;
-  CPPFile _file;
+  cppyyltype _loc;
 };
 
 inline ostream &operator << (ostream &out, const CPPIdentifier &identifier) {

+ 45 - 4
dtool/src/cppparser/cppInstance.cxx

@@ -38,7 +38,8 @@ CPPInstance(CPPType *type, const string &name, int storage_class) :
   CPPDeclaration(CPPFile()),
   _type(type),
   _ident(new CPPIdentifier(name)),
-  _storage_class(storage_class)
+  _storage_class(storage_class),
+  _alignment(NULL)
 {
   _initializer = NULL;
 }
@@ -53,7 +54,8 @@ CPPInstance(CPPType *type, CPPIdentifier *ident, int storage_class) :
   CPPDeclaration(CPPFile()),
   _type(type),
   _ident(ident),
-  _storage_class(storage_class)
+  _storage_class(storage_class),
+  _alignment(NULL)
 {
   _initializer = NULL;
 }
@@ -69,7 +71,8 @@ CPPInstance(CPPType *type, CPPIdentifier *ident, int storage_class) :
 CPPInstance::
 CPPInstance(CPPType *type, CPPInstanceIdentifier *ii, int storage_class,
             const CPPFile &file) :
-  CPPDeclaration(file)
+  CPPDeclaration(file),
+  _alignment(NULL)
 {
   _type = ii->unroll_type(type);
   _ident = ii->_ident;
@@ -102,7 +105,8 @@ CPPInstance(const CPPInstance &copy) :
   _type(copy._type),
   _ident(copy._ident),
   _initializer(copy._initializer),
-  _storage_class(copy._storage_class)
+  _storage_class(copy._storage_class),
+  _alignment(copy._alignment)
 {
   assert(_type != NULL);
 }
@@ -153,6 +157,9 @@ operator == (const CPPInstance &other) const {
   if (_storage_class != other._storage_class) {
     return false;
   }
+  if (_alignment != other._alignment) {
+    return false;
+  }
 
   // We *do* care about the identifier.  We need to differentiate
   // types of function variables, among possibly other things, based
@@ -199,6 +206,9 @@ operator < (const CPPInstance &other) const {
   if (_storage_class != other._storage_class) {
     return _storage_class < other._storage_class;
   }
+  if (_alignment != other._alignment) {
+    return _alignment < other._alignment;
+  }
 
   // We *do* care about the identifier.  We need to differentiate
   // types of function variables, among possibly other things, based
@@ -252,6 +262,29 @@ set_initializer(CPPExpression *initializer) {
   }
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: CPPInstance::set_alignment
+//       Access: Public
+//  Description: Sets the number of bytes to align this instance to.
+////////////////////////////////////////////////////////////////////
+void CPPInstance::
+set_alignment(int align) {
+  _alignment = new CPPExpression(align);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CPPInstance::set_alignment
+//       Access: Public
+//  Description: Sets the expression that is used to determine the
+//               required alignment for the variable.  This should
+//               be a constant expression, but we don't presently
+//               verify that it is.
+////////////////////////////////////////////////////////////////////
+void CPPInstance::
+set_alignment(CPPExpression *const_expr) {
+  _alignment = const_expr;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: CPPInstance::is_scoped
 //       Access: Public
@@ -536,6 +569,11 @@ output(ostream &out, int indent_level, CPPScope *scope, bool complete,
     get_template_scope()->_parameters.write_formal(out, scope);
     indent(out, indent_level);
   }
+
+  if (_alignment != NULL) {
+    out << "alignas(" << *_alignment << ") ";
+  }
+
   if (_storage_class & SC_static) {
     out << "static ";
   }
@@ -563,6 +601,9 @@ output(ostream &out, int indent_level, CPPScope *scope, bool complete,
   if (_storage_class & SC_mutable) {
     out << "mutable ";
   }
+  if (_storage_class & SC_constexpr) {
+    out << "constexpr ";
+  }
 
   string name;
   if (_ident != NULL) {

+ 7 - 4
dtool/src/cppparser/cppInstance.h

@@ -19,7 +19,6 @@
 
 #include "cppDeclaration.h"
 #include "cppType.h"
-#include "cppIdentifier.h"
 #include "cppTemplateParameterList.h"
 
 class CPPInstanceIdentifier;
@@ -47,19 +46,20 @@ public:
     SC_pure_virtual = 0x0080,
     SC_volatile     = 0x0100,
     SC_mutable      = 0x0200,
+    SC_constexpr    = 0x0400,
 
     // This bit is only set by CPPStructType::check_virtual().
-    SC_inherited_virtual = 0x0400,
+    SC_inherited_virtual = 0x0800,
 
     // This is a special "storage class" for methods tagged with the
     // BLOCKING macro (i.e. the special __blocking keyword).  These
     // are methods that might block and therefore need to release
     // Python threads for their duration.
-    SC_blocking     = 0x0800,
+    SC_blocking     = 0x1000,
 
     // And this is for methods tagged with __extension, which declares
     // extension methods defined separately from the source code.
-    SC_extension    = 0x1000,
+    SC_extension    = 0x2000,
   };
 
   CPPInstance(CPPType *type, const string &name, int storage_class = 0);
@@ -78,6 +78,8 @@ public:
   bool operator < (const CPPInstance &other) const;
 
   void set_initializer(CPPExpression *initializer);
+  void set_alignment(int align);
+  void set_alignment(CPPExpression *const_expr);
 
   bool is_scoped() const;
   CPPScope *get_scope(CPPScope *current_scope, CPPScope *global_scope,
@@ -112,6 +114,7 @@ public:
   CPPExpression *_initializer;
 
   int _storage_class;
+  CPPExpression *_alignment;
 
 private:
   typedef map<const CPPTemplateParameterList *, CPPInstance *, CPPTPLCompare> Instantiations;

+ 5 - 0
dtool/src/cppparser/cppInstanceIdentifier.cxx

@@ -286,6 +286,11 @@ r_unroll_type(CPPType *start_type,
     result = new CPPConstType(r_unroll_type(start_type, mi));
     break;
 
+  case IIT_volatile:
+    // Just pass it through for now.
+    result = r_unroll_type(start_type, mi);
+    break;
+
   case IIT_paren:
     result = r_unroll_type(start_type, mi);
     break;

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