Procházet zdrojové kódy

Add p3dembed and add support for offsetted p3d payload

rdb před 16 roky
rodič
revize
51afeb20ac

+ 11 - 0
direct/src/plugin/p3dFileParams.I

@@ -23,3 +23,14 @@ inline const string &P3DFileParams::
 get_p3d_filename() const {
   return _p3d_filename;
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: P3DFileParams::get_p3d_offset
+//       Access: Public
+//  Description: Returns the p3d file offset, the location
+//               in the file where the p3d data starts.
+////////////////////////////////////////////////////////////////////
+inline const int &P3DFileParams::
+get_p3d_offset() const {
+  return _p3d_offset;
+}

+ 15 - 1
direct/src/plugin/p3dFileParams.cxx

@@ -21,7 +21,7 @@
 //  Description: 
 ////////////////////////////////////////////////////////////////////
 P3DFileParams::
-P3DFileParams() {
+P3DFileParams() : _p3d_offset(0) {
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -32,6 +32,7 @@ P3DFileParams() {
 P3DFileParams::
 P3DFileParams(const P3DFileParams &copy) :
   _p3d_filename(copy._p3d_filename),
+  _p3d_offset(copy._p3d_offset),
   _tokens(copy._tokens),
   _args(copy._args)
 {
@@ -45,6 +46,7 @@ P3DFileParams(const P3DFileParams &copy) :
 void P3DFileParams::
 operator = (const P3DFileParams &other) {
   _p3d_filename = other._p3d_filename;
+  _p3d_offset = other._p3d_offset;
   _tokens = other._tokens;
   _args = other._args;
 }
@@ -59,6 +61,17 @@ set_p3d_filename(const string &p3d_filename) {
   _p3d_filename = p3d_filename;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: P3DFileParams::set_p3d_filename
+//       Access: Public
+//  Description: Specifies the location in the file where
+//               the p3d file data starts.
+////////////////////////////////////////////////////////////////////
+void P3DFileParams::
+set_p3d_offset(const int &p3d_offset) {
+  _p3d_offset = p3d_offset;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: P3DFileParams::set_tokens
 //       Access: Public
@@ -165,6 +178,7 @@ make_xml() {
   TiXmlElement *xfparams = new TiXmlElement("fparams");
 
   xfparams->SetAttribute("p3d_filename", _p3d_filename);
+  xfparams->SetAttribute("p3d_offset", _p3d_offset);
 
   Tokens::const_iterator ti;
   for (ti = _tokens.begin(); ti != _tokens.end(); ++ti) {

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

@@ -31,10 +31,12 @@ public:
   void operator = (const P3DFileParams &other);
 
   void set_p3d_filename(const string &p3d_filename);
+  void set_p3d_offset(const int &p3d_offset);
   void set_tokens(const P3D_token tokens[], size_t num_tokens);
   void set_args(int argc, const char *argv[]);
 
   inline const string &get_p3d_filename() const;
+  inline const int &get_p3d_offset() const;
   string lookup_token(const string &keyword) const;
   int lookup_token_int(const string &keyword) const;
   bool has_token(const string &keyword) const;
@@ -51,6 +53,7 @@ private:
   typedef vector<string> Args;
 
   string _p3d_filename;
+  int _p3d_offset;
   Tokens _tokens;
   Args _args;
 };

+ 17 - 8
direct/src/plugin/p3dInstance.cxx

@@ -211,7 +211,7 @@ P3DInstance::
 
   if (_auth_session != NULL) {
     _auth_session->shutdown(false);
-    unref_delete(_auth_session);
+    p3d_unref_delete(_auth_session);
     _auth_session = NULL;
   }
 
@@ -389,9 +389,9 @@ make_p3d_stream(const string &p3d_url) {
 //               Normally this is only called once.
 ////////////////////////////////////////////////////////////////////
 void P3DInstance::
-set_p3d_filename(const string &p3d_filename) {
+set_p3d_filename(const string &p3d_filename, const int &p3d_offset) {
   determine_p3d_basename(p3d_filename);
-  priv_set_p3d_filename(p3d_filename);
+  priv_set_p3d_filename(p3d_filename, p3d_offset);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -789,7 +789,7 @@ feed_url_stream(int unique_id,
   if (!download_ok || download->get_download_finished()) {
     // All done.
     _downloads.erase(di);
-    unref_delete(download);
+    p3d_unref_delete(download);
   }
 
   return download_ok;
@@ -1239,7 +1239,7 @@ auth_button_clicked() {
   // Delete the previous session and create a new one.
   if (_auth_session != NULL) {
     _auth_session->shutdown(false);
-    unref_delete(_auth_session);
+    p3d_unref_delete(_auth_session);
     _auth_session = NULL;
   }
   
@@ -1311,7 +1311,7 @@ auth_finished_main_thread() {
 //               internally, and might be passed a temporary filename.
 ////////////////////////////////////////////////////////////////////
 void P3DInstance::
-priv_set_p3d_filename(const string &p3d_filename) {
+priv_set_p3d_filename(const string &p3d_filename, const int &p3d_offset) {
   if (!_fparams.get_p3d_filename().empty()) {
     nout << "p3d_filename already set to: " << _fparams.get_p3d_filename()
          << ", trying to set to " << p3d_filename << "\n";
@@ -1319,6 +1319,10 @@ priv_set_p3d_filename(const string &p3d_filename) {
   }
 
   _fparams.set_p3d_filename(p3d_filename);
+  // The default for p3d_offset is -1, which means not to change it.
+  if (p3d_offset >= 0) {
+	_fparams.set_p3d_offset(p3d_offset);
+  }
   _got_fparams = true;
 
   _panda_script_object->set_float_property("instanceDownloadProgress", 1.0);
@@ -1328,8 +1332,13 @@ priv_set_p3d_filename(const string &p3d_filename) {
   // if Python has not yet started).
   send_notify("onpluginload");
 
-  if (!_mf_reader.open_read(_fparams.get_p3d_filename())) {
-    nout << "Couldn't read " << _fparams.get_p3d_filename() << "\n";
+  if (!_mf_reader.open_read(_fparams.get_p3d_filename(), _fparams.get_p3d_offset())) {
+	if (_fparams.get_p3d_offset() == 0) {
+      nout << "Couldn't read " << _fparams.get_p3d_filename() << "\n";
+    } else {
+	  nout << "Couldn't read " << _fparams.get_p3d_filename()
+	       << " at offset " << _fparams.get_p3d_offset() << "\n";
+	}
     set_failed();
     return;
   }

+ 2 - 2
direct/src/plugin/p3dInstance.h

@@ -58,7 +58,7 @@ public:
   ~P3DInstance();
 
   void set_p3d_url(const string &p3d_url);
-  void set_p3d_filename(const string &p3d_filename);
+  void set_p3d_filename(const string &p3d_filename, const int &p3d_offset = 0);
   int make_p3d_stream(const string &p3d_url);
   inline const P3DFileParams &get_fparams() const;
 
@@ -156,7 +156,7 @@ private:
     IT_num_image_types,     // Not a real value
   };
 
-  void priv_set_p3d_filename(const string &p3d_filename);
+  void priv_set_p3d_filename(const string &p3d_filename, const int &p3d_offset = -1);
   void determine_p3d_basename(const string &p3d_url);
 
   bool check_matches_origin(const string &origin_match);

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

@@ -129,7 +129,7 @@ P3DInstanceManager::
   assert(_sessions.empty());
 
   if (_auth_session != NULL) {
-    unref_delete(_auth_session);
+    p3d_unref_delete(_auth_session);
     _auth_session = NULL;
   }
 
@@ -424,13 +424,13 @@ create_instance(P3D_request_ready_func *func,
 ////////////////////////////////////////////////////////////////////
 bool P3DInstanceManager::
 set_p3d_filename(P3DInstance *inst, bool is_local,
-                 const string &p3d_filename) {
+                 const string &p3d_filename, const int &p3d_offset) {
   if (inst->is_started()) {
     nout << "Instance started twice: " << inst << "\n";
     return false;
   }
   if (is_local) {
-    inst->set_p3d_filename(p3d_filename);
+    inst->set_p3d_filename(p3d_filename, p3d_offset);
   } else {
     inst->set_p3d_url(p3d_filename);
   }
@@ -513,11 +513,11 @@ finish_instance(P3DInstance *inst) {
     if (session->get_num_instances() == 0) {
       _sessions.erase(session->get_session_key());
       session->shutdown();
-      unref_delete(session);
+      p3d_unref_delete(session);
     }
   }
 
-  unref_delete(inst);
+  p3d_unref_delete(inst);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -534,7 +534,7 @@ authorize_instance(P3DInstance *inst) {
     // We only want one auth_session window open at a time, to
     // minimize user confusion, so close any previous window.
     _auth_session->shutdown(true);
-    unref_delete(_auth_session);
+    p3d_unref_delete(_auth_session);
     _auth_session = NULL;
   }
 

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

@@ -93,7 +93,7 @@ public:
                   int argc, const char *argv[], void *user_data);
 
   bool set_p3d_filename(P3DInstance *inst, bool is_local,
-                        const string &p3d_filename);
+                        const string &p3d_filename, const int &p3d_offset);
   int make_p3d_stream(P3DInstance *inst, const string &p3d_url);
   bool start_instance(P3DInstance *inst);
   void finish_instance(P3DInstance *inst);

+ 12 - 9
direct/src/plugin/p3dMultifileReader.cxx

@@ -39,6 +39,7 @@ const int P3DMultifileReader::_current_minor_ver = 1;
 P3DMultifileReader::
 P3DMultifileReader() {
   _is_open = false;
+  _read_offset = 0;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -48,10 +49,12 @@ P3DMultifileReader() {
 //               on success, false on failure.
 ////////////////////////////////////////////////////////////////////
 bool P3DMultifileReader::
-open_read(const string &pathname) {
+open_read(const string &pathname, const int &offset) {
   if (_is_open) {
     close();
   }
+ 
+  _read_offset = offset;
   if (!read_header(pathname)) {
     return false;
   }
@@ -220,7 +223,7 @@ read_header(const string &pathname) {
   }
 
   char this_header[_header_size];
-  _in.seekg(0);
+  _in.seekg(_read_offset);
 
   // Here's a special case: if the multifile begins with a hash
   // character, then we continue reading and discarding lines of ASCII
@@ -296,7 +299,7 @@ read_index() {
   }
   while (next_entry != 0) {
     Subfile s;
-    s._index_start = (size_t)_in.tellg();
+    s._index_start = (size_t)_in.tellg() - _read_offset;
     s._index_length = 0;
     s._data_start = read_uint32();
     s._data_length = read_uint32();
@@ -319,7 +322,7 @@ read_index() {
     s._filename = string(buffer, name_length);
     delete[] buffer;
 
-    s._index_length = (size_t)_in.tellg() - s._index_start;
+    s._index_length = (size_t)_in.tellg() - s._index_start - _read_offset;
 
     if (flags & SF_signature) {
       // A subfile with this bit set is a signature.
@@ -334,7 +337,7 @@ read_index() {
       }
     }
 
-    _in.seekg(next_entry);
+    _in.seekg(next_entry + _read_offset);
     next_entry = read_uint32();
     if (!_in) {
       return false;
@@ -353,7 +356,7 @@ read_index() {
 ////////////////////////////////////////////////////////////////////
 bool P3DMultifileReader::
 extract_subfile(ostream &out, const Subfile &s) {
-  _in.seekg(s._data_start);
+  _in.seekg(s._data_start + _read_offset);
 
   static const size_t buffer_size = 4096;
   char buffer[buffer_size];
@@ -397,7 +400,7 @@ check_signatures() {
     Subfile *subfile = &(*pi);
 
     // Extract the signature data and certificate separately.
-    _in.seekg(subfile->_data_start);
+    _in.seekg(subfile->_data_start + _read_offset);
     size_t sig_size = read_uint32();
     char *sig = new char[sig_size];
     _in.read(sig, sig_size);
@@ -410,7 +413,7 @@ check_signatures() {
     size_t num_certs = read_uint32();
 
     // Read the remaining buffer of certificate data.
-    size_t bytes_read = (size_t)_in.tellg() - subfile->_data_start;
+    size_t bytes_read = (size_t)_in.tellg() - subfile->_data_start - _read_offset;
     size_t buffer_size = subfile->_data_length - bytes_read;
     char *buffer = new char[buffer_size];
     _in.read(buffer, buffer_size);
@@ -463,7 +466,7 @@ check_signatures() {
 
       // Read and hash the multifile contents, but only up till
       // _last_data_byte.
-      _in.seekg(0);
+      _in.seekg(_read_offset);
       streampos bytes_remaining = (streampos)_last_data_byte;
       static const size_t buffer_size = 4096;
       char buffer[buffer_size];

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

@@ -30,7 +30,7 @@
 class P3DMultifileReader {
 public:
   P3DMultifileReader();
-  bool open_read(const string &pathname);
+  bool open_read(const string &pathname, const int &offset = 0);
   inline bool is_open() const;
   void close();
 
@@ -82,6 +82,7 @@ private:
 
   ifstream _in;
   bool _is_open;
+  int _read_offset;
 
   typedef vector<Subfile> Subfiles;
   Subfiles _subfiles;

+ 5 - 4
direct/src/plugin/p3dPackage.cxx

@@ -429,7 +429,7 @@ contents_file_redownload_finished(bool success) {
       
     } else {
       download->resume_download_finished(false);
-      unref_delete(download);
+      p3d_unref_delete(download);
     }
   }
 }
@@ -1108,7 +1108,7 @@ void P3DPackage::
 set_active_download(Download *download) {
   if (_active_download != download) {
     if (_active_download != NULL) {
-      unref_delete(_active_download);
+      p3d_unref_delete(_active_download);
     }
     _active_download = download;
     if (_active_download != NULL) {
@@ -1127,7 +1127,7 @@ void P3DPackage::
 set_saved_download(Download *download) {
   if (_saved_download != download) {
     if (_saved_download != NULL) {
-      unref_delete(_saved_download);
+      p3d_unref_delete(_saved_download);
     }
     _saved_download = download;
     if (_saved_download != NULL) {
@@ -1350,7 +1350,7 @@ InstallStepDownloadFile(P3DPackage *package, const FileSpec &file) :
 P3DPackage::InstallStepDownloadFile::
 ~InstallStepDownloadFile() {
   if (_download != NULL) {
-    unref_delete(_download);
+    p3d_unref_delete(_download);
   }
 }
 
@@ -1577,6 +1577,7 @@ P3DPackage::InstallToken P3DPackage::InstallStepUnpackArchive::
 do_step(bool download_finished) {
   string source_pathname = _package->get_archive_file_pathname();
   P3DMultifileReader reader;
+
   if (!reader.open_read(source_pathname)) {
     nout << "Couldn't read " << source_pathname << "\n";
     return IT_step_failed;

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

@@ -38,7 +38,7 @@ P3DPythonObject::
   // child process that we no longer need the corresponding PyObject
   // to be kept around.
   _session->drop_pyobj(_object_id);
-  unref_delete(_session);
+  p3d_unref_delete(_session);
 }
 
 ////////////////////////////////////////////////////////////////////

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

@@ -1262,10 +1262,15 @@ add_package_info(P3DCInstance *inst, TiXmlElement *xpackage) {
 void P3DPythonRun::
 set_p3d_filename(P3DCInstance *inst, TiXmlElement *xfparams) {
   string p3d_filename;
+  int p3d_offset;
   const char *p3d_filename_c = xfparams->Attribute("p3d_filename");
   if (p3d_filename_c != NULL) {
     p3d_filename = p3d_filename_c;
   }
+  const char *p3d_offset_c = xfparams->Attribute("p3d_offset");
+  if (p3d_offset_c != NULL) {
+    p3d_offset = atoi(p3d_offset_c);
+  }
 
   PyObject *token_list = PyList_New(0);
   TiXmlElement *xtoken = xfparams->FirstChildElement("token");
@@ -1306,8 +1311,8 @@ set_p3d_filename(P3DCInstance *inst, TiXmlElement *xfparams) {
   }
 
   PyObject *result = PyObject_CallMethod
-    (_runner, (char *)"setP3DFilename", (char *)"sOOii", p3d_filename.c_str(),
-     token_list, arg_list, inst->get_instance_id(), _interactive_console);
+    (_runner, (char *)"setP3DFilename", (char *)"sOOiii", p3d_filename.c_str(),
+     token_list, arg_list, inst->get_instance_id(), _interactive_console, p3d_offset);
   Py_DECREF(token_list);
   Py_DECREF(arg_list);
 

+ 3 - 3
direct/src/plugin/p3dReferenceCount.I

@@ -47,7 +47,7 @@ ref() const {
 //     Function: P3DReferenceCount::unref
 //       Access: Public
 //  Description: Explicitly decrements the reference count.  Usually,
-//               you should call unref_delete() instead.
+//               you should call p3d_unref_delete() instead.
 //
 //               The return value is true if the new reference count
 //               is nonzero, false if it is zero.
@@ -68,14 +68,14 @@ get_ref_count() const {
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: unref_delete
+//     Function: p3d_unref_delete
 //  Description: This global helper function will unref the given
 //               P3DReferenceCount object, and if the reference count
 //               reaches zero, automatically delete it.
 ////////////////////////////////////////////////////////////////////
 template<class RefCountType>
 inline void
-unref_delete(RefCountType *ptr) {
+p3d_unref_delete(RefCountType *ptr) {
   if (!ptr->unref()) {
     delete ptr;
   } 

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

@@ -38,7 +38,7 @@ private:
 };
 
 template<class RefCountType>
-inline void unref_delete(RefCountType *ptr);
+inline void p3d_unref_delete(RefCountType *ptr);
 
 #include "p3dReferenceCount.I"
 

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

@@ -279,7 +279,7 @@ terminate_instance(P3DInstance *inst) {
     _instances.erase(inst->get_instance_id());
   }
   RELEASE_LOCK(_instances_lock);
-  unref_delete(inst);
+  p3d_unref_delete(inst);
 }
 
 ////////////////////////////////////////////////////////////////////

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

@@ -132,7 +132,7 @@ P3D_new_instance(P3D_request_ready_func *func,
 
 bool
 P3D_instance_start(P3D_instance *instance, bool is_local, 
-                   const char *p3d_filename) {
+                   const char *p3d_filename, const int p3d_offset) {
   assert(P3DInstanceManager::get_global_ptr()->is_initialized());
   if (p3d_filename == NULL) {
     p3d_filename = "";
@@ -145,7 +145,8 @@ P3D_instance_start(P3D_instance *instance, bool is_local,
     // We don't actually start it immediately; the instance will have
     // to download the p3d url and read it, reading the python
     // version, before it can start.
-    result = inst_mgr->set_p3d_filename(inst, is_local, p3d_filename);
+    result = inst_mgr->set_p3d_filename(inst, is_local,
+                                        p3d_filename, p3d_offset);
   }
   RELEASE_LOCK(_api_lock);
   return result;

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

@@ -342,7 +342,7 @@ P3D_new_instance_func(P3D_request_ready_func *func,
    The return value is true on success, false on failure. */
 typedef bool
 P3D_instance_start_func(P3D_instance *instance, bool is_local,
-                        const char *p3d_filename);
+                        const char *p3d_filename, const int p3d_offset = 0);
 
 /* This function is an alternative to P3D_instance_start(); it
    indicates an intention to feed the p3d file data to the instance as

+ 25 - 0
direct/src/plugin_standalone/p3dEmbed.cxx

@@ -0,0 +1,25 @@
+// Filename: p3dEmbed.cxx
+// Created by:  rdb (07Dec09)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 "p3d_plugin_composite1.cxx"
+#include "panda3dBase.cxx"
+#include <stdint.h>
+
+const uint32_t p3d_offset = 0xFF3D3D00;
+
+int
+main(int argc, char *argv[]) {
+  Panda3DBase program(true);
+  return program.run_embedded(p3d_offset, argc, argv);
+}

+ 0 - 24
direct/src/plugin_standalone/panda3d.I

@@ -11,27 +11,3 @@
 // with this source code in a file named "LICENSE."
 //
 ////////////////////////////////////////////////////////////////////
-
-
-////////////////////////////////////////////////////////////////////
-//     Function: Panda3D::time_to_exit
-//       Access: Public
-//  Description: Returns true if it is time to exit because the last
-//               instance has exited, or false if we should continue
-//               running.
-////////////////////////////////////////////////////////////////////
-bool Panda3D::
-time_to_exit() {
-  return _instances.empty() && _exit_with_last_instance;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: Panda3D::URLGetter::get_instance
-//       Access: Public
-//  Description: Returns the P3D_instance associated with this
-//               URLGetter.
-////////////////////////////////////////////////////////////////////
-P3D_instance *Panda3D::URLGetter::
-get_instance() {
-  return _instance;
-}

+ 3 - 623
direct/src/plugin_standalone/panda3d.cxx

@@ -13,11 +13,11 @@
 ////////////////////////////////////////////////////////////////////
 
 #include "panda3d.h"
-#include "httpClient.h"
 #include "load_plugin.h"
-#include "find_root_dir.h"
 #include "p3d_plugin_config.h"
 
+#include "panda3dBase.cxx"
+
 // We can include this header file to get the DTOOL_PLATFORM
 // definition, even though we don't link with dtool.
 #include "dtool_platform.h"
@@ -40,38 +40,13 @@
   #endif
 #endif
 
-
-// The amount of time in seconds to wait for new messages.
-static const double wait_cycle = 0.2;
-
 ////////////////////////////////////////////////////////////////////
 //     Function: Panda3D::Constructor
 //       Access: Public
 //  Description: 
 ////////////////////////////////////////////////////////////////////
 Panda3D::
-Panda3D(bool console_environment) {
-  _console_environment = console_environment;
-
-  _root_dir = find_root_dir();
-  _reporting_download = false;
-  _enable_security = false;
-
-  _window_type = P3D_WT_toplevel;
-
-  _win_x = 0;
-  _win_y = 0;
-  _win_width = 640;
-  _win_height = 480;
-
-  _exit_with_last_instance = true;
-  _host_url = PANDA_PACKAGE_HOST_URL;
-  _this_platform = DTOOL_PLATFORM;
-  _verify_contents = false;
-
-  // Seed the lame random number generator in rand(); we use it to
-  // select a mirror for downloading.
-  srand((unsigned int)time(NULL));
+Panda3D(bool console_environment) : Panda3DBase(console_environment) {
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -298,106 +273,6 @@ run_command_line(int argc, char *argv[]) {
   return 0;
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: Panda3D::run_main_loop
-//       Access: Public
-//  Description: Gets lost in the application main loop, waiting for
-//               system events and notifications from the open
-//               instance(s).
-////////////////////////////////////////////////////////////////////
-void Panda3D::
-run_main_loop() {
-#ifdef _WIN32
-  if (_window_type == P3D_WT_embedded) {
-    // Wait for new messages from Windows, and new requests from the
-    // plugin.
-    MSG msg;
-    int retval;
-    retval = GetMessage(&msg, NULL, 0, 0);
-    while (retval != 0 && !time_to_exit()) {
-      if (retval == -1) {
-        cerr << "Error processing message queue.\n";
-        return;
-      }
-      TranslateMessage(&msg);
-      DispatchMessage(&msg);
-
-      // Check for new requests from the Panda3D plugin.
-      P3D_instance *inst = P3D_check_request(wait_cycle);
-      while (inst != (P3D_instance *)NULL) {
-        P3D_request *request = P3D_instance_get_request(inst);
-        if (request != (P3D_request *)NULL) {
-          handle_request(request);
-        }
-        inst = P3D_check_request(wait_cycle);
-      }
-
-      while (!_url_getters.empty() && 
-             !PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) {
-        // If there are no Windows messages, check the download tasks.
-        run_getters();
-      }
-      retval = GetMessage(&msg, NULL, 0, 0);
-    }
-    
-    // WM_QUIT has been received.  Terminate all instances, and fall
-    // through.
-    while (!_instances.empty()) {
-      P3D_instance *inst = *(_instances.begin());
-      delete_instance(inst);
-    }
-
-  } else {
-    // Not an embedded window, so we don't have our own window to
-    // generate Windows events.  Instead, just wait for requests.
-    while (!time_to_exit()) {
-      P3D_instance *inst = P3D_check_request(wait_cycle);
-      if (inst != (P3D_instance *)NULL) {
-        P3D_request *request = P3D_instance_get_request(inst);
-        if (request != (P3D_request *)NULL) {
-          handle_request(request);
-        }
-      }
-      run_getters();
-    }
-  }
-
-#elif defined(__APPLE__)
-  // OSX really prefers to own the main loop, so we install a timer to
-  // call out to our instances and getters, rather than polling within
-  // the event loop as we do in the Windows case, above.
-  EventLoopRef main_loop = GetMainEventLoop();
-  EventLoopTimerUPP timer_upp = NewEventLoopTimerUPP(st_timer_callback);
-  EventLoopTimerRef timer;
-  EventTimerInterval interval = wait_cycle * kEventDurationSecond;
-  InstallEventLoopTimer(main_loop, interval, interval,
-                        timer_upp, this, &timer);
-  RunApplicationEventLoop();
-  RemoveEventLoopTimer(timer);
-  
-  // Terminate all instances, and fall through.
-  while (!_instances.empty()) {
-    P3D_instance *inst = *(_instances.begin());
-    delete_instance(inst);
-  }
-    
-#else  // _WIN32, __APPLE__
-
-  // Now wait while we process pending requests.
-  while (!time_to_exit()) {
-    P3D_instance *inst = P3D_check_request(wait_cycle);
-    if (inst != (P3D_instance *)NULL) {
-      P3D_request *request = P3D_instance_get_request(inst);
-      if (request != (P3D_request *)NULL) {
-        handle_request(request);
-      }
-    }
-    run_getters();
-  }
-
-#endif  // _WIN32, __APPLE__
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: Panda3D::post_arg_processing
 //       Access: Protected
@@ -817,260 +692,6 @@ get_core_api(const Filename &contents_filename, TiXmlElement *xpackage) {
   return true;
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: Panda3D::run_getters
-//       Access: Protected
-//  Description: Polls all of the active URL requests.
-////////////////////////////////////////////////////////////////////
-void Panda3D::
-run_getters() {
-  URLGetters::iterator gi;
-  gi = _url_getters.begin();
-  while (gi != _url_getters.end()) {
-    URLGetter *getter = (*gi);
-    if (getter->run()) {
-      // This URLGetter is still working.  Leave it.
-      ++gi;
-    } else {
-      // This URLGetter is done.  Remove it and delete it.
-      URLGetters::iterator dgi = gi;
-      ++gi;
-      _url_getters.erase(dgi);
-      delete getter;
-    }
-  }
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: Panda3D::handle_request
-//       Access: Protected
-//  Description: Handles a single request received via the plugin API
-//               from a p3d instance.
-////////////////////////////////////////////////////////////////////
-void Panda3D::
-handle_request(P3D_request *request) {
-  bool handled = false;
-  switch (request->_request_type) {
-  case P3D_RT_stop:
-    delete_instance(request->_instance);
-#ifdef _WIN32
-    // Post a silly message to spin the event loop.
-    PostMessage(NULL, WM_USER, 0, 0);
-#endif
-    handled = true;
-    break;
-
-  case P3D_RT_get_url:
-    {
-      int unique_id = request->_request._get_url._unique_id;
-      const string &url = request->_request._get_url._url;
-      URLGetter *getter = new URLGetter
-        (request->_instance, unique_id, URLSpec(url), "");
-      _url_getters.insert(getter);
-      handled = true;
-    }
-    break;
-
-  case P3D_RT_notify:
-    {
-      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:
-    break;
-  };
-
-  P3D_request_finish(request, handled);
-}
-
-#ifdef _WIN32
-LONG WINAPI
-window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
-  switch (msg) {
-  case WM_DESTROY:
-    PostQuitMessage(0);
-    break;
-  };
-
-  return DefWindowProc(hwnd, msg, wparam, lparam);
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: Panda3D::make_parent_window
-//       Access: Protected
-//  Description: Creates a toplevel window to contain the embedded
-//               instances.  Windows implementation.
-////////////////////////////////////////////////////////////////////
-void Panda3D::
-make_parent_window() {
-  WNDCLASS wc;
-
-  HINSTANCE application = GetModuleHandle(NULL);
-  ZeroMemory(&wc, sizeof(WNDCLASS));
-  wc.lpfnWndProc = window_proc;
-  wc.hInstance = application;
-  wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
-  wc.lpszClassName = "panda3d";
-
-  if (!RegisterClass(&wc)) {
-    cerr << "Could not register window class!\n";
-    exit(1);
-  }
-
-  DWORD window_style = 
-    WS_POPUP | WS_CLIPCHILDREN | WS_CLIPSIBLINGS |
-    WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX |
-    WS_SIZEBOX | WS_MAXIMIZEBOX;
-
-  HWND toplevel_window = 
-    CreateWindow("panda3d", "Panda3D", window_style,
-                 CW_USEDEFAULT, CW_USEDEFAULT, _win_width, _win_height,
-                 NULL, NULL, application, 0);
-  if (!toplevel_window) {
-    cerr << "Could not create toplevel window!\n";
-    exit(1);
-  }
-
-  ShowWindow(toplevel_window, SW_SHOWNORMAL);
-
-  memset(&_parent_window, 0, sizeof(_parent_window));
-  _parent_window._window_handle_type = P3D_WHT_win_hwnd;
-  _parent_window._handle._win_hwnd._hwnd = toplevel_window;
-}
-
-#else
-
-////////////////////////////////////////////////////////////////////
-//     Function: Panda3D::make_parent_window
-//       Access: Protected
-//  Description: Creates a toplevel window to contain the embedded
-//               instances.
-////////////////////////////////////////////////////////////////////
-void Panda3D::
-make_parent_window() {
-  // TODO.
-  assert(false);
-}
-
-#endif
-
-////////////////////////////////////////////////////////////////////
-//     Function: Panda3D::create_instance
-//       Access: Protected
-//  Description: Uses the plugin API to create a new P3D instance to
-//               play a particular .p3d file.  This instance is also
-//               started if start_instance is true (which requires
-//               that the named p3d file exists).
-////////////////////////////////////////////////////////////////////
-P3D_instance *Panda3D::
-create_instance(const string &p3d, bool start_instance,
-                int win_x, int win_y, int win_width, int win_height,
-                char **args, int num_args) {
-  // 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);
-  string os_p3d_filename = p3d;
-  bool is_local = !is_url(p3d);
-  if (is_local && start_instance) {
-    if (!p3d_filename.exists()) {
-      cerr << "No such file: " << p3d_filename << "\n";
-      exit(1);
-    }
-
-    p3d_filename.make_absolute();
-    os_p3d_filename = p3d_filename.to_os_specific();
-  } 
-
-  // Build up the token list.
-  Tokens tokens = _tokens;
-  P3D_token token;
-
-  string log_basename;
-  if (!_log_dirname.empty()) {
-    // Generate output to a logfile.
-    log_basename = p3d_filename.get_basename_wo_extension();
-    token._keyword = "log_basename";
-    token._value = log_basename.c_str();
-    tokens.push_back(token);
-  } else {
-    // Send output to the console.
-    token._keyword = "console_output";
-    if (_console_environment) {
-      token._value = "1";
-    } else {
-      token._value = "0";
-    }
-    tokens.push_back(token);
-  }
-
-  token._keyword = "auto_start";
-  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());
-  for (int i = 0; i < num_args; ++i) {
-    argv.push_back(args[i]);
-  }
-
-  P3D_instance *inst = P3D_new_instance(NULL, tokens_p, num_tokens,
-                                        argv.size(), &argv[0], NULL);
-
-  if (inst != NULL) {
-    if (start_instance) {
-      // 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);
-  }
-
-  return inst;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: Panda3D::delete_instance
-//       Access: Protected
-//  Description: Deletes the indicated instance and removes it from
-//               the internal structures.
-////////////////////////////////////////////////////////////////////
-void Panda3D::
-delete_instance(P3D_instance *inst) {
-  P3D_instance_finish(inst);
-  _instances.erase(inst);
-
-  // Make sure we also terminate any pending URLGetters associated
-  // with this instance.
-  URLGetters::iterator gi;
-  gi = _url_getters.begin();
-  while (gi != _url_getters.end()) {
-    URLGetter *getter = (*gi);
-    if (getter->get_instance() == inst) {
-      URLGetters::iterator dgi = gi;
-      ++gi;
-      _url_getters.erase(dgi);
-      delete getter;
-    }
-  }
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: Panda3D::usage
 //       Access: Protected
@@ -1163,244 +784,3 @@ usage() {
     << "    Specify the platform to masquerade as.  The default is \""
     << DTOOL_PLATFORM << "\" .\n\n";
 }
-
-////////////////////////////////////////////////////////////////////
-//     Function: Panda3D::parse_token
-//       Access: Protected
-//  Description: Parses a web token of the form token=value, and
-//               stores it in _tokens.  Returns true on success, false
-//               on failure.
-////////////////////////////////////////////////////////////////////
-bool Panda3D::
-parse_token(char *arg) {
-  char *equals = strchr(arg, '=');
-  if (equals == NULL) {
-    return false;
-  }
-
-  // Directly munge the C string to truncate it at the equals sign.
-  // Classic C tricks.
-  *equals = '\0';
-  P3D_token token;
-  token._keyword = strdup(arg);
-  token._value = strdup(equals + 1);
-  *equals = '=';
-
-  _tokens.push_back(token);
-
-  return true;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: Panda3D::parse_int_pair
-//       Access: Protected
-//  Description: Parses a string into an x,y pair of integers.
-//               Returns true on success, false on failure.
-////////////////////////////////////////////////////////////////////
-bool Panda3D::
-parse_int_pair(char *arg, int &x, int &y) {
-  char *endptr;
-  x = strtol(arg, &endptr, 10);
-  if (*endptr == ',') {
-    y = strtol(endptr + 1, &endptr, 10);
-    if (*endptr == '\0') {
-      return true;
-    }
-  }
-
-  // Some parse error on the string.
-  return false;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: Panda3D::is_url
-//       Access: Protected, Static
-//  Description: Returns true if the indicated string appears to be a
-//               URL, with a leading http:// or file:// or whatever,
-//               or false if it must be a local filename instead.
-////////////////////////////////////////////////////////////////////
-bool Panda3D::
-is_url(const string &param) {
-  // We define a URL prefix as a sequence of at least two letters,
-  // followed by a colon, followed by at least one slash.
-  size_t p = 0;
-  while (p < param.size() && isalpha(param[p])) {
-    ++p;
-  }
-  if (p < 2) {
-    // Not enough letters.
-    return false;
-  }
-  if (p >= param.size() || param[p] != ':') {
-    // No colon.
-    return false;
-  }
-  ++p;
-  if (p >= param.size() || param[p] != '/') {
-    // No slash.
-    return false;
-  }
-
-  // It matches the rules.
-  return true;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: Panda3D::report_downloading_package
-//       Access: Protected
-//  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 << "Installing package.\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 << "Installing " << name << "\n";
-
-  delete[] name;
-  P3D_object_decref(display_name);
-  _reporting_download = true;
-}
- 
-////////////////////////////////////////////////////////////////////
-//     Function: Panda3D::report_download_complete
-//       Access: Protected
-//  Description: Tells the user we're done downloading packages
-////////////////////////////////////////////////////////////////////
-void Panda3D::
-report_download_complete(P3D_instance *instance) {
-  if (_reporting_download) {
-    cerr << "Install complete.\n";
-  }
-}
-
-#ifdef __APPLE__
-////////////////////////////////////////////////////////////////////
-//     Function: Panda3D::st_timer_callback
-//       Access: Protected, Static
-//  Description: Installed as a timer on the event loop, so we can
-//               process local events, in the Apple implementation.
-////////////////////////////////////////////////////////////////////
-pascal void Panda3D::
-st_timer_callback(EventLoopTimerRef timer, void *user_data) {
-  ((Panda3D *)user_data)->timer_callback(timer);
-}
-#endif  // __APPLE__
-
-#ifdef __APPLE__
-////////////////////////////////////////////////////////////////////
-//     Function: Panda3D::timer_callback
-//       Access: Protected
-//  Description: Installed as a timer on the event loop, so we can
-//               process local events, in the Apple implementation.
-////////////////////////////////////////////////////////////////////
-void Panda3D::
-timer_callback(EventLoopTimerRef timer) {
-  // Check for new requests from the Panda3D plugin.
-  P3D_instance *inst = P3D_check_request(0.0);
-  while (inst != (P3D_instance *)NULL) {
-    P3D_request *request = P3D_instance_get_request(inst);
-    if (request != (P3D_request *)NULL) {
-      handle_request(request);
-    }
-    inst = P3D_check_request(0.0);
-  }
-  
-  // Check the download tasks.
-  run_getters();
-
-  // If we're out of instances, exit the application.
-  if (time_to_exit()) {
-    QuitApplicationEventLoop();
-  }
-}
-#endif  // __APPLE__
-
-////////////////////////////////////////////////////////////////////
-//     Function: Panda3D::URLGetter::Constructor
-//       Access: Public
-//  Description: 
-////////////////////////////////////////////////////////////////////
-Panda3D::URLGetter::
-URLGetter(P3D_instance *instance, int unique_id,
-          const URLSpec &url, const string &post_data) :
-  _instance(instance),
-  _unique_id(unique_id),
-  _url(url),
-  _post_data(post_data)
-{
-  HTTPClient *http = HTTPClient::get_global_ptr();
-
-  _channel = http->make_channel(false);
-  //  _channel->set_download_throttle(true);
-  if (_post_data.empty()) {
-    _channel->begin_get_document(_url);
-  } else {
-    _channel->begin_post_form(_url, _post_data);
-  }
-
-  _channel->download_to_ram(&_rf);
-  _bytes_sent = 0;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: Panda3D::URLGetter::run
-//       Access: Public
-//  Description: Polls the URLGetter for new results.  Returns true if
-//               the URL request is still in progress and run() should
-//               be called again later, or false if the URL request
-//               has been completed and run() should not be called
-//               again.
-////////////////////////////////////////////////////////////////////
-bool Panda3D::URLGetter::
-run() {
-  if (_channel->run() || _rf.get_data_size() != 0) {
-    if (_rf.get_data_size() != 0) {
-      // Got some new data.
-      bool download_ok = P3D_instance_feed_url_stream
-        (_instance, _unique_id, P3D_RC_in_progress,
-         _channel->get_status_code(),
-         _channel->get_file_size(),
-         (const unsigned char *)_rf.get_data().data(), _rf.get_data_size());
-      _bytes_sent += _rf.get_data_size();
-      _rf.clear();
-
-      if (!download_ok) {
-        // The plugin doesn't care any more.  Interrupt the download.
-        cerr << "Download interrupted: " << _url 
-             << ", after " << _bytes_sent << " of " << _channel->get_file_size()
-             << " bytes.\n";
-        return false;
-      }
-    }
-
-    // Still more to come; call this method again later.
-    return true;
-  }
-
-  // All done.
-  P3D_result_code status = P3D_RC_done;
-  if (!_channel->is_valid()) {
-    if (_channel->get_status_code() != 0) {
-      status = P3D_RC_http_error;
-    } else {
-      status = P3D_RC_generic_error;
-    }
-    cerr << "Error getting URL " << _url << "\n";
-  }
-
-  P3D_instance_feed_url_stream
-    (_instance, _unique_id, status,
-     _channel->get_status_code(),
-     _bytes_sent, NULL, 0);
-  return false;
-}

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

@@ -15,15 +15,7 @@
 #ifndef PANDA3D_H
 #define PANDA3D_H
 
-// This program must link with Panda for HTTPClient support.  This
-// means it probably should be built with LINK_ALL_STATIC defined, so
-// we won't have to deal with confusing .dll or .so files that might
-// compete on the disk with the dynamically-loaded versions.  There's
-// no competition in memory address space, though, because
-// p3d_plugin--the only file we dynamically link in--doesn't itself
-// link with Panda.
-
-#include "pandabase.h"
+#include "panda3dBase.h"
 #include "p3d_plugin.h"
 #include "httpChannel.h"
 #include "ramfile.h"
@@ -36,12 +28,11 @@
 // Description : A standalone program that invokes the Panda3D plugin
 //               to launch .p3d files.
 ////////////////////////////////////////////////////////////////////
-class Panda3D {
+class Panda3D : private Panda3DBase {
 public:
   Panda3D(bool console_environment);
 
   int run_command_line(int argc, char *argv[]);
-  void run_main_loop();
 
 protected:
   bool post_arg_processing();
@@ -52,44 +43,10 @@ protected:
   void add_mirror(string mirror_url);
   void choose_random_mirrors(vector_string &result, int num_mirrors);
   bool get_core_api(const Filename &contents_filename, TiXmlElement *xplugin);
-  void run_getters();
-  void handle_request(P3D_request *request);
-  void make_parent_window();
-
-  P3D_instance *
-  create_instance(const string &p3d, bool start_instance,
-                  int win_x, int win_y, int win_width, int win_height,
-                  char **args, int num_args);
-  void delete_instance(P3D_instance *instance);
 
   void usage();
-  bool parse_token(char *arg);
-  bool parse_int_pair(char *arg, int &x, int &y);
-  static bool is_url(const string &param);
-
-  void report_downloading_package(P3D_instance *instance);
-  void report_download_complete(P3D_instance *instance);
-
-  inline bool time_to_exit();
-
-#ifdef __APPLE__
-  static pascal void st_timer_callback(EventLoopTimerRef timer, void *user_data);
-  void timer_callback(EventLoopTimerRef timer);
-#endif
 
 protected:
-  string _root_dir;
-  string _log_dirname;
-  string _log_basename;
-  string _this_platform;
-  bool _verify_contents;
-  P3D_window_type _window_type;
-  P3D_window_handle _parent_window;
-  int _win_x, _win_y;
-  int _win_width, _win_height;
-  bool _exit_with_last_instance;
-
-  string _host_url;
   string _super_mirror_url;
   string _host_url_prefix;
   string _download_url_prefix;
@@ -98,38 +55,6 @@ protected:
   Mirrors _mirrors;
   
   FileSpec _core_api_dll;
-  bool _reporting_download;
-  bool _enable_security;
-  bool _console_environment;
-
-  typedef pset<P3D_instance *> Instances;
-  Instances _instances;
-
-  typedef pvector<P3D_token> Tokens;
-  Tokens _tokens;
-
-  // This nested class keeps track of active URL requests.
-  class URLGetter {
-  public:
-    URLGetter(P3D_instance *instance, int unique_id,
-              const URLSpec &url, const string &post_data);
-    
-    bool run();
-    inline P3D_instance *get_instance();
-    
-  private:
-    P3D_instance *_instance;
-    int _unique_id;
-    URLSpec _url;
-    string _post_data;
-    
-    PT(HTTPChannel) _channel;
-    Ramfile _rf;
-    size_t _bytes_sent;
-  };
-  
-  typedef pset<URLGetter *> URLGetters;
-  URLGetters _url_getters;
 };
 
 #include "panda3d.I"

+ 37 - 0
direct/src/plugin_standalone/panda3dBase.I

@@ -0,0 +1,37 @@
+// Filename: panda3dBase.I
+// Created by:  pro-rsoft (07Dec09)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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: Panda3DBase::time_to_exit
+//       Access: Public
+//  Description: Returns true if it is time to exit because the last
+//               instance has exited, or false if we should continue
+//               running.
+////////////////////////////////////////////////////////////////////
+bool Panda3DBase::
+time_to_exit() {
+  return _instances.empty() && _exit_with_last_instance;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Panda3DBase::URLGetter::get_instance
+//       Access: Public
+//  Description: Returns the P3D_instance associated with this
+//               URLGetter.
+////////////////////////////////////////////////////////////////////
+P3D_instance *Panda3DBase::URLGetter::
+get_instance() {
+  return _instance;
+}

+ 691 - 0
direct/src/plugin_standalone/panda3dBase.cxx

@@ -0,0 +1,691 @@
+// Filename: panda3dBase.cxx
+// Created by:  pro-rsoft (07Dec09)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 "panda3dBase.h"
+#include "httpClient.h"
+#include "find_root_dir.h"
+#include "p3d_plugin_config.h"
+#include "executionEnvironment.h"
+
+// We can include this header file to get the DTOOL_PLATFORM
+// definition, even though we don't link with dtool.
+#include "dtool_platform.h"
+#include "pandaVersion.h"
+
+#include <ctype.h>
+#include <sstream>
+#include <algorithm>
+#ifdef _WIN32
+#include <windows.h>
+#else
+#include <signal.h>
+#endif
+
+// The amount of time in seconds to wait for new messages.
+static const double wait_cycle = 0.2;
+
+////////////////////////////////////////////////////////////////////
+//     Function: Panda3DBase::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+Panda3DBase::
+Panda3DBase(bool console_environment) {
+  _console_environment = console_environment;
+
+  _root_dir = find_root_dir();
+  _reporting_download = false;
+  _enable_security = false;
+
+  _window_type = P3D_WT_toplevel;
+
+  _win_x = 0;
+  _win_y = 0;
+  _win_width = 640;
+  _win_height = 480;
+
+  _exit_with_last_instance = true;
+  _host_url = PANDA_PACKAGE_HOST_URL;
+  _this_platform = DTOOL_PLATFORM;
+  _verify_contents = false;
+
+  // Seed the lame random number generator in rand(); we use it to
+  // select a mirror for downloading.
+  srand((unsigned int)time(NULL));
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Panda3DBase::run_embedded
+//       Access: Public
+//  Description: Gets lost in the application main loop, waiting for
+//               system events and notifications from the open
+//               instance(s).
+////////////////////////////////////////////////////////////////////
+int Panda3DBase::
+run_embedded(int read_offset, int argc, char *argv[]) {
+  if (!P3D_initialize(P3D_API_VERSION, NULL,
+                      _host_url.c_str(), _verify_contents, _this_platform.c_str(),
+                      _log_dirname.c_str(), _log_basename.c_str(),
+                      !_enable_security, _console_environment)) {
+    // Oops, failure to initialize.
+    cerr << "Failed to initialize plugin (wrong API version?)\n";
+    return 1;
+  }
+  
+  // Invoke the Core API and run the program
+  P3D_instance *inst = create_instance
+    (ExecutionEnvironment::get_binary_name(), true, 
+     _win_x, _win_y, _win_width, _win_height,
+     argv, argc, read_offset);
+  _instances.insert(inst);
+  
+  run_main_loop();
+  P3D_finalize();
+  return 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Panda3DBase::run_main_loop
+//       Access: Public
+//  Description: Gets lost in the application main loop, waiting for
+//               system events and notifications from the open
+//               instance(s).
+////////////////////////////////////////////////////////////////////
+void Panda3DBase::
+run_main_loop() {
+#ifdef _WIN32
+  if (_window_type == P3D_WT_embedded) {
+    // Wait for new messages from Windows, and new requests from the
+    // plugin.
+    MSG msg;
+    int retval;
+    retval = GetMessage(&msg, NULL, 0, 0);
+    while (retval != 0 && !time_to_exit()) {
+      if (retval == -1) {
+        cerr << "Error processing message queue.\n";
+        return;
+      }
+      TranslateMessage(&msg);
+      DispatchMessage(&msg);
+
+      // Check for new requests from the Panda3D plugin.
+      P3D_instance *inst = P3D_check_request(wait_cycle);
+      while (inst != (P3D_instance *)NULL) {
+        P3D_request *request = P3D_instance_get_request(inst);
+        if (request != (P3D_request *)NULL) {
+          handle_request(request);
+        }
+        inst = P3D_check_request(wait_cycle);
+      }
+
+      while (!_url_getters.empty() && 
+             !PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) {
+        // If there are no Windows messages, check the download tasks.
+        run_getters();
+      }
+      retval = GetMessage(&msg, NULL, 0, 0);
+    }
+    
+    // WM_QUIT has been received.  Terminate all instances, and fall
+    // through.
+    while (!_instances.empty()) {
+      P3D_instance *inst = *(_instances.begin());
+      delete_instance(inst);
+    }
+
+  } else {
+    // Not an embedded window, so we don't have our own window to
+    // generate Windows events.  Instead, just wait for requests.
+    while (!time_to_exit()) {
+      P3D_instance *inst = P3D_check_request(wait_cycle);
+      if (inst != (P3D_instance *)NULL) {
+        P3D_request *request = P3D_instance_get_request(inst);
+        if (request != (P3D_request *)NULL) {
+          handle_request(request);
+        }
+      }
+      run_getters();
+    }
+  }
+
+#elif defined(__APPLE__)
+  // OSX really prefers to own the main loop, so we install a timer to
+  // call out to our instances and getters, rather than polling within
+  // the event loop as we do in the Windows case, above.
+  EventLoopRef main_loop = GetMainEventLoop();
+  EventLoopTimerUPP timer_upp = NewEventLoopTimerUPP(st_timer_callback);
+  EventLoopTimerRef timer;
+  EventTimerInterval interval = wait_cycle * kEventDurationSecond;
+  InstallEventLoopTimer(main_loop, interval, interval,
+                        timer_upp, this, &timer);
+  RunApplicationEventLoop();
+  RemoveEventLoopTimer(timer);
+  
+  // Terminate all instances, and fall through.
+  while (!_instances.empty()) {
+    P3D_instance *inst = *(_instances.begin());
+    delete_instance(inst);
+  }
+    
+#else  // _WIN32, __APPLE__
+
+  // Now wait while we process pending requests.
+  while (!time_to_exit()) {
+    P3D_instance *inst = P3D_check_request(wait_cycle);
+    if (inst != (P3D_instance *)NULL) {
+      P3D_request *request = P3D_instance_get_request(inst);
+      if (request != (P3D_request *)NULL) {
+        handle_request(request);
+      }
+    }
+    run_getters();
+  }
+
+#endif  // _WIN32, __APPLE__
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Panda3DBase::run_getters
+//       Access: Protected
+//  Description: Polls all of the active URL requests.
+////////////////////////////////////////////////////////////////////
+void Panda3DBase::
+run_getters() {
+  URLGetters::iterator gi;
+  gi = _url_getters.begin();
+  while (gi != _url_getters.end()) {
+    URLGetter *getter = (*gi);
+    if (getter->run()) {
+      // This URLGetter is still working.  Leave it.
+      ++gi;
+    } else {
+      // This URLGetter is done.  Remove it and delete it.
+      URLGetters::iterator dgi = gi;
+      ++gi;
+      _url_getters.erase(dgi);
+      delete getter;
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Panda3DBase::handle_request
+//       Access: Protected
+//  Description: Handles a single request received via the plugin API
+//               from a p3d instance.
+////////////////////////////////////////////////////////////////////
+void Panda3DBase::
+handle_request(P3D_request *request) {
+  bool handled = false;
+  switch (request->_request_type) {
+  case P3D_RT_stop:
+    delete_instance(request->_instance);
+#ifdef _WIN32
+    // Post a silly message to spin the event loop.
+    PostMessage(NULL, WM_USER, 0, 0);
+#endif
+    handled = true;
+    break;
+
+  case P3D_RT_get_url:
+    {
+      int unique_id = request->_request._get_url._unique_id;
+      const string &url = request->_request._get_url._url;
+      URLGetter *getter = new URLGetter
+        (request->_instance, unique_id, URLSpec(url), "");
+      _url_getters.insert(getter);
+      handled = true;
+    }
+    break;
+
+  case P3D_RT_notify:
+    {
+      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:
+    break;
+  };
+
+  P3D_request_finish(request, handled);
+}
+
+#ifdef _WIN32
+LONG WINAPI
+window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
+  switch (msg) {
+  case WM_DESTROY:
+    PostQuitMessage(0);
+    break;
+  };
+
+  return DefWindowProc(hwnd, msg, wparam, lparam);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Panda3DBase::make_parent_window
+//       Access: Protected
+//  Description: Creates a toplevel window to contain the embedded
+//               instances.  Windows implementation.
+////////////////////////////////////////////////////////////////////
+void Panda3DBase::
+make_parent_window() {
+  WNDCLASS wc;
+
+  HINSTANCE application = GetModuleHandle(NULL);
+  ZeroMemory(&wc, sizeof(WNDCLASS));
+  wc.lpfnWndProc = window_proc;
+  wc.hInstance = application;
+  wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
+  wc.lpszClassName = "panda3d";
+
+  if (!RegisterClass(&wc)) {
+    cerr << "Could not register window class!\n";
+    exit(1);
+  }
+
+  DWORD window_style = 
+    WS_POPUP | WS_CLIPCHILDREN | WS_CLIPSIBLINGS |
+    WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX |
+    WS_SIZEBOX | WS_MAXIMIZEBOX;
+
+  HWND toplevel_window = 
+    CreateWindow("panda3d", "Panda3D", window_style,
+                 CW_USEDEFAULT, CW_USEDEFAULT, _win_width, _win_height,
+                 NULL, NULL, application, 0);
+  if (!toplevel_window) {
+    cerr << "Could not create toplevel window!\n";
+    exit(1);
+  }
+
+  ShowWindow(toplevel_window, SW_SHOWNORMAL);
+
+  memset(&_parent_window, 0, sizeof(_parent_window));
+  _parent_window._window_handle_type = P3D_WHT_win_hwnd;
+  _parent_window._handle._win_hwnd._hwnd = toplevel_window;
+}
+
+#else
+
+////////////////////////////////////////////////////////////////////
+//     Function: Panda3DBase::make_parent_window
+//       Access: Protected
+//  Description: Creates a toplevel window to contain the embedded
+//               instances.
+////////////////////////////////////////////////////////////////////
+void Panda3DBase::
+make_parent_window() {
+  // TODO.
+  assert(false);
+}
+
+#endif
+
+////////////////////////////////////////////////////////////////////
+//     Function: Panda3DBase::create_instance
+//       Access: Protected
+//  Description: Uses the plugin API to create a new P3D instance to
+//               play a particular .p3d file.  This instance is also
+//               started if start_instance is true (which requires
+//               that the named p3d file exists).
+////////////////////////////////////////////////////////////////////
+P3D_instance *Panda3DBase::
+create_instance(const string &p3d, bool start_instance,
+                int win_x, int win_y, int win_width, int win_height,
+                char **args, int num_args, const 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);
+  string os_p3d_filename = p3d;
+  bool is_local = !is_url(p3d);
+  if (is_local && start_instance) {
+    if (!p3d_filename.exists()) {
+      cerr << "No such file: " << p3d_filename << "\n";
+      exit(1);
+    }
+
+    p3d_filename.make_absolute();
+    os_p3d_filename = p3d_filename.to_os_specific();
+  } 
+
+  // Build up the token list.
+  Tokens tokens = _tokens;
+  P3D_token token;
+
+  string log_basename;
+  if (!_log_dirname.empty()) {
+    // Generate output to a logfile.
+    log_basename = p3d_filename.get_basename_wo_extension();
+    token._keyword = "log_basename";
+    token._value = log_basename.c_str();
+    tokens.push_back(token);
+  } else {
+    // Send output to the console.
+    token._keyword = "console_output";
+    if (_console_environment) {
+      token._value = "1";
+    } else {
+      token._value = "0";
+    }
+    tokens.push_back(token);
+  }
+
+  token._keyword = "auto_start";
+  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());
+  for (int i = 0; i < num_args; ++i) {
+    argv.push_back(args[i]);
+  }
+
+  P3D_instance *inst = P3D_new_instance(NULL, tokens_p, num_tokens,
+                                        argv.size(), &argv[0], NULL);
+
+  if (inst != NULL) {
+    if (start_instance) {
+      // 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_offset);
+    }
+
+    P3D_instance_setup_window
+      (inst, _window_type, _win_x, _win_y, _win_width, _win_height, &_parent_window);
+  }
+
+  return inst;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Panda3DBase::delete_instance
+//       Access: Protected
+//  Description: Deletes the indicated instance and removes it from
+//               the internal structures.
+////////////////////////////////////////////////////////////////////
+void Panda3DBase::
+delete_instance(P3D_instance *inst) {
+  P3D_instance_finish(inst);
+  _instances.erase(inst);
+
+  // Make sure we also terminate any pending URLGetters associated
+  // with this instance.
+  URLGetters::iterator gi;
+  gi = _url_getters.begin();
+  while (gi != _url_getters.end()) {
+    URLGetter *getter = (*gi);
+    if (getter->get_instance() == inst) {
+      URLGetters::iterator dgi = gi;
+      ++gi;
+      _url_getters.erase(dgi);
+      delete getter;
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Panda3DBase::parse_token
+//       Access: Protected
+//  Description: Parses a web token of the form token=value, and
+//               stores it in _tokens.  Returns true on success, false
+//               on failure.
+////////////////////////////////////////////////////////////////////
+bool Panda3DBase::
+parse_token(char *arg) {
+  char *equals = strchr(arg, '=');
+  if (equals == NULL) {
+    return false;
+  }
+
+  // Directly munge the C string to truncate it at the equals sign.
+  // Classic C tricks.
+  *equals = '\0';
+  P3D_token token;
+  token._keyword = strdup(arg);
+  token._value = strdup(equals + 1);
+  *equals = '=';
+
+  _tokens.push_back(token);
+
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Panda3DBase::parse_int_pair
+//       Access: Protected
+//  Description: Parses a string into an x,y pair of integers.
+//               Returns true on success, false on failure.
+////////////////////////////////////////////////////////////////////
+bool Panda3DBase::
+parse_int_pair(char *arg, int &x, int &y) {
+  char *endptr;
+  x = strtol(arg, &endptr, 10);
+  if (*endptr == ',') {
+    y = strtol(endptr + 1, &endptr, 10);
+    if (*endptr == '\0') {
+      return true;
+    }
+  }
+
+  // Some parse error on the string.
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Panda3DBase::is_url
+//       Access: Protected, Static
+//  Description: Returns true if the indicated string appears to be a
+//               URL, with a leading http:// or file:// or whatever,
+//               or false if it must be a local filename instead.
+////////////////////////////////////////////////////////////////////
+bool Panda3DBase::
+is_url(const string &param) {
+  // We define a URL prefix as a sequence of at least two letters,
+  // followed by a colon, followed by at least one slash.
+  size_t p = 0;
+  while (p < param.size() && isalpha(param[p])) {
+    ++p;
+  }
+  if (p < 2) {
+    // Not enough letters.
+    return false;
+  }
+  if (p >= param.size() || param[p] != ':') {
+    // No colon.
+    return false;
+  }
+  ++p;
+  if (p >= param.size() || param[p] != '/') {
+    // No slash.
+    return false;
+  }
+
+  // It matches the rules.
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Panda3DBase::report_downloading_package
+//       Access: Protected
+//  Description: Tells the user we have to download a package.
+////////////////////////////////////////////////////////////////////
+void Panda3DBase::
+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 << "Installing package.\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 << "Installing " << name << "\n";
+
+  delete[] name;
+  P3D_object_decref(display_name);
+  _reporting_download = true;
+}
+ 
+////////////////////////////////////////////////////////////////////
+//     Function: Panda3DBase::report_download_complete
+//       Access: Protected
+//  Description: Tells the user we're done downloading packages
+////////////////////////////////////////////////////////////////////
+void Panda3DBase::
+report_download_complete(P3D_instance *instance) {
+  if (_reporting_download) {
+    cerr << "Install complete.\n";
+  }
+}
+
+#ifdef __APPLE__
+////////////////////////////////////////////////////////////////////
+//     Function: Panda3DBase::st_timer_callback
+//       Access: Protected, Static
+//  Description: Installed as a timer on the event loop, so we can
+//               process local events, in the Apple implementation.
+////////////////////////////////////////////////////////////////////
+pascal void Panda3DBase::
+st_timer_callback(EventLoopTimerRef timer, void *user_data) {
+  ((Panda3D *)user_data)->timer_callback(timer);
+}
+#endif  // __APPLE__
+
+#ifdef __APPLE__
+////////////////////////////////////////////////////////////////////
+//     Function: Panda3DBase::timer_callback
+//       Access: Protected
+//  Description: Installed as a timer on the event loop, so we can
+//               process local events, in the Apple implementation.
+////////////////////////////////////////////////////////////////////
+void Panda3DBase::
+timer_callback(EventLoopTimerRef timer) {
+  // Check for new requests from the Panda3D plugin.
+  P3D_instance *inst = P3D_check_request(0.0);
+  while (inst != (P3D_instance *)NULL) {
+    P3D_request *request = P3D_instance_get_request(inst);
+    if (request != (P3D_request *)NULL) {
+      handle_request(request);
+    }
+    inst = P3D_check_request(0.0);
+  }
+  
+  // Check the download tasks.
+  run_getters();
+
+  // If we're out of instances, exit the application.
+  if (time_to_exit()) {
+    QuitApplicationEventLoop();
+  }
+}
+#endif  // __APPLE__
+
+////////////////////////////////////////////////////////////////////
+//     Function: Panda3DBase::URLGetter::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+Panda3DBase::URLGetter::
+URLGetter(P3D_instance *instance, int unique_id,
+          const URLSpec &url, const string &post_data) :
+  _instance(instance),
+  _unique_id(unique_id),
+  _url(url),
+  _post_data(post_data)
+{
+  HTTPClient *http = HTTPClient::get_global_ptr();
+
+  _channel = http->make_channel(false);
+  //  _channel->set_download_throttle(true);
+  if (_post_data.empty()) {
+    _channel->begin_get_document(_url);
+  } else {
+    _channel->begin_post_form(_url, _post_data);
+  }
+
+  _channel->download_to_ram(&_rf);
+  _bytes_sent = 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Panda3DBase::URLGetter::run
+//       Access: Public
+//  Description: Polls the URLGetter for new results.  Returns true if
+//               the URL request is still in progress and run() should
+//               be called again later, or false if the URL request
+//               has been completed and run() should not be called
+//               again.
+////////////////////////////////////////////////////////////////////
+bool Panda3DBase::URLGetter::
+run() {
+  if (_channel->run() || _rf.get_data_size() != 0) {
+    if (_rf.get_data_size() != 0) {
+      // Got some new data.
+      bool download_ok = P3D_instance_feed_url_stream
+        (_instance, _unique_id, P3D_RC_in_progress,
+         _channel->get_status_code(),
+         _channel->get_file_size(),
+         (const unsigned char *)_rf.get_data().data(), _rf.get_data_size());
+      _bytes_sent += _rf.get_data_size();
+      _rf.clear();
+
+      if (!download_ok) {
+        // The plugin doesn't care any more.  Interrupt the download.
+        cerr << "Download interrupted: " << _url 
+             << ", after " << _bytes_sent << " of " << _channel->get_file_size()
+             << " bytes.\n";
+        return false;
+      }
+    }
+
+    // Still more to come; call this method again later.
+    return true;
+  }
+
+  // All done.
+  P3D_result_code status = P3D_RC_done;
+  if (!_channel->is_valid()) {
+    if (_channel->get_status_code() != 0) {
+      status = P3D_RC_http_error;
+    } else {
+      status = P3D_RC_generic_error;
+    }
+    cerr << "Error getting URL " << _url << "\n";
+  }
+
+  P3D_instance_feed_url_stream
+    (_instance, _unique_id, status,
+     _channel->get_status_code(),
+     _bytes_sent, NULL, 0);
+  return false;
+}

+ 121 - 0
direct/src/plugin_standalone/panda3dBase.h

@@ -0,0 +1,121 @@
+// Filename: panda3dBase.h
+// Created by:  pro-rsoft (07Dec09)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 PANDA3DBASE_H
+#define PANDA3DBASE_H
+
+// This program must link with Panda for HTTPClient support.  This
+// means it probably should be built with LINK_ALL_STATIC defined, so
+// we won't have to deal with confusing .dll or .so files that might
+// compete on the disk with the dynamically-loaded versions.  There's
+// no competition in memory address space, though, because
+// p3d_plugin--the only file we dynamically link in--doesn't itself
+// link with Panda.
+
+#include "pandabase.h"
+#include "p3d_plugin.h"
+#include "httpChannel.h"
+#include "ramfile.h"
+#include "fileSpec.h"
+#include "pset.h"
+#include "vector_string.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : Panda3DBase
+// Description : Base for creating a standalone application that
+//               invokes the panda3d plugin to launch .p3d files.
+////////////////////////////////////////////////////////////////////
+class Panda3DBase {
+public:
+  Panda3DBase(bool console_environment);
+
+  int run_embedded(int read_offset, int argc, char *argv[]);
+  void run_main_loop();
+
+protected:
+  void run_getters();
+  void handle_request(P3D_request *request);
+  void make_parent_window();
+
+  P3D_instance *
+  create_instance(const string &p3d, bool start_instance,
+                  int win_x, int win_y, int win_width, int win_height,
+                  char **args, int num_args, const int &p3d_offset = 0);
+  void delete_instance(P3D_instance *instance);
+
+  bool parse_token(char *arg);
+  bool parse_int_pair(char *arg, int &x, int &y);
+  static bool is_url(const string &param);
+
+  void report_downloading_package(P3D_instance *instance);
+  void report_download_complete(P3D_instance *instance);
+
+  inline bool time_to_exit();
+
+#ifdef __APPLE__
+  static pascal void st_timer_callback(EventLoopTimerRef timer, void *user_data);
+  void timer_callback(EventLoopTimerRef timer);
+#endif
+
+protected:
+  string _host_url;
+  string _root_dir;
+  string _log_dirname;
+  string _log_basename;
+  string _this_platform;
+  bool _verify_contents;
+  P3D_window_type _window_type;
+  P3D_window_handle _parent_window;
+  int _win_x, _win_y;
+  int _win_width, _win_height;
+  bool _exit_with_last_instance;
+
+  bool _reporting_download;
+  bool _enable_security;
+  bool _console_environment;
+
+  typedef pset<P3D_instance *> Instances;
+  Instances _instances;
+
+  typedef pvector<P3D_token> Tokens;
+  Tokens _tokens;
+
+  // This nested class keeps track of active URL requests.
+  class URLGetter {
+  public:
+    URLGetter(P3D_instance *instance, int unique_id,
+              const URLSpec &url, const string &post_data);
+    
+    bool run();
+    inline P3D_instance *get_instance();
+    
+  private:
+    P3D_instance *_instance;
+    int _unique_id;
+    URLSpec _url;
+    string _post_data;
+    
+    PT(HTTPChannel) _channel;
+    Ramfile _rf;
+    size_t _bytes_sent;
+  };
+  
+  typedef pset<URLGetter *> URLGetters;
+  URLGetters _url_getters;
+};
+
+#include "panda3dBase.I"
+
+#endif
+