Browse Source

more robust temp file and log handling

David Rose 16 years ago
parent
commit
5289285624

+ 2 - 0
direct/src/plugin/Sources.pp

@@ -45,6 +45,7 @@
     p3dSession.h p3dSession.I \
     p3dSplashWindow.h p3dSplashWindow.I \
     p3dStringObject.h \
+    p3dTemporaryFile.h p3dTemporaryFile.I \
     p3dToplevelObject.h \
     p3dUndefinedObject.h \
     p3dWinSplashWindow.h p3dWinSplashWindow.I \
@@ -74,6 +75,7 @@
     p3dSession.cxx \
     p3dSplashWindow.cxx \
     p3dStringObject.cxx \
+    p3dTemporaryFile.cxx \
     p3dToplevelObject.cxx \
     p3dUndefinedObject.cxx \
     p3dWinSplashWindow.cxx \

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

@@ -124,7 +124,8 @@ static void unload_dso();
 ////////////////////////////////////////////////////////////////////
 bool
 load_plugin(const string &p3d_plugin_filename, const string &contents_filename,
-            const string &download_url, const string &platform) {
+            const string &download_url, const string &platform,
+            const string &log_directory, const string &log_basename) {
   string filename = p3d_plugin_filename;
   if (filename.empty()) {
     // Look for the plugin along the path.
@@ -294,7 +295,8 @@ load_plugin(const string &p3d_plugin_filename, const string &contents_filename,
   plugin_loaded = true;
 
   if (!P3D_initialize(P3D_API_VERSION, contents_filename.c_str(),
-                      download_url.c_str(), platform.c_str())) {
+                      download_url.c_str(), platform.c_str(),
+                      log_directory.c_str(), log_basename.c_str())) {
     // Oops, failure to initialize.
     cerr << "Failed to initialize plugin (wrong API version?)\n";
     unload_plugin();

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

@@ -60,7 +60,8 @@ extern P3D_instance_handle_event_func *P3D_instance_handle_event;
 string get_plugin_basename();
 bool 
 load_plugin(const string &p3d_plugin_filename, const string &contents_filename,
-            const string &download_url, const string &platform);
+            const string &download_url, const string &platform,
+            const string &log_directory, const string &log_basename);
 void unload_plugin();
 bool is_plugin_loaded();
 

+ 13 - 0
direct/src/plugin/p3dFileParams.cxx

@@ -122,6 +122,19 @@ lookup_token(const string &keyword) const {
   return string();
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: P3DFileParams::lookup_token_int
+//       Access: Public
+//  Description: Returns the integer value associated with the first
+//               appearance of the named token, or zero if the
+//               token does not appear or is not an integer.
+////////////////////////////////////////////////////////////////////
+int P3DFileParams::
+lookup_token_int(const string &keyword) const {
+  string value = lookup_token(keyword);
+  return atoi(value.c_str());
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: P3DFileParams::has_token
 //       Access: Public

+ 1 - 0
direct/src/plugin/p3dFileParams.h

@@ -36,6 +36,7 @@ public:
 
   inline const string &get_p3d_filename() const;
   string lookup_token(const string &keyword) const;
+  int lookup_token_int(const string &keyword) const;
   bool has_token(const string &keyword) const;
 
   TiXmlElement *make_xml();

+ 52 - 15
direct/src/plugin/p3dInstance.cxx

@@ -25,6 +25,7 @@
 #include "p3dToplevelObject.h"
 #include "p3dUndefinedObject.h"
 #include "p3dMultifileReader.h"
+#include "p3dTemporaryFile.h"
 
 #include <sstream>
 #include <algorithm>
@@ -59,6 +60,8 @@ P3DInstance(P3D_request_ready_func *func,
   _panda_script_object = new P3DToplevelObject;
   _user_data = user_data;
   _request_pending = false;
+  _temp_p3d_filename = NULL;
+  _temp_splash_image = NULL;
   _got_fparams = false;
   _got_wparams = false;
 
@@ -68,6 +71,7 @@ P3DInstance(P3D_request_ready_func *func,
   P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
   _instance_id = inst_mgr->get_unique_id();
   _full_disk_access = false;
+  _hidden = false;
   _session = NULL;
   _panda3d = NULL;
   _splash_window = NULL;
@@ -122,6 +126,16 @@ P3DInstance::
     _splash_window = NULL;
   }
 
+  if (_temp_p3d_filename != NULL) {
+    delete _temp_p3d_filename;
+    _temp_p3d_filename = NULL;
+  }
+
+  if (_temp_splash_image != NULL) {
+    delete _temp_splash_image;
+    _temp_splash_image = NULL;
+  }
+
 #ifdef __APPLE__
   if (_frame_timer != NULL) {
     CFRunLoopTimerInvalidate(_frame_timer);
@@ -167,9 +181,8 @@ void P3DInstance::
 set_p3d_url(const string &p3d_url) {
   nout << "set_p3d_url(" << p3d_url << ")\n";
   // Make a temporary file to receive the instance data.
-  char *name = tempnam(NULL, "p3d_");
-  string filename = name;
-  free(name);
+  assert(_temp_p3d_filename == NULL);
+  _temp_p3d_filename = new P3DTemporaryFile(".p3d");
 
   // Mark the time we started downloading, so we'll know when to set
   // the install label.
@@ -183,7 +196,7 @@ set_p3d_url(const string &p3d_url) {
   // Start downloading the data.
   InstanceDownload *download = new InstanceDownload(this);
   download->set_url(p3d_url);
-  download->set_filename(filename);
+  download->set_filename(_temp_p3d_filename->get_filename());
 
   _panda_script_object->set_string_property("status", "downloading_instance");
   start_download(download);
@@ -243,10 +256,15 @@ set_p3d_filename(const string &p3d_filename) {
 ////////////////////////////////////////////////////////////////////
 void P3DInstance::
 set_wparams(const P3DWindowParams &wparams) {
-  nout << "set_wparams, _session = " << _session << "\n";
   _got_wparams = true;
   _wparams = wparams;
-  nout << "set window_type = " << _wparams.get_window_type() << "\n";
+
+  if (_hidden || _wparams.get_win_width() == 0 || _wparams.get_win_height() == 0) {
+    // If we're a hidden app, or if the window has no size, then it is
+    // really a hidden window, regardless of what type it claims to
+    // be.
+    _wparams.set_window_type(P3D_WT_hidden);
+  }
 
   if (_wparams.get_window_type() != P3D_WT_hidden) {
     // Update or create the splash window.
@@ -854,6 +872,15 @@ scan_app_desc_file(TiXmlDocument *doc) {
     _full_disk_access = (full_disk_access != 0);
   }
 
+  int hidden = 0;
+  if (xpackage->QueryIntAttribute("hidden", &hidden) == TIXML_SUCCESS) {
+    _hidden = (hidden != 0);
+  }
+
+  if (_hidden && _got_wparams) {
+    _wparams.set_window_type(P3D_WT_hidden);
+  }
+
   TiXmlElement *xrequires = xpackage->FirstChildElement("requires");
   while (xrequires != NULL) {
     const char *name = xrequires->Attribute("name");
@@ -1015,6 +1042,11 @@ handle_notify_request(const string &message) {
       _splash_window = NULL;
     }
 
+    if (_temp_splash_image != NULL) {
+      delete _temp_splash_image;
+      _temp_splash_image = NULL;
+    }
+
     _panda_script_object->set_string_property("status", "open");
 
 #ifdef __APPLE__
@@ -1112,7 +1144,6 @@ handle_script_request(const string &operation, P3D_object *object,
     char *buffer = new char[size + 1];
     P3D_OBJECT_GET_STRING(value, buffer, size + 1);
     result = P3D_OBJECT_EVAL(object, buffer);
-    logfile << " eval " << *object << ": " << buffer << ", result = " << result << "\n";
     delete[] buffer;
     
     if (result != NULL) {
@@ -1157,14 +1188,13 @@ make_splash_window() {
   }
 
   // Make a temporary file to receive the splash image.
-  char *name = tempnam(NULL, "p3d_");
-  string filename = name;
-  free(name);
+  assert(_temp_splash_image == NULL);
+  _temp_splash_image = new P3DTemporaryFile(".jpg");
 
   // Start downloading the requested splash image.
   SplashDownload *download = new SplashDownload(this);
   download->set_url(splash_image_url);
-  download->set_filename(filename);
+  download->set_filename(_temp_splash_image->get_filename());
 
   start_download(download);
 }
@@ -1202,6 +1232,8 @@ report_package_info_ready(P3DPackage *package) {
       _splash_window->set_install_progress(0.0);
     }
     _panda_script_object->set_string_property("status", "downloading");
+    _panda_script_object->set_int_property("numDownloadingPackages", _downloading_packages.size());
+    _panda_script_object->set_int_property("totalDownloadSize", _total_download_size);
     send_notify("ondownloadbegin");
 
     start_next_download();
@@ -1232,6 +1264,8 @@ start_next_download() {
       }
       _panda_script_object->set_string_property("downloadPackageName", package->get_package_name());
       _panda_script_object->set_string_property("downloadPackageDisplayName", name);
+      _panda_script_object->set_int_property("downloadPackageNumber", _download_package_index + 1);
+      _panda_script_object->set_int_property("downloadPackageSize", package->get_download_size());
       set_install_label("Installing " + name);
 
       nout << "Downloading " << package->get_package_name()
@@ -1241,6 +1275,7 @@ start_next_download() {
            << " bytes.\n";
 
       package->activate_download();
+      send_notify("ondownloadnext");
       return;
     }
     
@@ -1253,10 +1288,12 @@ start_next_download() {
   _downloading_packages.clear();
 
   if (get_packages_ready()) {
-    _panda_script_object->set_bool_property("downloadComplete", true);
-    _panda_script_object->set_string_property("status", "starting");
-    send_notify("ondownloadcomplete");
-    
+    if (!_panda_script_object->get_bool_property("downloadComplete")) {
+      _panda_script_object->set_bool_property("downloadComplete", true);
+      _panda_script_object->set_string_property("status", "starting");
+      send_notify("ondownloadcomplete");
+    }
+      
     // Notify the session also.
     if (_session != NULL) {
       _session->report_packages_done(this, true);

+ 5 - 0
direct/src/plugin/p3dInstance.h

@@ -40,6 +40,7 @@ class P3DDownload;
 class P3DPackage;
 class P3DObject;
 class P3DToplevelObject;
+class P3DTemporaryFile;
 
 ////////////////////////////////////////////////////////////////////
 //       Class : P3DInstance
@@ -145,6 +146,9 @@ private:
   P3D_object *_browser_script_object;
   P3DToplevelObject *_panda_script_object;
 
+  P3DTemporaryFile *_temp_p3d_filename;
+  P3DTemporaryFile *_temp_splash_image;
+
   bool _got_fparams;
   P3DFileParams _fparams;
 
@@ -156,6 +160,7 @@ private:
   string _python_version;
   string _log_basename;
   bool _full_disk_access;
+  bool _hidden;
 
   // Not ref-counted: session is the parent.
   P3DSession *_session;

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

@@ -60,6 +60,20 @@ get_platform() const {
   return _platform;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: P3DInstanceManager::get_log_directory
+//       Access: Public
+//  Description: Returns the pathname of the directory into which all
+//               log files should be written.  This filename will end
+//               with a slash or backslash, as appropriate, so that
+//               logfile pathnames may be made by concatenting
+//               directly with this string.
+////////////////////////////////////////////////////////////////////
+inline const string &P3DInstanceManager::
+get_log_directory() const {
+  return _log_directory;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: P3DInstanceManager::has_contents_file
 //       Access: Public

+ 173 - 2
direct/src/plugin/p3dInstanceManager.cxx

@@ -24,6 +24,7 @@
 #include "find_root_dir.h"
 #include "fileSpec.h"
 #include "get_tinyxml.h"
+#include "mkdir_complete.h"
 
 // We can include this header file to get the DTOOL_PLATFORM
 // definition, even though we don't link with dtool.
@@ -32,9 +33,12 @@
 #ifdef _WIN32
 #include <shlobj.h>
 #else
-//#include <sys/stat.h>
+#include <sys/stat.h>
 #endif
 
+static ofstream logfile;
+ostream *nout_stream = &logfile;
+
 P3DInstanceManager *P3DInstanceManager::_global_ptr;
 
 ////////////////////////////////////////////////////////////////////
@@ -45,6 +49,7 @@ P3DInstanceManager *P3DInstanceManager::_global_ptr;
 P3DInstanceManager::
 P3DInstanceManager() {
   _is_initialized = false;
+  _next_temp_filename_counter = 0;
   _unique_id = 0;
   _xcontents = NULL;
 
@@ -105,6 +110,15 @@ P3DInstanceManager::
   assert(_instances.empty());
   assert(_sessions.empty());
 
+  // Delete any remaining temporary files.
+  TempFilenames::iterator ti;
+  for (ti = _temp_filenames.begin(); ti != _temp_filenames.end(); ++ti) {
+    const string &filename = (*ti);
+    nout << "Removing delinquent temp file " << filename << "\n";
+    unlink(filename.c_str());
+  }
+  _temp_filenames.clear();
+
   nout << "counts: " << _undefined_object->_ref_count
        << " " << _none_object->_ref_count
        << " " << _true_object->_ref_count
@@ -138,23 +152,104 @@ P3DInstanceManager::
 ////////////////////////////////////////////////////////////////////
 bool P3DInstanceManager::
 initialize(const string &contents_filename, const string &download_url,
-           const string &platform) {
+           const string &platform, const string &log_directory,
+           const string &log_basename) {
 
   _root_dir = find_root_dir();
   _download_url = download_url;
+#ifdef P3D_PLUGIN_DOWNLOAD
   if (_download_url.empty()) {
     _download_url = P3D_PLUGIN_DOWNLOAD;
   }
+#endif
   _platform = platform;
   if (_platform.empty()) {
     _platform = DTOOL_PLATFORM;
   }
 
+  _log_directory = log_directory;
+#ifdef P3D_PLUGIN_LOG_DIRECTORY
+  if (_log_directory.empty()) {
+    _log_directory = P3D_PLUGIN_LOG_DIRECTORY;
+  }
+#endif
+
+  // Determine the temporary directory.
+#ifdef _WIN32
+  size_t needs_size_1 = GetTempPath(0, NULL);
+  char *buffer_1 = new char[needs_size_1];
+  if (GetTempPath(needs_size_1, buffer_1) != 0) {
+    _temp_directory = buffer_1;
+  }
+  delete[] buffer_1;
+
+  static const size_t buffer_size = 4096;
+  char buffer[buffer_size];
+  if (GetTempPath(buffer_size, buffer) != 0) {
+    _temp_directory = buffer;
+  }
+  
+  // Also insist that the temp directory is fully specified.
+  size_t needs_size_2 = GetFullPathName(_temp_directory.c_str(), 0, NULL, NULL);
+  char *buffer_2 = new char[needs_size_2];
+  if (GetFullPathName(_temp_directory.c_str(), needs_size_2, buffer_2, NULL) != 0) {
+    _temp_directory = buffer_2;
+  }
+  delete[] buffer_2;
+
+  // Also make sure the directory actually exists.
+  mkdir_complete(_temp_directory, nout);
+
+#else
+  _temp_directory = "/tmp/";
+#endif  // _WIN32
+
+  // If the log directory is still empty, use the temp directory.
+  if (_log_directory.empty()) {
+    _log_directory = _temp_directory;
+  }
+
+  _log_basename = log_basename;
+#ifdef P3D_PLUGIN_LOG_BASENAME2
+  if (_log_basename.empty()) {
+    _log_basename = P3D_PLUGIN_LOG_BASENAME2;
+  }
+#endif
+
   // Ensure that the download URL ends with a slash.
   if (!_download_url.empty() && _download_url[_download_url.size() - 1] != '/') {
     _download_url += "/";
   }
 
+  // Ensure that the temp directory ends with a slash.
+  if (!_temp_directory.empty() && _temp_directory[_temp_directory.size() - 1] != '/') {
+#ifdef _WIN32
+    if (_temp_directory[_temp_directory.size() - 1] != '\\')
+#endif
+      _temp_directory += "/";
+  }
+
+  // Ensure that the log directory ends with a slash.
+  if (!_log_directory.empty() && _log_directory[_log_directory.size() - 1] != '/') {
+#ifdef _WIN32
+    if (_log_directory[_log_directory.size() - 1] != '\\')
+#endif
+      _log_directory += "/";
+  }
+
+  // Construct the logfile pathname.
+  if (!_log_basename.empty()) {
+    _log_pathname = _log_directory;
+    _log_pathname += _log_basename;
+    _log_pathname += ".log";
+
+    logfile.open(_log_pathname.c_str(), ios::out | ios::trunc);
+    if (logfile) {
+      logfile.setf(ios::unitbuf);
+      nout_stream = &logfile;
+    }
+  }
+
   nout << "_root_dir = " << _root_dir
        << ", contents = " << contents_filename
        << ", download = " << _download_url
@@ -497,6 +592,82 @@ make_class_definition() const {
   return new_class;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: P3DInstanceManager::make_temp_filename
+//       Access: Public
+//  Description: Constructs a new, unique temporary filename with the
+//               indicated extension.  You should use the
+//               P3DTemporaryFilename interface instead of calling
+//               this method directly.
+////////////////////////////////////////////////////////////////////
+string P3DInstanceManager::
+make_temp_filename(const string &extension) {
+  string result;
+  bool exists;
+
+  do {
+    int tid;
+#ifdef _WIN32
+    tid = GetCurrentProcessId();
+#else
+    tid = getpid();
+#endif
+    if (tid == 0) {
+      tid = 1;
+    }
+    int hash = ((clock() + _next_temp_filename_counter) * ((time(NULL) * tid) >> 8)) & 0xffffff;
+    ++_next_temp_filename_counter;
+    char hex_code[10];
+    sprintf(hex_code, "%06x", hash);
+
+    result = _temp_directory;
+    result += "p3d_";
+    result += hex_code;
+    result += extension;
+    
+    exists = false;
+    if (_temp_filenames.find(result) != _temp_filenames.end()) {
+      // We've previously allocated this file.
+      exists = true;
+
+    } else {
+
+      // Check if the file exists on disk.
+#ifdef _WIN32
+      DWORD results = GetFileAttributes(result.c_str());
+      if (results != -1) {
+        exists = true;
+      }
+
+#else  // _WIN32
+      struct stat this_buf;
+      if (stat(result.c_str(), &this_buf) == 0) {
+        exists = true;
+      }
+#endif
+    }
+
+  } while (exists);
+
+  nout << "make_temp_filename: " << result << "\n";
+  return result;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: P3DInstanceManager::release_temp_filename
+//       Access: Public
+//  Description: Releases a temporary filename assigned earlier via
+//               make_temp_filename().  If the file exists, it will be
+//               removed.  You should use the P3DTemporaryFilename
+//               interface instead of calling this method directly.
+////////////////////////////////////////////////////////////////////
+void P3DInstanceManager::
+release_temp_filename(const string &filename) {
+  nout << "release_temp_filename: " << filename << "\n";
+  _temp_filenames.erase(filename);
+  unlink(filename.c_str());
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: P3DInstanceManager::get_global_ptr
 //       Access: Public, Static

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

@@ -45,13 +45,16 @@ private:
 public:
   bool initialize(const string &contents_filename,
                   const string &download_url,
-                  const string &platform);
+                  const string &platform,
+                  const string &log_directory,
+                  const string &log_basename);
 
   inline bool is_initialized() const;
 
   inline const string &get_root_dir() const;
   inline const string &get_download_url() const;
   inline const string &get_platform() const;
+  inline const string &get_log_directory() const;
 
   inline bool has_contents_file() const;
   bool read_contents_file(const string &contents_filename);
@@ -88,6 +91,9 @@ public:
   inline P3D_object *new_none_object();
   inline P3D_object *new_bool_object(bool value);
 
+  string make_temp_filename(const string &extension);
+  void release_temp_filename(const string &filename);
+
   static P3DInstanceManager *get_global_ptr();
   static void delete_global_ptr();
 
@@ -106,6 +112,10 @@ private:
   string _root_dir;
   string _download_url;
   string _platform;
+  string _log_directory;
+  string _log_basename;
+  string _log_pathname;
+  string _temp_directory;
 
   TiXmlElement *_xcontents;
 
@@ -123,6 +133,10 @@ private:
   typedef map<string, P3DPackage *> Packages;
   Packages _packages;
 
+  typedef set<string> TempFilenames;
+  TempFilenames _temp_filenames;
+  int _next_temp_filename_counter;
+
   int _unique_id;
 
   // This condition var is waited on the main thread and signaled in a

+ 13 - 10
direct/src/plugin/p3dPackage.cxx

@@ -16,6 +16,7 @@
 #include "p3dInstanceManager.h"
 #include "p3dInstance.h"
 #include "p3dMultifileReader.h"
+#include "p3dTemporaryFile.h"
 #include "mkdir_complete.h"
 
 #include "zlib.h"
@@ -53,6 +54,8 @@ P3DPackage(const string &package_name,
   _package_fullname += string("_") + _package_version;
   _package_dir += string("/") + _package_version;
 
+  _temp_contents_file = NULL;
+
   _info_ready = false;
   _download_size = 0;
   _allow_data_download = false;
@@ -85,6 +88,7 @@ P3DPackage::
     _active_download = NULL;
   }
 
+  assert(_temp_contents_file == NULL);
   assert(_instances.empty());
 }
 
@@ -179,10 +183,10 @@ download_contents_file() {
 
   // Download contents.xml to a temporary filename first, in case
   // multiple packages are downloading it simultaneously.
-  _contents_file_pathname = tempnam(NULL, "p3d_");
+  assert(_temp_contents_file == NULL);
+  _temp_contents_file = new P3DTemporaryFile(".xml");
 
-  cerr << "starting contents download\n";
-  start_download(DT_contents_file, url, _contents_file_pathname, false);
+  start_download(DT_contents_file, url, _temp_contents_file->get_filename(), false);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -193,20 +197,18 @@ download_contents_file() {
 void P3DPackage::
 contents_file_download_finished(bool success) {
   P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
-  cerr << "done contents download: " << success 
-       << ", has_contents = " << inst_mgr->has_contents_file()
-       << "\n";
 
   if (!inst_mgr->has_contents_file()) {
-    if (!success || !inst_mgr->read_contents_file(_contents_file_pathname)) {
-      nout << "Couldn't read " << _contents_file_pathname << "\n";
+    if (!success || !inst_mgr->read_contents_file(_temp_contents_file->get_filename())) {
+      nout << "Couldn't read " << *_temp_contents_file << "\n";
 
       // Maybe we can read an already-downloaded contents.xml file.
       string standard_filename = inst_mgr->get_root_dir() + "/contents.xml";
       if (!inst_mgr->read_contents_file(standard_filename)) {
         // Couldn't even read that.  Fail.
         report_done(false);
-        unlink(_contents_file_pathname.c_str());
+        delete _temp_contents_file;
+        _temp_contents_file = NULL;
         return;
       }
     }
@@ -214,7 +216,8 @@ contents_file_download_finished(bool success) {
     
   // The file is correctly installed by now; we can remove the
   // temporary file.
-  unlink(_contents_file_pathname.c_str());
+  delete _temp_contents_file;
+  _temp_contents_file = NULL;
 
   download_desc_file();
 }

+ 2 - 1
direct/src/plugin/p3dPackage.h

@@ -21,6 +21,7 @@
 #include "get_tinyxml.h"
 
 class P3DInstance;
+class P3DTemporaryFile;
 
 ////////////////////////////////////////////////////////////////////
 //       Class : P3DPackage
@@ -107,7 +108,7 @@ private:
   string _package_fullname;
   string _package_dir;
 
-  string _contents_file_pathname;
+  P3DTemporaryFile *_temp_contents_file;
 
   string _desc_file_basename;
   string _desc_file_pathname;

+ 1 - 1
direct/src/plugin/p3dPythonRun.cxx

@@ -978,7 +978,7 @@ set_p3d_filename(P3DCInstance *inst, TiXmlElement *xfparams) {
 
     xarg = xarg->NextSiblingElement("arg");
   }
-  
+
   PyObject *result = PyObject_CallMethod
     (_runner, (char *)"setP3DFilename", (char *)"sOOi", p3d_filename.c_str(),
      token_list, arg_list, inst->get_instance_id());

+ 32 - 28
direct/src/plugin/p3dSession.cxx

@@ -650,6 +650,8 @@ start_p3dpython(P3DInstance *inst) {
     return;
   }
 
+  P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
+
   _python_root_dir = inst->_panda3d->get_package_dir();
 
   // We'll be changing the directory to the standard start directory
@@ -699,7 +701,7 @@ start_p3dpython(P3DInstance *inst) {
   const char *keep[] = {
     "TMP", "TEMP", "HOME", "USER", 
 #ifdef _WIN32
-    "SYSTEMROOT", "USERPROFILE", "COMSPEC",
+    "SYSTEMROOT", "USERPROFILE", "COMSPEC", "PANDA_ROOT",
 #endif
     NULL
   };
@@ -766,7 +768,17 @@ start_p3dpython(P3DInstance *inst) {
     log_basename = inst->get_fparams().lookup_token("log_basename");
   }
 
-  // However, it is always written into the temp directory only; the
+  bool console_output = (inst->get_fparams().lookup_token_int("console_output") != 0);
+
+#ifdef P3D_PLUGIN_LOG_BASENAME3
+  if (log_basename.empty()) {
+    // No log_basename specified for the app; use the compiled-in
+    // default.
+    log_basename = P3D_PLUGIN_LOG_BASENAME3;
+  }
+#endif
+
+  // However, it is always written into the log directory only; the
   // user may not override the log file to put it anywhere else.
   size_t slash = log_basename.rfind('/');
   if (slash != string::npos) {
@@ -779,32 +791,24 @@ start_p3dpython(P3DInstance *inst) {
   }
 #endif  // _WIN32
 
-  if (!log_basename.empty()) {
-#ifdef _WIN32
-    static const size_t buffer_size = 4096;
-    char buffer[buffer_size];
-    if (GetTempPath(buffer_size, buffer) != 0) {
-      _output_filename = buffer;
-    }
-#else
-    _output_filename = "/tmp/";
-#endif  // _WIN32
-    _output_filename += log_basename;
+  if (!console_output && !log_basename.empty()) {
+    _log_pathname = inst_mgr->get_log_directory();
+    _log_pathname += log_basename;
 
     // We always tack on the extension ".log", to make it even more
     // difficult to overwrite a system file.
-    _output_filename += ".log";
+    _log_pathname += ".log";
   }
 
   nout << "Attempting to start python from " << p3dpython << "\n";
 #ifdef _WIN32
   _p3dpython_handle = win_create_process
-    (p3dpython, start_dir, env, _output_filename,
+    (p3dpython, start_dir, env, _log_pathname,
      _pipe_read, _pipe_write);
   bool started_p3dpython = (_p3dpython_handle != INVALID_HANDLE_VALUE);
 #else
   _p3dpython_pid = posix_create_process
-    (p3dpython, start_dir, env, _output_filename,
+    (p3dpython, start_dir, env, _log_pathname,
      _pipe_read, _pipe_write);
   bool started_p3dpython = (_p3dpython_pid > 0);
 #endif
@@ -976,7 +980,7 @@ rt_terminate() {
 //       Access: Private, Static
 //  Description: Creates a sub-process to run the named program
 //               executable, with the indicated environment string.
-//               Standard error is logged to output_filename, if that
+//               Standard error is logged to log_pathname, if that
 //               string is nonempty.
 //
 //               Opens the two HandleStreams as the read and write
@@ -988,7 +992,7 @@ rt_terminate() {
 ////////////////////////////////////////////////////////////////////
 HANDLE P3DSession::
 win_create_process(const string &program, const string &start_dir,
-                   const string &env, const string &output_filename,
+                   const string &env, const string &log_pathname,
                    HandleStream &pipe_read, HandleStream &pipe_write) {
 
   // Create a bi-directional pipe to communicate with the sub-process.
@@ -1013,19 +1017,19 @@ win_create_process(const string &program, const string &start_dir,
   }
 
   HANDLE error_handle = GetStdHandle(STD_ERROR_HANDLE);
-  bool got_output_filename = !output_filename.empty();
-  if (got_output_filename) {
+  bool got_log_pathname = !log_pathname.empty();
+  if (got_log_pathname) {
     // Open the named file for output and redirect the child's stderr
     // into it.
     HANDLE handle = CreateFile
-      (output_filename.c_str(), GENERIC_WRITE, 
+      (log_pathname.c_str(), GENERIC_WRITE, 
        FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
        NULL, CREATE_ALWAYS, 0, NULL);
     if (handle != INVALID_HANDLE_VALUE) {
       error_handle = handle;
       SetHandleInformation(error_handle, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT);
     } else {
-      nout << "Unable to open " << output_filename << "\n";
+      nout << "Unable to open " << log_pathname << "\n";
     }
   }
 
@@ -1063,7 +1067,7 @@ win_create_process(const string &program, const string &start_dir,
   // Close the pipe handles that are now owned by the child.
   CloseHandle(w_from);
   CloseHandle(r_to);
-  if (got_output_filename) {
+  if (got_log_pathname) {
     CloseHandle(error_handle);
   }
 
@@ -1099,7 +1103,7 @@ win_create_process(const string &program, const string &start_dir,
 ////////////////////////////////////////////////////////////////////
 int P3DSession::
 posix_create_process(const string &program, const string &start_dir,
-                     const string &env, const string &output_filename,
+                     const string &env, const string &log_pathname,
                      HandleStream &pipe_read, HandleStream &pipe_write) {
   // Create a bi-directional pipe to communicate with the sub-process.
   int to_fd[2];
@@ -1124,14 +1128,14 @@ posix_create_process(const string &program, const string &start_dir,
 
   if (child == 0) {
     // Here we are in the child process.
-    bool got_output_filename = !output_filename.empty();
-    if (got_output_filename) {
+    bool got_log_pathname = !log_pathname.empty();
+    if (got_log_pathname) {
       // Open the named file for output and redirect the child's stderr
       // into it.
-      int logfile_fd = open(output_filename.c_str(), 
+      int logfile_fd = open(log_pathname.c_str(), 
                             O_WRONLY | O_CREAT | O_TRUNC, 0666);
       if (logfile_fd < 0) {
-        nout << "Unable to open " << output_filename << "\n";
+        nout << "Unable to open " << log_pathname << "\n";
       } else {
         dup2(logfile_fd, STDERR_FILENO);
         close(logfile_fd);

+ 1 - 1
direct/src/plugin/p3dSession.h

@@ -90,7 +90,7 @@ private:
   int _session_id;
   string _session_key;
   string _python_version;
-  string _output_filename;
+  string _log_pathname;
   string _python_root_dir;
   string _start_dir;
 

+ 24 - 0
direct/src/plugin/p3dTemporaryFile.I

@@ -0,0 +1,24 @@
+// Filename: p3dTemporaryFile.I
+// Created by:  drose (19Aug09)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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."
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: P3DTemporaryFile::get_filename
+//       Access: Public
+//  Description: Returns the temporary filename.
+////////////////////////////////////////////////////////////////////
+inline const string &P3DTemporaryFile::
+get_filename() const {
+  return _filename;
+}

+ 38 - 0
direct/src/plugin/p3dTemporaryFile.cxx

@@ -0,0 +1,38 @@
+// Filename: p3dTemporaryFile.cxx
+// Created by:  drose (19Aug09)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 "p3dTemporaryFile.h"
+#include "p3dInstanceManager.h"
+
+////////////////////////////////////////////////////////////////////
+//     Function: P3DTemporaryFile::Constructor
+//       Access: Public
+//  Description: Constructs a new, unique temporary filename.
+////////////////////////////////////////////////////////////////////
+P3DTemporaryFile::
+P3DTemporaryFile(const string &extension) {
+  P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
+  _filename = inst_mgr->make_temp_filename(extension);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: P3DTemporaryFile::Destructor
+//       Access: Public
+//  Description: Deletes the temporary file, if it exists.
+////////////////////////////////////////////////////////////////////
+P3DTemporaryFile::
+~P3DTemporaryFile() {
+  P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
+  inst_mgr->release_temp_filename(_filename);
+}

+ 47 - 0
direct/src/plugin/p3dTemporaryFile.h

@@ -0,0 +1,47 @@
+// Filename: p3dTemporaryFile.h
+// Created by:  drose (19Aug09)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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."
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef P3DTEMPORARYFILE_H
+#define P3DTEMPORARYFILE_H
+
+#include "p3d_plugin_common.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : P3DTemporaryFile
+// Description : This represents a temporary filename for some
+//               transitory purpose.  This returns a filename which is
+//               guaranteed to be unique at the time the constructor
+//               was called.
+//
+//               The file on disk, if it exists, will automatically be
+//               deleted when the destructor is called.
+////////////////////////////////////////////////////////////////////
+class P3DTemporaryFile {
+public:
+  P3DTemporaryFile(const string &extension);
+  ~P3DTemporaryFile();
+
+  inline const string &get_filename() const;
+
+private:
+  string _filename;
+};
+
+inline ostream &operator << (ostream &out, P3DTemporaryFile &tfile) {
+  return out << tfile.get_filename();
+}
+
+#include "p3dTemporaryFile.I"
+
+#endif

+ 11 - 1
direct/src/plugin/p3dWindowParams.I

@@ -17,13 +17,23 @@
 //     Function: P3DWindowParams::get_window_type
 //       Access: Public
 //  Description: Returns the window_type that was passed to the
-//               constructor.
+//               constructor, or to set_window_type().
 ////////////////////////////////////////////////////////////////////
 inline P3D_window_type P3DWindowParams::
 get_window_type() const {
   return _window_type;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: P3DWindowParams::set_window_type
+//       Access: Public
+//  Description: Changes the window_type.
+////////////////////////////////////////////////////////////////////
+inline void P3DWindowParams::
+set_window_type(P3D_window_type window_type) {
+  _window_type = window_type;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: P3DWindowParams::get_window_x
 //       Access: Public

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

@@ -35,6 +35,8 @@ public:
   void operator = (const P3DWindowParams &other);
 
   inline P3D_window_type get_window_type() const;
+  inline void set_window_type(P3D_window_type window_type);
+
   inline int get_win_x() const;
   inline int get_win_y() const;
   inline int get_win_width() const;

+ 8 - 31
direct/src/plugin/p3d_plugin.cxx

@@ -32,14 +32,10 @@
 bool initialized_lock = false;
 LOCK _api_lock;
 
-ofstream logfile;
-string plugin_output_filename;
-ostream *nout_stream;
-
-
 bool 
 P3D_initialize(int api_version, const char *contents_filename,
-               const char *download_url, const char *platform) {
+               const char *download_url, const char *platform,
+               const char *log_directory, const char *log_basename) {
   if (api_version != P3D_API_VERSION) {
     // Can't accept an incompatible version.
     return false;
@@ -63,36 +59,17 @@ P3D_initialize(int api_version, const char *contents_filename,
     platform = "";
   }
 
-#ifdef P3D_PLUGIN_LOGFILE2
-  string logfilename = P3D_PLUGIN_LOGFILE2;
-#else
-  string logfilename;
-#endif  // P3D_PLUGIN_LOGFILE2
-
-  if (logfilename.empty()) {
-#ifdef _WIN32
-    static const size_t buffer_size = 4096;
-    char buffer[buffer_size];
-    if (GetTempPath(buffer_size, buffer) != 0) {
-      logfilename = buffer;
-      logfilename += "panda3d.2.log";
-    }
-#else
-    logfilename = "/tmp/panda3d.2.log";
-#endif  // _WIN32
+  if (log_directory == NULL) {
+    log_directory = "";
   }
 
-  cerr << "logfile: " << logfilename << "\n";
-
-  nout_stream = &cerr;
-  logfile.open(logfilename.c_str(), ios::out | ios::trunc);
-  if (logfile) {
-    logfile.setf(ios::unitbuf);
-    nout_stream = &logfile;
+  if (log_basename == NULL) {
+    log_basename = "";
   }
 
   P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
-  bool result = inst_mgr->initialize(contents_filename, download_url, platform);
+  bool result = inst_mgr->initialize(contents_filename, download_url,
+                                     platform, log_directory, log_basename);
   RELEASE_LOCK(_api_lock);
   return result;
 }

+ 14 - 1
direct/src/plugin/p3d_plugin.h

@@ -101,13 +101,26 @@ extern "C" {
    If platform is not NULL or empty, it specifies the current platform
    string; otherwise, the compiled-in default is used.
 
+   If log_directory is not NULL or empty, it specifies the directory
+   into which all log files will be written; otherwise, the
+   compiled-in default will be used, or the system temp directory if
+   no default is compiled in.
+
+   If log_basename is not NULL or empty, it specifies the filename in
+   log_directory to which the core API's logfile output will be
+   written.  Otherwise, the compiled-in default is used; if there is
+   no compiled-in default, no logfile output will be generated by the
+   core API.  Note that the individual instances also have their own
+   log_basename values.
+
    This function returns true if the core API is valid and uses a
    compatible API, false otherwise.  If it returns false, the host
    should not call any more functions in this API, and should
    immediately unload the DLL and (if possible) download a new one. */
 typedef bool 
 P3D_initialize_func(int api_version, const char *contents_filename,
-                    const char *download_url, const char *platform);
+                    const char *download_url, const char *platform,
+                    const char *log_directory, const char *log_basename);
 
 /* This function should be called to unload the core API.  It will
    release all internally-allocated memory and return the core API to

+ 1 - 2
direct/src/plugin/p3d_plugin_common.h

@@ -38,8 +38,7 @@
 
 using namespace std;
 
-// Appears in p3d_plugin.cxx.
-extern string plugin_output_filename;
+// Appears in p3dInstanceManager.cxx.
 extern ostream *nout_stream;
 #define nout (*nout_stream)
 

+ 1 - 0
direct/src/plugin/p3d_plugin_composite1.cxx

@@ -20,6 +20,7 @@
 #include "p3dSession.cxx"
 #include "p3dSplashWindow.cxx"
 #include "p3dStringObject.cxx"
+#include "p3dTemporaryFile.cxx"
 #include "p3dToplevelObject.cxx"
 #include "p3dUndefinedObject.cxx"
 #include "p3dWinSplashWindow.cxx"

+ 4 - 2
direct/src/plugin/p3d_plugin_config.h.pp

@@ -13,8 +13,10 @@
 
 /* The filename(s) to generate output to when the plugin is running.
    For debugging purposes only. */
-#$[]define P3D_PLUGIN_LOGFILE1 "$[subst \,\\,$[osfilename $[P3D_PLUGIN_LOGFILE1]]]"
-#$[]define P3D_PLUGIN_LOGFILE2 "$[subst \,\\,$[osfilename $[P3D_PLUGIN_LOGFILE2]]]"
+#$[]define P3D_PLUGIN_LOG_DIRECTORY "$[subst \,\\,$[osfilename $[P3D_PLUGIN_LOG_DIRECTORY]]]"
+#$[]define P3D_PLUGIN_LOG_BASENAME1 "$[P3D_PLUGIN_LOG_BASENAME1]"
+#$[]define P3D_PLUGIN_LOG_BASENAME2 "$[P3D_PLUGIN_LOG_BASENAME2]"
+#$[]define P3D_PLUGIN_LOG_BASENAME3 "$[P3D_PLUGIN_LOG_BASENAME3]"
 
 /* For development only: the location at which p3dpython.exe can be
    found.  Empty string for the default. */

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

@@ -928,7 +928,7 @@ do_load_plugin() {
 #endif  // P3D_PLUGIN_P3D_PLUGIN
 
   nout << "Attempting to load core API from " << pathname << "\n";
-  if (!load_plugin(pathname, "", "", "")) {
+  if (!load_plugin(pathname, "", "", "", "", "")) {
     nout << "Unable to launch core API in " << pathname << "\n";
     return;
   }

+ 33 - 15
direct/src/plugin_npapi/startup.cxx

@@ -30,26 +30,44 @@ static bool logfile_is_open = false;
 static void
 open_logfile() {
   if (!logfile_is_open) {
-#ifdef P3D_PLUGIN_LOGFILE1
-    string logfilename = P3D_PLUGIN_LOGFILE1;
-#else
-    string logfilename;
-#endif  // P3D_PLUGIN_LOGFILE1
+    // Note that this logfile name may not be specified at runtime.  It
+    // must be compiled in if it is specified at all.
+
+    string log_basename;
+#ifdef P3D_PLUGIN_LOG_BASENAME1
+    log_basename = P3D_PLUGIN_LOG_BASENAME1;
+#endif
 
-    if (logfilename.empty()) {
+    if (!log_basename.empty()) {
+      // Get the log directory.
+      string log_directory;
+#ifdef P3D_PLUGIN_LOG_DIRECTORY
+      log_directory = P3D_PLUGIN_LOG_DIRECTORY;
+#endif
+      if (log_directory.empty()) {
 #ifdef _WIN32
-      static const size_t buffer_size = 4096;
-      char buffer[buffer_size];
-      if (GetTempPath(buffer_size, buffer) != 0) {
-        logfilename = buffer;
-        logfilename += "panda3d.1.log";
-      }
+        static const size_t buffer_size = 4096;
+        char buffer[buffer_size];
+        if (GetTempPath(buffer_size, buffer) != 0) {
+          log_directory = buffer;
+        }
 #else
-      logfilename = "/tmp/panda3d.1.log";
+        log_directory = "/tmp/";
 #endif  // _WIN32
+      }
+
+      // Construct the full logfile pathname.
+      string log_pathname = log_directory;
+      log_pathname += log_basename;
+      log_pathname += ".log";
+
+      logfile.open(log_pathname.c_str());
+      logfile.setf(ios::unitbuf);
     }
-    logfile.open(logfilename.c_str());
-    logfile.setf(ios::unitbuf);
+
+    // If we didn't have a logfile name compiled in, we throw away log
+    // output by the simple expedient of never actually opening the
+    // ofstream.
     logfile_is_open = true;
   }
 }

+ 92 - 20
direct/src/plugin_standalone/panda3d.cxx

@@ -44,6 +44,7 @@
 Panda3D::
 Panda3D() {
   _root_dir = find_root_dir();
+  _reporting_download = false;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -60,7 +61,7 @@ run(int argc, char *argv[]) {
   // We prefix a "+" sign to tell gnu getopt not to parse options
   // following the first not-option parameter.  (These will be passed
   // into the sub-process.)
-  const char *optstr = "+mu:p:ft:s:o:h";
+  const char *optstr = "+mu:p:ft:s:o:l:h";
 
   bool allow_multiple = false;
   string download_url = P3D_PLUGIN_DOWNLOAD;
@@ -69,7 +70,7 @@ run(int argc, char *argv[]) {
 
   P3D_window_type window_type = P3D_WT_toplevel;
   int win_x = 0, win_y = 0;
-  int win_width = 0, win_height = 0;
+  int win_width = 640, win_height = 480;
 
   int flag = getopt(argc, argv, optstr);
 
@@ -120,6 +121,11 @@ run(int argc, char *argv[]) {
       }
       break;
 
+    case 'l':
+      _log_dirname = Filename::from_os_specific(optarg).to_os_specific();
+      _log_basename = "panda3d";
+      break;
+
     case 'h':
     case '?':
     case '+':
@@ -321,7 +327,6 @@ get_plugin(const string &download_url, const string &this_platform, bool force_d
   // Couldn't read it, so go get it.
   string url = download_url;
   url += "contents.xml";
-  cerr << "Getting URL " << url << "\n";
   
   HTTPClient *http = HTTPClient::get_global_ptr();
   PT(HTTPChannel) channel = http->get_document(url);
@@ -436,7 +441,8 @@ get_core_api(const Filename &contents_filename, const string &download_url,
 #endif  // P3D_PLUGIN_P3D_PLUGIN
 
   if (!load_plugin(pathname, contents_filename.to_os_specific(),
-                   download_url, this_platform)) {
+                   download_url, this_platform, _log_dirname,
+                   _log_basename)) {
     cerr << "Unable to launch core API in " << pathname << "\n" << flush;
     return false;
   }
@@ -481,7 +487,6 @@ handle_request(P3D_request *request) {
 
   switch (request->_request_type) {
   case P3D_RT_stop:
-    cerr << "Got P3D_RT_stop\n";
     delete_instance(request->_instance);
 #ifdef _WIN32
     // Post a silly message to spin the event loop.
@@ -491,8 +496,6 @@ handle_request(P3D_request *request) {
     break;
 
   case P3D_RT_get_url:
-    cerr << "Got P3D_RT_get_url: " << request->_request._get_url._url
-         << "\n";
     {
       int unique_id = request->_request._get_url._unique_id;
       const string &url = request->_request._get_url._url;
@@ -504,8 +507,6 @@ handle_request(P3D_request *request) {
     break;
 
   case P3D_RT_post_url:
-    cerr << "Got P3D_RT_post_url: " << request->_request._post_url._url 
-         << "\n";
     {
       int unique_id = request->_request._post_url._unique_id;
       const string &url = request->_request._post_url._url;
@@ -519,9 +520,15 @@ handle_request(P3D_request *request) {
     break;
 
   case P3D_RT_notify:
-    cerr << "Got P3D_RT_notify: " << request->_request._notify._message
-         << "\n";
-    // Ignore notifications.
+    {
+      if (strcmp(request->_request._notify._message, "ondownloadnext") == 0) {
+        // Tell the user we're downloading a package.
+        report_downloading_package(request->_instance);
+      } else if (strcmp(request->_request._notify._message, "ondownloadcomplete") == 0) {
+        // Tell the user we're done downloading.
+        report_download_complete(request->_instance);
+      }
+    }
     break;
 
   default:
@@ -624,6 +631,30 @@ create_instance(const string &p3d, P3D_window_type window_type,
     is_local = true;
   } 
 
+  // Build up the token list.
+  pvector<P3D_token> tokens;
+  string log_basename;
+  if (!_log_dirname.empty()) {
+    // Generate output to a logfile.
+    log_basename = p3d_filename.get_basename_wo_extension();
+    P3D_token token;
+    token._keyword = "log_basename";
+    token._value = log_basename.c_str();
+    tokens.push_back(token);
+  } else {
+    // Send output to the console.
+    P3D_token token;
+    token._keyword = "console_output";
+    token._value = "1";
+    tokens.push_back(token);
+  }
+
+  P3D_token *tokens_p;
+  size_t num_tokens = tokens.size();
+  if (!tokens.empty()) {
+    tokens_p = &tokens[0];
+  }
+
   // Build up the argument list, beginning with the p3d_filename.
   pvector<const char *> argv;
   argv.push_back(os_p3d_filename.c_str());
@@ -631,13 +662,15 @@ create_instance(const string &p3d, P3D_window_type window_type,
     argv.push_back(args[i]);
   }
 
-  P3D_instance *inst = P3D_new_instance(NULL, NULL, 0,
+  P3D_instance *inst = P3D_new_instance(NULL, tokens_p, num_tokens,
                                         argv.size(), &argv[0], NULL);
 
   if (inst != NULL) {
+    // We call start() first, to give the core API a chance to notice
+    // the "hidden" attrib before we set the window parameters.
+    P3D_instance_start(inst, is_local, os_p3d_filename.c_str());
     P3D_instance_setup_window
       (inst, window_type, win_x, win_y, win_width, win_height, parent_window);
-    P3D_instance_start(inst, is_local, os_p3d_filename.c_str());
   }
 
   return inst;
@@ -682,10 +715,10 @@ usage() {
     << "   panda3d -m [opts] file_a.p3d file_b.p3d [file_c.p3d ...]\n\n"
   
     << "This program is used to execute a Panda3D application bundle stored\n"
-    << "in a .p3d file.  In the first form, without a -m option, it\n"
+    << "in a .p3d file.  In the first form, without the -m option, it\n"
     << "executes one application; remaining arguments following the\n"
     << "application name are passed into the application.  In the second\n"
-    << "form, with a -m option, it can execute multiple applications\n"
+    << "form, with the -m option, it can execute multiple applications\n"
     << "simultaneously, though in this form arguments cannot be passed into\n"
     << "the applications.\n\n"
 
@@ -708,6 +741,11 @@ usage() {
     << "    Specify the position (origin) of the graphic window on the\n"
     << "    screen, or on the parent window.\n\n"
 
+    << "  -l log_dirname\n"
+    << "    Specify the full path to the directory in which log files are\n"
+    << "    to be written.  If this is not specified, the default is to send\n"
+    << "    the application output to the console.\n\n"
+
     << "  -f\n"
     << "    Force a HTTP contact to the Panda3D download server, to check\n"
     << "    if a new version is available.  Normally, this is done only\n"
@@ -743,6 +781,44 @@ parse_int_pair(char *arg, int &x, int &y) {
   return false;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: Panda3D::report_downloading_package
+//       Access: Private
+//  Description: Tells the user we have to download a package.
+////////////////////////////////////////////////////////////////////
+void Panda3D::
+report_downloading_package(P3D_instance *instance) {
+  P3D_object *obj = P3D_instance_get_panda_script_object(instance);
+  
+  P3D_object *display_name = P3D_object_get_property(obj, "downloadPackageDisplayName");
+  if (display_name == NULL) {
+    cerr << "no name: " << obj << "\n";
+    return;
+  }
+
+  int name_length = P3D_object_get_string(display_name, NULL, 0);
+  char *name = new char[name_length + 1];
+  P3D_object_get_string(display_name, name, name_length + 1);
+
+  cerr << "Downloading " << name << "\n";
+
+  delete[] name;
+  P3D_object_decref(display_name);
+  _reporting_download = true;
+}
+ 
+////////////////////////////////////////////////////////////////////
+//     Function: Panda3D::report_download_complete
+//       Access: Private
+//  Description: Tells the user we're done downloading packages
+////////////////////////////////////////////////////////////////////
+void Panda3D::
+report_download_complete(P3D_instance *instance) {
+  if (_reporting_download) {
+    cerr << "Download complete.\n";
+  }
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: Panda3D::URLGetter::Constructor
 //       Access: Public
@@ -758,8 +834,6 @@ URLGetter(P3D_instance *instance, int unique_id,
 {
   HTTPClient *http = HTTPClient::get_global_ptr();
 
-  cerr << "Getting URL " << _url << "\n";
-
   _channel = http->make_channel(false);
   if (_post_data.empty()) {
     _channel->begin_get_document(_url);
@@ -815,8 +889,6 @@ run() {
       status = P3D_RC_generic_error;
     }
     cerr << "Error getting URL " << _url << "\n";
-  } else {
-    cerr << "Done getting URL " << _url << ", got " << _bytes_sent << " bytes\n";
   }
 
   P3D_instance_feed_url_stream

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

@@ -65,9 +65,15 @@ private:
   void usage();
   bool parse_int_pair(char *arg, int &x, int &y);
 
+  void report_downloading_package(P3D_instance *instance);
+  void report_download_complete(P3D_instance *instance);
+
 private:
   string _root_dir;
+  string _log_dirname;
+  string _log_basename;
   FileSpec _core_api_dll;
+  bool _reporting_download;
 
   typedef pset<P3D_instance *> Instances;
   Instances _instances;

+ 1 - 6
direct/src/showutil/packp3d.py

@@ -84,7 +84,7 @@ def makePackedApp(args):
         elif option == '-h':
             print __doc__
             sys.exit(1)
-    
+
     if not args:
         raise ArgumentError, "No destination app specified.  Use:\npackp3d.py app.p3d"
 
@@ -133,10 +133,6 @@ def main(appRunner):
     """ This function is called when this module is invoked as
     packp3d.p3d. """
 
-    print "args = %s" % (appRunner.argv,)
-    vfs = VirtualFileSystem.getGlobalPtr()
-    print "cwd = %s, %s" % (vfs.getCwd(), ExecutionEnvironment.getCwd())
-    print "sys.path = %s" % (sys.path,)
     try:
         makePackedApp(appRunner.argv[1:])
     except ArgumentError, e:
@@ -145,7 +141,6 @@ def main(appRunner):
     sys.exit(0)
 
 if __name__ == '__main__':
-    print "sys.path = %s" % (sys.path,)
     try:
         makePackedApp(sys.argv[1:])
     except ArgumentError, e:

+ 0 - 6
direct/src/showutil/runp3d.py

@@ -244,7 +244,6 @@ class AppRunner(DirectObject):
         same page with the Panda3D plugin. """
 
         self.dom = dom
-        print "setBrowserScriptObject(%s)" % (dom)
 
         # Now evaluate any deferred expressions.
         for expression in self.deferredEvals:
@@ -334,11 +333,6 @@ class AppRunner(DirectObject):
         settings, for future windows; or applies them directly to the
         main window if the window has already been opened. """
 
-        print "session %s, nextScriptId = %s" % (self.sessionId, self.nextScriptId)
-
-
-        print "setupWindow %s, %s, %s, %s, %s, %s, %s" % (windowType, x, y, width, height, parent, subprocessWindow)
-
         if self.started and base.win:
             # If we've already got a window, this must be a
             # resize/reposition request.

+ 1 - 1
direct/src/stdpy/file.py

@@ -248,7 +248,7 @@ open = file
 def listdir(path):
     """ Implements os.listdir over vfs. """
     files = []
-    dirlist = _vfs.scanDirectory(path)
+    dirlist = _vfs.scanDirectory(pm.Filename.fromOsSpecific(path))
     if dirlist is None:
         message = 'No such file or directory: %s' % (path)
         raise OSError, message