Browse Source

beginning Mozilla plugin integration

David Rose 16 years ago
parent
commit
ca983e8ece
41 changed files with 2126 additions and 269 deletions
  1. 0 21
      direct/src/plugin/Sources.pp
  2. 8 12
      direct/src/plugin/handleStreamBuf.cxx
  3. 145 0
      direct/src/plugin/load_plugin_src.cxx
  4. 31 0
      direct/src/plugin/load_plugin_src.h
  5. 38 0
      direct/src/plugin/p3dCInstance.cxx
  6. 3 0
      direct/src/plugin/p3dCInstance.h
  7. 2 2
      direct/src/plugin/p3dDownload.cxx
  8. 1 1
      direct/src/plugin/p3dFileDownload.cxx
  9. 17 6
      direct/src/plugin/p3dInstance.cxx
  10. 1 0
      direct/src/plugin/p3dInstance.h
  11. 4 4
      direct/src/plugin/p3dInstanceManager.cxx
  12. 10 10
      direct/src/plugin/p3dMultifileReader.cxx
  13. 29 29
      direct/src/plugin/p3dPackage.cxx
  14. 3 3
      direct/src/plugin/p3dProgressWindow.cxx
  15. 23 18
      direct/src/plugin/p3dPythonRun.cxx
  16. 3 3
      direct/src/plugin/p3dSession.I
  17. 20 20
      direct/src/plugin/p3dSession.cxx
  18. 3 3
      direct/src/plugin/p3dSession.h
  19. 4 4
      direct/src/plugin/p3dWinProgressWindow.cxx
  20. 22 1
      direct/src/plugin/p3d_plugin.cxx
  21. 23 11
      direct/src/plugin/p3d_plugin.h
  22. 6 1
      direct/src/plugin/p3d_plugin_common.h
  23. 30 0
      direct/src/plugin_npapi/Sources.pp
  24. 320 0
      direct/src/plugin_npapi/np_entry.cpp
  25. 215 0
      direct/src/plugin_npapi/npn_gate.cpp
  26. 358 0
      direct/src/plugin_npapi/npp_gate.cpp
  27. 9 0
      direct/src/plugin_npapi/nppanda3d.def
  28. 56 0
      direct/src/plugin_npapi/nppanda3d.rc
  29. 52 0
      direct/src/plugin_npapi/nppanda3d_common.h
  30. 4 0
      direct/src/plugin_npapi/nppanda3d_composite1.cxx
  31. 65 0
      direct/src/plugin_npapi/nppanda3d_startup.cxx
  32. 22 0
      direct/src/plugin_npapi/nppanda3d_startup.h
  33. 150 0
      direct/src/plugin_npapi/npplat.h
  34. 96 0
      direct/src/plugin_npapi/pluginbase.h
  35. 14 0
      direct/src/plugin_npapi/ppInstance.I
  36. 136 0
      direct/src/plugin_npapi/ppInstance.cxx
  37. 68 0
      direct/src/plugin_npapi/ppInstance.h
  38. 24 0
      direct/src/plugin_standalone/Sources.pp
  39. 42 112
      direct/src/plugin_standalone/panda3d.cxx
  40. 57 8
      direct/src/showbase/RunAppMF.py
  41. 12 0
      direct/src/showbase/ShowBase.py

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

@@ -64,24 +64,3 @@
     p3dPythonRun.cxx p3dPythonRun.h p3dPythonRun.I
 
 #end bin_target
-
-#begin bin_target
-  #define USE_PACKAGES openssl zlib
-  #define TARGET panda3d
-
-  #define OTHER_LIBS \
-    prc:c dtoolutil:c dtoolbase:c dtool:m \
-    interrogatedb:c dconfig:c dtoolconfig:m \
-    express:c downloader:c pandaexpress:m \
-    pstatclient:c pandabase:c linmath:c putil:c \
-    pipeline:c panda:m \
-    pystub
-
-  #define OSX_SYS_FRAMEWORKS Foundation AppKit
-
-  #define SOURCES \
-    panda3d.cxx
-
-  #define WIN_SYS_LIBS user32.lib gdi32.lib shell32.lib
-
-#end bin_target

+ 8 - 12
direct/src/plugin/handleStreamBuf.cxx

@@ -260,10 +260,9 @@ read_chars(char *start, size_t length) {
   if (!success) {
     DWORD error = GetLastError();
     if (error != ERROR_HANDLE_EOF && error != ERROR_BROKEN_PIPE) {
-      cerr
-        << "Error reading " << length
-        << " bytes, windows error code 0x" << hex
-        << error << dec << ".\n";
+      cerr << "Error reading " << length
+           << " bytes, windows error code 0x" << hex
+           << error << dec << ".\n";
       return 0;
     }
   }
@@ -274,8 +273,7 @@ read_chars(char *start, size_t length) {
   // Posix case.
   ssize_t result = ::read(_handle, start, length);
   if (result < 0) {
-    cerr
-      << "Error reading " << length << " bytes\n";
+    cerr << "Error reading " << length << " bytes\n";
     return 0;
   }
 
@@ -313,10 +311,9 @@ write_chars(const char *start, size_t length) {
   if (!success) {
     DWORD error = GetLastError();
     if (error != ERROR_NO_DATA && error != ERROR_BROKEN_PIPE) {
-      cerr
-        << "Error writing " << length
-        << " bytes, windows error code 0x" << hex
-        << error << dec << ".\n";
+      cerr << "Error writing " << length
+           << " bytes, windows error code 0x" << hex
+           << error << dec << ".\n";
     }
     return bytes_written;
   }
@@ -329,8 +326,7 @@ write_chars(const char *start, size_t length) {
     ssize_t result = ::write(_handle, start, remaining);
     if (result < 0) {
       if (errno != EPIPE) {
-        cerr
-          << "Error writing " << remaining << " bytes\n";
+        cerr << "Error writing " << remaining << " bytes\n";
       }
       return length - remaining;
     }

+ 145 - 0
direct/src/plugin/load_plugin_src.cxx

@@ -0,0 +1,145 @@
+// Filename: load_plugin_src.cxx
+// Created by:  drose (19Jun09)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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."
+//
+////////////////////////////////////////////////////////////////////
+
+
+// This code is used in the plugin_standalone directory, and also in
+// the plugin_npapi directory.  To facilitate that code re-use with
+// minimal structural overhead, it is designed to be simply #included
+// into the different source files.
+
+#ifdef _WIN32
+static const string dll_ext = ".dll";
+#elif defined(__APPLE__)
+static const string dll_ext = ".dylib";
+#else
+static const string dll_ext = ".so";
+#endif
+
+static const string default_plugin_filename = "libp3d_plugin";
+
+P3D_initialize_func *P3D_initialize;
+P3D_free_string_func *P3D_free_string;
+P3D_create_instance_func *P3D_create_instance;
+P3D_instance_finish_func *P3D_instance_finish;
+P3D_instance_has_property_func *P3D_instance_has_property;
+P3D_instance_get_property_func *P3D_instance_get_property;
+P3D_instance_set_property_func *P3D_instance_set_property;
+P3D_instance_get_request_func *P3D_instance_get_request;
+P3D_check_request_func *P3D_check_request;
+P3D_request_finish_func *P3D_request_finish;
+P3D_instance_feed_url_stream_func *P3D_instance_feed_url_stream;
+
+#ifdef _WIN32
+static HMODULE module;
+#endif
+
+
+
+static void
+unload_plugin() {
+#ifdef _WIN32
+  FreeLibrary(module);
+  module = NULL;
+#else 
+  // TODO: unload_dso
+#endif
+  
+  P3D_initialize = NULL;
+  P3D_free_string = NULL;
+  P3D_create_instance = NULL;
+  P3D_instance_finish = NULL;
+  P3D_instance_has_property = NULL;
+  P3D_instance_get_property = NULL;
+  P3D_instance_set_property = NULL;
+  P3D_instance_get_request = NULL;
+  P3D_check_request = NULL;
+  P3D_request_finish = NULL;
+  P3D_instance_feed_url_stream = NULL;
+}
+
+static bool
+load_plugin(const string &p3d_plugin_filename) {
+  string filename = p3d_plugin_filename;
+  if (filename.empty()) {
+    // Look for the plugin along the path.
+    filename = default_plugin_filename + dll_ext;
+  }
+
+#ifdef _WIN32
+  module = LoadLibrary(filename.c_str());
+  if (module == NULL) {
+    // Couldn't load the DLL.
+    return false;
+  }
+
+  // Now get all of the function pointers.
+  P3D_initialize = (P3D_initialize_func *)GetProcAddress(module, "P3D_initialize");  
+  P3D_free_string = (P3D_free_string_func *)GetProcAddress(module, "P3D_free_string");  
+  P3D_create_instance = (P3D_create_instance_func *)GetProcAddress(module, "P3D_create_instance");  
+  P3D_instance_finish = (P3D_instance_finish_func *)GetProcAddress(module, "P3D_instance_finish");  
+  P3D_instance_has_property = (P3D_instance_has_property_func *)GetProcAddress(module, "P3D_instance_has_property");  
+  P3D_instance_get_property = (P3D_instance_get_property_func *)GetProcAddress(module, "P3D_instance_get_property");  
+  P3D_instance_set_property = (P3D_instance_set_property_func *)GetProcAddress(module, "P3D_instance_set_property");  
+  P3D_instance_get_request = (P3D_instance_get_request_func *)GetProcAddress(module, "P3D_instance_get_request");  
+  P3D_check_request = (P3D_check_request_func *)GetProcAddress(module, "P3D_check_request");  
+  P3D_request_finish = (P3D_request_finish_func *)GetProcAddress(module, "P3D_request_finish");  
+  P3D_instance_feed_url_stream = (P3D_instance_feed_url_stream_func *)GetProcAddress(module, "P3D_instance_feed_url_stream");  
+
+#else  // _WIN32
+  // Posix case.
+  void *module = dlopen(filename.c_str(), RTLD_NOW | RTLD_LOCAL);
+  if (module == NULL) {
+    // Couldn't load the .so.
+    return false;
+  }
+  
+  // Now get all of the function pointers.
+  P3D_initialize = (P3D_initialize_func *)dlsym(module, "P3D_initialize");  
+  P3D_free_string = (P3D_free_string_func *)dlsym(module, "P3D_free_string");  
+  P3D_create_instance = (P3D_create_instance_func *)dlsym(module, "P3D_create_instance");  
+  P3D_instance_finish = (P3D_instance_finish_func *)dlsym(module, "P3D_instance_finish");  
+  P3D_instance_has_property = (P3D_instance_has_property_func *)dlsym(module, "P3D_instance_has_property");  
+  P3D_instance_get_property = (P3D_instance_get_property_func *)dlsym(module, "P3D_instance_get_property");  
+  P3D_instance_set_property = (P3D_instance_set_property_func *)dlsym(module, "P3D_instance_set_property");  
+  P3D_instance_get_request = (P3D_instance_get_request_func *)dlsym(module, "P3D_instance_get_request");  
+  P3D_check_request = (P3D_check_request_func *)dlsym(module, "P3D_check_request");  
+  P3D_request_finish = (P3D_request_finish_func *)dlsym(module, "P3D_request_finish");  
+  P3D_instance_feed_url_stream = (P3D_instance_feed_url_stream_func *)dlsym(module, "P3D_instance_feed_url_stream");  
+
+#endif  // _WIN32
+
+  // Ensure that all of the function pointers have been found.
+  if (P3D_initialize == NULL ||
+      P3D_free_string == NULL ||
+      P3D_create_instance == NULL ||
+      P3D_instance_finish == NULL ||
+      P3D_instance_has_property == NULL ||
+      P3D_instance_get_property == NULL ||
+      P3D_instance_set_property == NULL ||
+      P3D_instance_get_request == NULL ||
+      P3D_check_request == NULL ||
+      P3D_request_finish == NULL ||
+      P3D_instance_feed_url_stream == NULL) {
+    return false;
+  }
+
+  // Successfully loaded.
+  if (!P3D_initialize(P3D_API_VERSION, "c:/cygwin/home/drose/t0.log")) {
+    // Oops, failure to initialize.
+    unload_plugin();
+    return false;
+  }
+
+  return true;
+}

+ 31 - 0
direct/src/plugin/load_plugin_src.h

@@ -0,0 +1,31 @@
+// Filename: load_plugin_src.h
+// Created by:  drose (19Jun09)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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."
+//
+////////////////////////////////////////////////////////////////////
+
+
+// This code is used in the plugin_standalone directory, and also in
+// the plugin_npapi directory.  To facilitate that code re-use with
+// minimal structural overhead, it is designed to be simply #included
+// into the different source files.
+
+extern P3D_initialize_func *P3D_initialize;
+extern P3D_free_string_func *P3D_free_string;
+extern P3D_create_instance_func *P3D_create_instance;
+extern P3D_instance_finish_func *P3D_instance_finish;
+extern P3D_instance_has_property_func *P3D_instance_has_property;
+extern P3D_instance_get_property_func *P3D_instance_get_property;
+extern P3D_instance_set_property_func *P3D_instance_set_property;
+extern P3D_instance_get_request_func *P3D_instance_get_request;
+extern P3D_check_request_func *P3D_check_request;
+extern P3D_request_finish_func *P3D_request_finish;
+extern P3D_instance_feed_url_stream_func *P3D_instance_feed_url_stream;

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

@@ -58,6 +58,23 @@ P3DCInstance(TiXmlElement *xinstance) :
     _parent_window._hwnd = (HWND)hwnd;
   }
 #endif
+
+  TiXmlElement *xtoken = xinstance->FirstChildElement("token");
+  while (xtoken != NULL) {
+    Token token;
+    const char *keyword = xtoken->Attribute("keyword");
+    if (keyword != NULL) {
+      token._keyword = keyword;
+    }
+
+    const char *value = xtoken->Attribute("value");
+    if (value != NULL) {
+      token._value = value;
+    }
+
+    _tokens.push_back(token);
+    xtoken = xtoken->NextSiblingElement("token");
+  }
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -68,3 +85,24 @@ P3DCInstance(TiXmlElement *xinstance) :
 P3DCInstance::
 ~P3DCInstance() {
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: P3DCInstance::get_py_tokens
+//       Access: Public
+//  Description: Returns a Python list object that corresponds to the
+//               tokens passed to this instance, expressed as a list
+//               of 2-tuples.  New instance.
+////////////////////////////////////////////////////////////////////
+PyObject *P3DCInstance::
+get_py_tokens() const {
+  PyObject *list = PyList_New(_tokens.size());
+
+  for (size_t i = 0; i < _tokens.size(); ++i) {
+    const Token &token = _tokens[i];
+    PyObject *tuple = Py_BuildValue("(ss)", token._keyword.c_str(), 
+                                    token._value.c_str());
+    PyList_SetItem(list, i, tuple);
+  }
+
+  return list;
+}

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

@@ -20,6 +20,7 @@
 #include "p3d_plugin.h"
 #include "pvector.h"
 
+#include <Python.h>
 #include <tinyxml.h>
 
 class P3DSession;
@@ -37,6 +38,8 @@ public:
   inline const string &get_p3d_filename() const;
   inline int get_instance_id() const;
 
+  PyObject *get_py_tokens() const;
+
 private:
   class Token {
   public:

+ 2 - 2
direct/src/plugin/p3dDownload.cxx

@@ -129,7 +129,7 @@ void P3DDownload::
 download_progress() {
   time_t now = time(NULL);
   if (now != _last_reported_time || true) {
-    cerr << "Downloading " << get_url() << ": " 
+    nout << "Downloading " << get_url() << ": " 
          << int(get_download_progress() * 1000.0) / 10.0 << "\n";
     _last_reported_time = now;
   }
@@ -145,6 +145,6 @@ download_progress() {
 ////////////////////////////////////////////////////////////////////
 void P3DDownload::
 download_finished(bool success) {
-  cerr << "Downloading " << get_url() << ": " 
+  nout << "Downloading " << get_url() << ": " 
        << int(get_download_progress() * 1000.0) / 10.0 << "\n";
 }

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

@@ -47,7 +47,7 @@ bool P3DFileDownload::
 open_file() {
   P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
   if (!inst_mgr->mkfile_public(_filename)) {
-    cerr << "Unable to create " << _filename << "\n";
+    nout << "Unable to create " << _filename << "\n";
     return false;
   }
   

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

@@ -41,7 +41,7 @@ P3DInstance(P3D_request_ready_func *func,
   _parent_window(parent_window)
 {
   fill_tokens(tokens, num_tokens);
-  cerr << "instance, size = " << _win_width << " " << _win_height << "\n";
+  nout << "instance, size = " << _win_width << " " << _win_height << "\n";
 
   _instance_id = _next_instance_id;
   ++_next_instance_id;
@@ -72,6 +72,9 @@ P3DInstance::
   DESTROY_LOCK(_request_lock);
 
   // TODO: empty _pending_requests queue and _downloads map.
+
+  // TODO: Is it possible for someone to delete an instance while a
+  // download is still running?  Who will crash when this happens?
 }
 
 
@@ -158,7 +161,7 @@ get_request() {
 ////////////////////////////////////////////////////////////////////
 void P3DInstance::
 add_request(P3D_request *request) {
-  cerr << "adding a request\n";
+  nout << "adding a request\n";
   assert(request->_instance == this);
 
   ACQUIRE_LOCK(_request_lock);
@@ -201,7 +204,7 @@ feed_url_stream(int unique_id,
                 size_t this_data_size) {
   Downloads::iterator di = _downloads.find(unique_id);
   if (di == _downloads.end()) {
-    cerr << "Unexpected feed_url_stream for " << unique_id << "\n";
+    nout << "Unexpected feed_url_stream for " << unique_id << "\n";
     // Don't know this request.
     return false;
   }
@@ -213,7 +216,7 @@ feed_url_stream(int unique_id,
 
   if (!download_ok || download->get_download_finished()) {
     // All done.
-    cerr << "completed download " << unique_id << "\n";
+    nout << "completed download " << unique_id << "\n";
     _downloads.erase(di);
     delete download;
   }
@@ -261,7 +264,7 @@ start_download(P3DDownload *download) {
   bool inserted = _downloads.insert(Downloads::value_type(download_id, download)).second;
   assert(inserted);
 
-  cerr << "beginning download " << download_id << ": " << download->get_url()
+  nout << "beginning download " << download_id << ": " << download->get_url()
        << "\n";
 
   P3D_request *request = new P3D_request;
@@ -334,6 +337,15 @@ make_xml() {
     break;
   }
 
+  Tokens::const_iterator ti;
+  for (ti = _tokens.begin(); ti != _tokens.end(); ++ti) {
+    const Token &token = (*ti);
+    TiXmlElement *xtoken = new TiXmlElement("token");
+    xtoken->SetAttribute("keyword", token._keyword.c_str());
+    xtoken->SetAttribute("value", token._value.c_str());
+    xinstance->LinkEndChild(xtoken);
+  }
+
   return xinstance;
 }
 
@@ -356,4 +368,3 @@ fill_tokens(const P3D_token tokens[], size_t num_tokens) {
     _tokens.push_back(token);
   }
 }
-

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

@@ -16,6 +16,7 @@
 #define P3DINSTANCE_H
 
 #include "p3d_plugin_common.h"
+#include "p3dFileDownload.h"
 
 #include <vector>
 #include <deque>

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

@@ -33,7 +33,7 @@ P3DInstanceManager *P3DInstanceManager::_global_ptr;
 ////////////////////////////////////////////////////////////////////
 P3DInstanceManager::
 P3DInstanceManager() {
-  cerr << "creating instance manager\n";
+  nout << "creating instance manager\n";
   _is_initialized = false;
   _unique_session_index = 0;
 
@@ -91,7 +91,7 @@ P3DInstanceManager::
 bool P3DInstanceManager::
 initialize() {
   _root_dir = find_root_dir();
-  cerr << "_root_dir = " << _root_dir << "\n";
+  nout << "_root_dir = " << _root_dir << "\n";
 
 #ifdef _WIN32
   _download_url = "http://10.196.143.118/~drose/p3d/";
@@ -101,7 +101,7 @@ initialize() {
 #endif
 
   if (_root_dir.empty()) {
-    cerr << "Could not find root directory.\n";
+    nout << "Could not find root directory.\n";
     return false;
   }
 
@@ -487,7 +487,7 @@ find_root_dir() const {
   }
 
   // Couldn't find a directory.  Bail.
-  cerr << "Couldn't find a root directory.\n";
+  nout << "Couldn't find a root directory.\n";
   return string();
 
 #else  // _WIN32

+ 10 - 10
direct/src/plugin/p3dMultifileReader.cxx

@@ -66,14 +66,14 @@ extract(const string &pathname, const string &to_dir,
 
   _in.open(pathname.c_str(), ios::in | ios::binary);
   if (!_in) {
-    cerr << "Couldn't open " << pathname << "\n";
+    nout << "Couldn't open " << pathname << "\n";
     return false;
   }
 
   for (size_t i = 0; i < _header_size; ++i) {
     int ch = _in.get();
     if (ch != _header[i]) {
-      cerr << "Failed header check: " << pathname << "\n";
+      nout << "Failed header check: " << pathname << "\n";
       return false;
     }
   }
@@ -81,13 +81,13 @@ extract(const string &pathname, const string &to_dir,
   unsigned int major = read_uint16();
   unsigned int minor = read_uint16();
   if (major != _current_major_ver || minor != _current_minor_ver) {
-    cerr << "Incompatible multifile version: " << pathname << "\n";
+    nout << "Incompatible multifile version: " << pathname << "\n";
     return false;
   }
 
   unsigned int scale = read_uint32();
   if (scale != 1) {
-    cerr << "Unsupported scale factor in " << pathname << "\n";
+    nout << "Unsupported scale factor in " << pathname << "\n";
     return false;
   }
 
@@ -95,7 +95,7 @@ extract(const string &pathname, const string &to_dir,
   read_uint32();
 
   if (!read_index()) {
-    cerr << "Error reading multifile index\n";
+    nout << "Error reading multifile index\n";
     return false;
   }
 
@@ -106,17 +106,17 @@ extract(const string &pathname, const string &to_dir,
   Subfiles::iterator si;
   for (si = _subfiles.begin(); si != _subfiles.end(); ++si) {
     const Subfile &s = (*si);
-    cerr << s._filename << "\n";
+    nout << s._filename << "\n";
 
     string output_pathname = to_dir + "/" + s._filename;
     if (!inst_mgr->mkfile_public(output_pathname)) {
-      cerr << "Unable to create " << output_pathname << "\n";
+      nout << "Unable to create " << output_pathname << "\n";
       return false;
     }
 
     ofstream out(output_pathname.c_str(), ios::out | ios::binary);
     if (!out) {
-      cerr << "Unable to write to " << output_pathname << "\n";
+      nout << "Unable to write to " << output_pathname << "\n";
       return false;
     }
 
@@ -136,7 +136,7 @@ extract(const string &pathname, const string &to_dir,
     }
 
     if (remaining_data != 0) {
-      cerr << "Unable to extract " << s._filename << "\n";
+      nout << "Unable to extract " << s._filename << "\n";
       return false;
     }
 
@@ -177,7 +177,7 @@ read_index() {
     s._length = read_uint32();
     unsigned int flags = read_uint16();
     if (flags != 0) {
-      cerr << "Unsupported per-subfile options in multifile\n";
+      nout << "Unsupported per-subfile options in multifile\n";
       return false;
     }
     s._timestamp = read_uint32();

+ 29 - 29
direct/src/plugin/p3dPackage.cxx

@@ -143,7 +143,7 @@ cancel_callback(Callback *callback) {
   if (ci != _callbacks.end()) {
     _callbacks.erase(ci);
   } else {
-    cerr << "Canceling unknown callback on " << _package_fullname << "\n";
+    nout << "Canceling unknown callback on " << _package_fullname << "\n";
   }
 }
 
@@ -175,7 +175,7 @@ desc_file_download_finished(bool success) {
 
   TiXmlDocument doc(_desc_file_pathname.c_str());
   if (!doc.LoadFile()) {
-    cerr << "Couldn't read " << _desc_file_pathname << "\n";
+    nout << "Couldn't read " << _desc_file_pathname << "\n";
     report_done(false);
     return;
   }
@@ -190,7 +190,7 @@ desc_file_download_finished(bool success) {
 ////////////////////////////////////////////////////////////////////
 void P3DPackage::
 got_desc_file(TiXmlDocument *doc, bool freshly_downloaded) {
-  cerr << "got desc file\n";
+  nout << "got desc file\n";
 
   TiXmlElement *xpackage = doc->FirstChildElement("package");
   TiXmlElement *uncompressed_archive = NULL;
@@ -224,7 +224,7 @@ got_desc_file(TiXmlDocument *doc, bool freshly_downloaded) {
     component = component->NextSiblingElement("component");
   }
 
-  cerr << "got " << _components.size() << " components\n";
+  nout << "got " << _components.size() << " components\n";
 
   // Verify all of the components.
   bool all_components_ok = true;
@@ -304,7 +304,7 @@ compressed_archive_download_finished(bool success) {
     download_compressed_archive(false);
   }
 
-  cerr << _compressed_archive._filename
+  nout << _compressed_archive._filename
        << " failed hash check after download\n";
   report_done(false);
 }
@@ -316,28 +316,28 @@ compressed_archive_download_finished(bool success) {
 ////////////////////////////////////////////////////////////////////
 void P3DPackage::
 uncompress_archive() {
-  cerr << "uncompressing " << _compressed_archive._filename << "\n";
+  nout << "uncompressing " << _compressed_archive._filename << "\n";
 
   string source_pathname = _package_dir + "/" + _compressed_archive._filename;
   string target_pathname = _package_dir + "/" + _uncompressed_archive._filename;
 
   ifstream source(source_pathname.c_str(), ios::in | ios::binary);
   if (!source) {
-    cerr << "Couldn't open " << source_pathname << "\n";
+    nout << "Couldn't open " << source_pathname << "\n";
     report_done(false);
     return;
   }
 
   P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
   if (!inst_mgr->mkfile_public(target_pathname)) {
-    cerr << "Unable to create " << target_pathname << "\n";
+    nout << "Unable to create " << target_pathname << "\n";
     report_done(false);
     return;
   }
 
   ofstream target(target_pathname.c_str(), ios::out | ios::binary);
   if (!target) {
-    cerr << "Couldn't write to " << target_pathname << "\n";
+    nout << "Couldn't write to " << target_pathname << "\n";
     report_done(false);
     return;
   }
@@ -369,7 +369,7 @@ uncompress_archive() {
 
   int result = inflateInit(&z);
   if (result < 0) {
-    cerr << z.msg << "\n";
+    nout << z.msg << "\n";
     report_done(false);
     return;
   }
@@ -391,7 +391,7 @@ uncompress_archive() {
     if (z.avail_out < write_buffer_size) {
       target.write(write_buffer, write_buffer_size - z.avail_out);
       if (!target) {
-        cerr << "Couldn't write entire file to " << target_pathname << "\n";
+        nout << "Couldn't write entire file to " << target_pathname << "\n";
         report_done(false);
         return;
       }
@@ -414,7 +414,7 @@ uncompress_archive() {
       flush = Z_FINISH;
 
     } else if (result < 0) {
-      cerr << z.msg << "\n";
+      nout << z.msg << "\n";
       inflateEnd(&z);
       report_done(false);
       return;
@@ -423,7 +423,7 @@ uncompress_archive() {
 
   result = inflateEnd(&z);
   if (result < 0) {
-    cerr << z.msg << "\n";
+    nout << z.msg << "\n";
     report_done(false);
     return;
   }
@@ -432,7 +432,7 @@ uncompress_archive() {
   target.close();
 
   if (!_uncompressed_archive.full_verify(_package_dir)) {
-    cerr << "after uncompressing " << target_pathname
+    nout << "after uncompressing " << target_pathname
          << ", failed hash check\n";
     report_done(false);
     return;
@@ -451,19 +451,19 @@ uncompress_archive() {
 ////////////////////////////////////////////////////////////////////
 void P3DPackage::
 extract_archive() {
-  cerr << "extracting " << _uncompressed_archive._filename << "\n";
+  nout << "extracting " << _uncompressed_archive._filename << "\n";
 
   string source_pathname = _package_dir + "/" + _uncompressed_archive._filename;
   P3DMultifileReader reader;
   if (!reader.extract(source_pathname, _package_dir,
                       this, download_portion + uncompress_portion, extract_portion)) {
-    cerr << "Failure extracting " << _uncompressed_archive._filename
+    nout << "Failure extracting " << _uncompressed_archive._filename
          << "\n";
     report_done(false);
     return;
   }
 
-  cerr << "done extracting\n";
+  nout << "done extracting\n";
   report_done(true);
 }
 
@@ -490,7 +490,7 @@ report_progress(double progress) {
 ////////////////////////////////////////////////////////////////////
 void P3DPackage::
 report_done(bool success) {
-  cerr << "report_done(" << success << "), "
+  nout << "report_done(" << success << "), "
        << _callbacks.size() << " callbacks\n";
   if (success) {
     _ready = true;
@@ -740,33 +740,33 @@ quick_verify(const string &package_dir) const {
   string pathname = package_dir + "/" + _filename;
   struct stat st;
   if (stat(pathname.c_str(), &st) != 0) {
-    cerr << "file not found: " << _filename << "\n";
+    nout << "file not found: " << _filename << "\n";
     return false;
   }
 
   if (st.st_size != _size) {
     // If the size is wrong, the file fails.
-    cerr << "size wrong: " << _filename << "\n";
+    nout << "size wrong: " << _filename << "\n";
     return false;
   }
 
   if (st.st_mtime == _timestamp) {
     // If the size is right and the timestamp is right, the file passes.
-    cerr << "file ok: " << _filename << "\n";
+    nout << "file ok: " << _filename << "\n";
     return true;
   }
 
-  cerr << "modification time wrong: " << _filename << "\n";
+  nout << "modification time wrong: " << _filename << "\n";
 
   // If the size is right but the timestamp is wrong, the file
   // soft-fails.  We follow this up with a hash check.
   if (!check_hash(pathname)) {
     // Hard fail, the hash is wrong.
-    cerr << "hash check wrong: " << _filename << "\n";
+    nout << "hash check wrong: " << _filename << "\n";
     return false;
   }
 
-  cerr << "hash check ok: " << _filename << "\n";
+  nout << "hash check ok: " << _filename << "\n";
 
   // The hash is OK after all.  Change the file's timestamp back to
   // what we expect it to be, so we can quick-verify it successfully
@@ -795,13 +795,13 @@ full_verify(const string &package_dir) const {
   string pathname = package_dir + "/" + _filename;
   struct stat st;
   if (stat(pathname.c_str(), &st) != 0) {
-    cerr << "file not found: " << _filename << "\n";
+    nout << "file not found: " << _filename << "\n";
     return false;
   }
 
   if (st.st_size != _size) {
     // If the size is wrong, the file fails.
-    cerr << "size wrong: " << _filename << "\n";
+    nout << "size wrong: " << _filename << "\n";
     return false;
   }
 
@@ -809,11 +809,11 @@ full_verify(const string &package_dir) const {
   // soft-fails.  We follow this up with a hash check.
   if (!check_hash(pathname)) {
     // Hard fail, the hash is wrong.
-    cerr << "hash check wrong: " << _filename << "\n";
+    nout << "hash check wrong: " << _filename << "\n";
     return false;
   }
 
-  cerr << "hash check ok: " << _filename << "\n";
+  nout << "hash check ok: " << _filename << "\n";
 
   // The hash is OK.  If the timestamp is wrong, change it back to
   // what we expect it to be, so we can quick-verify it successfully
@@ -839,7 +839,7 @@ bool P3DPackage::FileSpec::
 check_hash(const string &pathname) const {
   ifstream stream(pathname.c_str(), ios::in | ios::binary);
   if (!stream) {
-    cerr << "unable to read " << pathname << "\n";
+    nout << "unable to read " << pathname << "\n";
     return false;
   }
 

+ 3 - 3
direct/src/plugin/p3dProgressWindow.cxx

@@ -47,14 +47,14 @@ package_ready(P3DPackage *package, bool success) {
       if (success) {
         _session->start_p3dpython();
       } else {
-        cerr << "Failed to install " << package->get_package_name()
+        nout << "Failed to install " << package->get_package_name()
              << "_" << package->get_package_version() << "\n";
       }
     } else {
-      cerr << "Unexpected panda3d package: " << package << "\n";
+      nout << "Unexpected panda3d package: " << package << "\n";
     }
   } else {
-    cerr << "Unexpected callback for P3DSession\n";
+    nout << "Unexpected callback for P3DSession\n";
   }
 }
 

+ 23 - 18
direct/src/plugin/p3dPythonRun.cxx

@@ -44,10 +44,10 @@ P3DPythonRun(int argc, char *argv[]) {
   HANDLE read = GetStdHandle(STD_INPUT_HANDLE);
   HANDLE write = GetStdHandle(STD_OUTPUT_HANDLE);
   if (!SetStdHandle(STD_INPUT_HANDLE, INVALID_HANDLE_VALUE)) {
-    cerr << "unable to reset input handle\n";
+    nout << "unable to reset input handle\n";
   }
   if (!SetStdHandle(STD_OUTPUT_HANDLE, INVALID_HANDLE_VALUE)) {
-    cerr << "unable to reset input handle\n";
+    nout << "unable to reset input handle\n";
   }
 
   _pipe_read.open_read(read);
@@ -58,10 +58,10 @@ P3DPythonRun(int argc, char *argv[]) {
 #endif  // _WIN32
 
   if (!_pipe_read) {
-    cerr << "unable to open read pipe\n";
+    nout << "unable to open read pipe\n";
   }
   if (!_pipe_write) {
-    cerr << "unable to open write pipe\n";
+    nout << "unable to open write pipe\n";
   }
 
   spawn_read_thread();
@@ -126,14 +126,14 @@ run_python() {
   task_mgr->add(_check_comm_task);
 
   // Finally, get lost in taskMgr.run().
-  cerr << "calling run()\n";
+  nout << "calling run()\n";
   PyObject *done = PyObject_CallMethod(_taskMgr, "run", "");
   if (done == NULL) {
     PyErr_Print();
     return false;
   }
   Py_DECREF(done);
-  cerr << "done calling run()\n";
+  nout << "done calling run()\n";
 
   return true;
 }
@@ -146,7 +146,7 @@ run_python() {
 ////////////////////////////////////////////////////////////////////
 void P3DPythonRun::
 handle_command(TiXmlDocument *doc) {
-  cerr << "got command: " << *doc << "\n";
+  nout << "got command: " << *doc << "\n";
   TiXmlElement *xcommand = doc->FirstChildElement("command");
   if (xcommand != NULL) {
     const char *cmd = xcommand->Attribute("cmd");
@@ -166,7 +166,7 @@ handle_command(TiXmlDocument *doc) {
         terminate_session();
         
       } else {
-        cerr << "Unhandled command " << cmd << "\n";
+        nout << "Unhandled command " << cmd << "\n";
       }
     }
   }
@@ -254,7 +254,7 @@ spawn_read_thread() {
 ////////////////////////////////////////////////////////////////////
 void P3DPythonRun::
 join_read_thread() {
-  cerr << "waiting for thread\n";
+  nout << "waiting for thread\n";
   _read_thread_continue = false;
   _pipe_read.close();
   
@@ -267,7 +267,7 @@ join_read_thread() {
   void *return_val;
   pthread_join(_read_thread, &return_val);
 #endif
-  cerr << "done waiting for thread\n";
+  nout << "done waiting for thread\n";
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -278,7 +278,7 @@ join_read_thread() {
 ////////////////////////////////////////////////////////////////////
 void P3DPythonRun::
 start_instance(P3DCInstance *inst) {
-  cerr << "starting instance " << inst->get_p3d_filename() << "\n";
+  nout << "starting instance " << inst->get_p3d_filename() << "\n";
   _instances[inst->get_instance_id()] = inst;
 
   string window_type;
@@ -312,8 +312,13 @@ start_instance(P3DCInstance *inst) {
     PyErr_Print();
   }
   Py_XDECREF(result);
+
+  PyObject *tokens = inst->get_py_tokens();
   
-  result = PyObject_CallFunction(_runPackedApp, "[s]", inst->get_p3d_filename().c_str());
+  result = PyObject_CallFunction
+    (_runPackedApp, "sO", inst->get_p3d_filename().c_str(), tokens);
+  Py_DECREF(tokens);
+
   if (result == NULL) {
     PyErr_Print();
   }
@@ -329,7 +334,7 @@ void P3DPythonRun::
 terminate_instance(int id) {
   Instances::iterator ii = _instances.find(id);
   if (ii == _instances.end()) {
-    cerr << "Can't stop instance " << id << ": not started.\n";
+    nout << "Can't stop instance " << id << ": not started.\n";
     return;
   }
 
@@ -358,14 +363,14 @@ terminate_session() {
   }
   _instances.clear();
 
-  cerr << "calling stop()\n";
+  nout << "calling stop()\n";
   PyObject *result = PyObject_CallMethod(_taskMgr, "stop", "");
   if (result == NULL) {
     PyErr_Print();
     return;
   }
   Py_DECREF(result);
-  cerr << "done calling stop()\n";
+  nout << "done calling stop()\n";
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -375,14 +380,14 @@ terminate_session() {
 ////////////////////////////////////////////////////////////////////
 void P3DPythonRun::
 rt_thread_run() {
-  cerr << "thread reading.\n";
+  nout << "thread reading.\n";
   while (_read_thread_continue) {
     TiXmlDocument *doc = new TiXmlDocument;
 
     _pipe_read >> *doc;
     if (!_pipe_read || _pipe_read.eof()) {
       // Some error on reading.  Abort.
-      cerr << "Error on reading.\n";
+      nout << "Error on reading.\n";
       _program_continue = false;
       return;
     }
@@ -444,7 +449,7 @@ main(int argc, char *argv[]) {
   P3DPythonRun run(argc, argv);
   
   if (!run.run_python()) {
-    cerr << "Couldn't initialize Python.\n";
+    nout << "Couldn't initialize Python.\n";
     return 1;
   }
   return 0;

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

@@ -19,7 +19,7 @@
 //  Description: Returns a string that uniquely identifies this
 //               session.  See P3dInstance::get_session_key().
 ////////////////////////////////////////////////////////////////////
-INLINE const string &P3DSession::
+inline const string &P3DSession::
 get_session_key() const {
   return _session_key;
 }
@@ -30,7 +30,7 @@ get_session_key() const {
 //  Description: Returns a string that uniquely identifies this
 //               session's required Python version.
 ////////////////////////////////////////////////////////////////////
-INLINE const string &P3DSession::
+inline const string &P3DSession::
 get_python_version() const {
   return _python_version;
 }
@@ -42,7 +42,7 @@ get_python_version() const {
 //               within the session.  When this is zero, the session
 //               may be safely deleted.
 ////////////////////////////////////////////////////////////////////
-INLINE int P3DSession::
+inline int P3DSession::
 get_num_instances() const {
   return _instances.size();
 }

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

@@ -89,7 +89,7 @@ P3DSession::
     // Now give the process a chance to terminate itself cleanly.
     if (WaitForSingleObject(_p3dpython_handle, 2000) == WAIT_TIMEOUT) {
       // It didn't shut down cleanly, so kill it the hard way.
-      cerr << "Terminating process.\n";
+      nout << "Terminating process.\n";
       TerminateProcess(_p3dpython_handle, 2);
     }
 
@@ -232,8 +232,8 @@ send_command(TiXmlDocument *command) {
 void P3DSession::
 start_p3dpython() {
 #ifdef _WIN32
-  //string p3dpython = "c:/cygwin/home/drose/player/direct/built/bin/p3dpython.exe";
-  string p3dpython = _python_root_dir + "/p3dpython.exe";
+  string p3dpython = "c:/cygwin/home/drose/player/direct/built/bin/p3dpython.exe";
+  //string p3dpython = _python_root_dir + "/p3dpython.exe";
 #else
   string p3dpython = "/Users/drose/player/direct/built/bin/p3dpython";
 #endif
@@ -244,9 +244,9 @@ start_p3dpython() {
   // These are the enviroment variables we forward from the current
   // environment, if they are set.
   const char *keep[] = {
-    "TEMP", "HOME", "USER", 
+    "TMP", "TEMP", "HOME", "USER", 
 #ifdef _WIN32
-    "SYSTEMROOT",
+    "SYSTEMROOT", "USERPROFILE",
 #endif
     NULL
   };
@@ -290,18 +290,18 @@ start_p3dpython() {
 #endif
 
   if (!started_p3dpython) {
-    cerr << "Failed to create process.\n";
+    nout << "Failed to create process.\n";
     return;
   }
   _p3dpython_running = true;
 
-  cerr << "Created child process\n";
+  nout << "Created child process\n";
 
   if (!_pipe_read) {
-    cerr << "unable to open read pipe\n";
+    nout << "unable to open read pipe\n";
   }
   if (!_pipe_write) {
-    cerr << "unable to open write pipe\n";
+    nout << "unable to open write pipe\n";
   }
   
   spawn_read_thread();
@@ -358,7 +358,7 @@ join_read_thread() {
     return;
   }
 
-  cerr << "session waiting for thread\n";
+  nout << "session waiting for thread\n";
   _read_thread_continue = false;
   _pipe_read.close();
   
@@ -371,7 +371,7 @@ join_read_thread() {
   void *return_val;
   pthread_join(_read_thread, &return_val);
 #endif
-  cerr << "session done waiting for thread\n";
+  nout << "session done waiting for thread\n";
 
   _started_read_thread = false;
 }
@@ -383,20 +383,20 @@ join_read_thread() {
 ////////////////////////////////////////////////////////////////////
 void P3DSession::
 rt_thread_run() {
-  cerr << "session thread reading.\n";
+  nout << "session thread reading.\n";
   while (_read_thread_continue) {
     TiXmlDocument *doc = new TiXmlDocument;
 
     _pipe_read >> *doc;
     if (!_pipe_read || _pipe_read.eof()) {
       // Some error on reading.  Abort.
-      cerr << "Error on session reading.\n";
+      nout << "Error on session reading.\n";
       rt_terminate();
       return;
     }
 
     // Successfully read an XML document.
-    cerr << "Session got request: " << *doc << "\n";
+    nout << "Session got request: " << *doc << "\n";
 
     // TODO: feed the request up to the parent.
     delete doc;
@@ -478,7 +478,7 @@ win_create_process(const string &program, const string &start_dir,
 
   // Create the pipe to the process.
   if (!CreatePipe(&r_to, &w_to, NULL, 0)) {
-    cerr << "failed to create pipe\n";
+    nout << "failed to create pipe\n";
   } else {
     // Make sure the right end of the pipe is inheritable.
     SetHandleInformation(r_to, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT);
@@ -487,7 +487,7 @@ win_create_process(const string &program, const string &start_dir,
 
   // Create the pipe from the process.
   if (!CreatePipe(&r_from, &w_from, NULL, 0)) {
-    cerr << "failed to create pipe\n";
+    nout << "failed to create pipe\n";
   } else { 
     // Make sure the right end of the pipe is inheritable.
     SetHandleInformation(w_from, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT);
@@ -507,7 +507,7 @@ win_create_process(const string &program, const string &start_dir,
       error_handle = handle;
       SetHandleInformation(error_handle, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT);
     } else {
-      cerr << "Unable to open " << output_filename << "\n";
+      nout << "Unable to open " << output_filename << "\n";
     }
   }
 
@@ -606,7 +606,7 @@ posix_create_process(const string &program, const string &start_dir,
       int logfile_fd = open(output_filename.c_str(), 
                             O_WRONLY | O_CREAT | O_TRUNC, 0666);
       if (logfile_fd < 0) {
-        cerr << "Unable to open " << output_filename << "\n";
+        nout << "Unable to open " << output_filename << "\n";
       } else {
         dup2(logfile_fd, STDERR_FILENO);
         close(logfile_fd);
@@ -621,7 +621,7 @@ posix_create_process(const string &program, const string &start_dir,
     close(from_fd[0]);
 
     if (chdir(start_dir.c_str()) < 0) {
-      cerr << "Could not chdir to " << start_dir << "\n";
+      nout << "Could not chdir to " << start_dir << "\n";
       _exit(1);
     }
 
@@ -637,7 +637,7 @@ posix_create_process(const string &program, const string &start_dir,
     ptrs.push_back((char *)NULL);
     
     execle(program.c_str(), program.c_str(), (char *)0, &ptrs[0]);
-    cerr << "Failed to exec " << program << "\n";
+    nout << "Failed to exec " << program << "\n";
     _exit(1);
   }
 

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

@@ -38,13 +38,13 @@ public:
   P3DSession(P3DInstance *inst);
   ~P3DSession();
 
-  INLINE const string &get_session_key() const;
-  INLINE const string &get_python_version() const;
+  inline const string &get_session_key() const;
+  inline const string &get_python_version() const;
 
   void start_instance(P3DInstance *inst);
   void terminate_instance(P3DInstance *inst);
 
-  INLINE int get_num_instances() const;
+  inline int get_num_instances() const;
 
 private:
   void send_command(TiXmlDocument *command);

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

@@ -118,7 +118,7 @@ thread_run() {
   retval = GetMessage(&msg, NULL, 0, 0);
   while (retval != 0 && _thread_continue) {
     if (retval == -1) {
-      cerr << "Error processing message queue.\n";
+      nout << "Error processing message queue.\n";
       break;
     }
     TranslateMessage(&msg);
@@ -186,7 +186,7 @@ make_window() {
     wc.lpszClassName = "panda3d_progress";
     
     if (!RegisterClass(&wc)) {
-      cerr << "Could not register window class!\n";
+      nout << "Could not register window class!\n";
     }
     registered_class = true;
   }
@@ -218,7 +218,7 @@ make_window() {
                    parent_hwnd, NULL, application, 0);
     
     if (!_hwnd) {
-      cerr << "Could not create embedded window!\n";
+      nout << "Could not create embedded window!\n";
       return;
     }
 
@@ -234,7 +234,7 @@ make_window() {
                    x, y, width, height,
                    NULL, NULL, application, 0);
     if (!_hwnd) {
-      cerr << "Could not create toplevel window!\n";
+      nout << "Could not create toplevel window!\n";
       return;
     }
 

+ 22 - 1
direct/src/plugin/p3d_plugin.cxx

@@ -24,8 +24,13 @@
 bool initialized_lock = false;
 LOCK _lock;
 
+ofstream log;
+string plugin_output_filename;
+ostream *nout_stream;
+
+
 bool 
-P3D_initialize(int api_version) {
+P3D_initialize(int api_version, const char *output_filename) {
   if (api_version != P3D_API_VERSION) {
     // Can't accept an incompatible version.
     return false;
@@ -37,6 +42,18 @@ P3D_initialize(int api_version) {
   }
   ACQUIRE_LOCK(_lock);
 
+  plugin_output_filename = string();
+  if (output_filename != NULL) {
+    plugin_output_filename = output_filename;
+  }
+  nout_stream = &cerr;
+  if (!plugin_output_filename.empty()) {
+    log.open(plugin_output_filename.c_str(), ios::out | ios::trunc);
+    if (log) {
+      nout_stream = &log;
+    }
+  }
+
   P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
   bool result = inst_mgr->initialize();
   RELEASE_LOCK(_lock);
@@ -61,6 +78,10 @@ P3D_create_instance(P3D_request_ready_func *func,
   assert(P3DInstanceManager::get_global_ptr()->is_initialized());
   ACQUIRE_LOCK(_lock);
   P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
+  if (p3d_filename == NULL) {
+    p3d_filename = "";
+  }
+
   P3DInstance *result = 
     inst_mgr->create_instance(func, p3d_filename, window_type, 
                               win_x, win_y, win_width, win_height,

+ 23 - 11
direct/src/plugin/p3d_plugin.h

@@ -73,7 +73,7 @@ extern "C" {
 libraries match.  It should be passed to P3D_initialize() (below).
 This number will be incremented whenever there are changes to any of
 the interface specifications defined in this header file. */
-#define P3D_API_VERSION 1
+#define P3D_API_VERSION 2
 
 /************************ GLOBAL FUNCTIONS **************************/
 
@@ -85,12 +85,17 @@ the interface specifications defined in this header file. */
    the dll can verify that it has been built with the same version of
    the API as the host.
 
+   The output_filename is usually NULL, but if you put a filename
+   here, it will be used as the log file for the output from the
+   plugin.  This is useful for debugging, particularly when running
+   within a browser that squelches stderr.
+
    This function returns true if the plugin 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);
+P3D_initialize_func(int api_version, const char *output_filename);
 
 /* This function frees a pointer returned by
    P3D_instance_get_property(), or another similar function that
@@ -187,19 +192,26 @@ typedef struct {
   const char *_value;
 } P3D_token;
 
-/* This function creates a new Panda3D instance.  For p3d_filename
-   pass the name of a file on disk that contains the contents of the
-   p3d file that should be launched within the instance.  For tokens,
-   pass an array of P3D_token elements (above), which correspond to
-   the user-supplied keyword/value pairs that may appear in the embed
-   token within the HTML syntax; the host is responsible for
-   allocating this array, and for deallocating it after this call (the
-   plugin will make its own copy of the array).
+/* This function creates a new Panda3D instance.  
+
+   For p3d_filename pass the name of a file on disk that contains the
+   contents of the p3d file that should be launched within the
+   instance.  If this is empty or NULL, the "src" token (below) will
+   be downloaded instead.
+
+   For tokens, pass an array of P3D_token elements (above), which
+   correspond to the user-supplied keyword/value pairs that may appear
+   in the embed token within the HTML syntax; the host is responsible
+   for allocating this array, and for deallocating it after this call
+   (the plugin will make its own copy of the array).
 
    Most tokens are implemented by the application and are undefined at
-   the system level.  However, one token in particular is
+   the system level.  However, two tokens in particular are
    system-defined:
 
+     "src" : names a URL that will be loaded for the contents of the
+       p3d file, if p3d_filename is empty or NULL.
+
      "output_filename" : names a file to create on disk which contains
        the console output from the application.  This may be useful in
        debugging.  If this is omitted, or an empty string, the console

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

@@ -26,12 +26,17 @@
 #include "p3d_lock.h"
 
 #include <iostream>
+#include <fstream>
 #include <string>
 #include <assert.h>
 
 using namespace std;
 
-#define INLINE inline
+// Appears in p3d_plugin.cxx.
+extern ofstream log;
+extern string plugin_output_filename;
+extern ostream *nout_stream;
+#define nout (*nout_stream)
 
 #endif
 

+ 30 - 0
direct/src/plugin_npapi/Sources.pp

@@ -0,0 +1,30 @@
+// This directory is still experimental.  Define HAVE_P3D_PLUGIN in
+// your Config.pp to build it.
+#define BUILD_DIRECTORY $[and $[HAVE_P3D_PLUGIN],$[HAVE_NPAPI]]
+
+#define USE_PACKAGES npapi
+
+#begin lib_target
+  // By Mozilla convention, on Windows at least, the generated DLL
+  // filename must begin with "np", not "libnp".
+  #define TARGET nppanda3d
+  #define LIB_PREFIX
+
+  #define COMBINED_SOURCES \
+    $[TARGET]_composite1.cxx
+
+  #define SOURCES \
+    nppanda3d_common.h \
+    nppanda3d_startup.h \
+    ppInstance.h ppInstance.I
+
+  #define INCLUDED_SOURCES \
+    nppanda3d_startup.cxx \
+    ppInstance.cxx
+ 
+  #define WIN_RESOURCE_FILE nppanda3d.rc
+  #define LINKER_DEF_FILE nppanda3d.def
+
+  #define INSTALL_HEADERS
+
+#end lib_target

+ 320 - 0
direct/src/plugin_npapi/np_entry.cpp

@@ -0,0 +1,320 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: NPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is 
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or 
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the NPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the NPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+//////////////////////////////////////////////////////////////
+//
+// Main plugin entry point implementation -- exports from the 
+// plugin library
+//
+#include "npplat.h"
+#include "pluginbase.h"
+
+NPNetscapeFuncs NPNFuncs;
+
+NPError OSCALL NP_Shutdown()
+{
+  NS_PluginShutdown();
+  return NPERR_NO_ERROR;
+}
+
+static NPError fillPluginFunctionTable(NPPluginFuncs* aNPPFuncs)
+{
+  if(aNPPFuncs == NULL)
+    return NPERR_INVALID_FUNCTABLE_ERROR;
+
+  // Set up the plugin function table that Netscape will use to
+  // call us. Netscape needs to know about our version and size   
+  // and have a UniversalProcPointer for every function we implement.
+
+  aNPPFuncs->version       = (NP_VERSION_MAJOR << 8) | NP_VERSION_MINOR;
+#ifdef XP_MAC
+  aNPPFuncs->newp          = NewNPP_NewProc(Private_New);
+  aNPPFuncs->destroy       = NewNPP_DestroyProc(Private_Destroy);
+  aNPPFuncs->setwindow     = NewNPP_SetWindowProc(Private_SetWindow);
+  aNPPFuncs->newstream     = NewNPP_NewStreamProc(Private_NewStream);
+  aNPPFuncs->destroystream = NewNPP_DestroyStreamProc(Private_DestroyStream);
+  aNPPFuncs->asfile        = NewNPP_StreamAsFileProc(Private_StreamAsFile);
+  aNPPFuncs->writeready    = NewNPP_WriteReadyProc(Private_WriteReady);
+  aNPPFuncs->write         = NewNPP_WriteProc(Private_Write);
+  aNPPFuncs->print         = NewNPP_PrintProc(Private_Print);
+  aNPPFuncs->event         = NewNPP_HandleEventProc(Private_HandleEvent);	
+  aNPPFuncs->urlnotify     = NewNPP_URLNotifyProc(Private_URLNotify);			
+  aNPPFuncs->getvalue      = NewNPP_GetValueProc(Private_GetValue);
+  aNPPFuncs->setvalue      = NewNPP_SetValueProc(Private_SetValue);
+#else
+  aNPPFuncs->newp          = NPP_New;
+  aNPPFuncs->destroy       = NPP_Destroy;
+  aNPPFuncs->setwindow     = NPP_SetWindow;
+  aNPPFuncs->newstream     = NPP_NewStream;
+  aNPPFuncs->destroystream = NPP_DestroyStream;
+  aNPPFuncs->asfile        = NPP_StreamAsFile;
+  aNPPFuncs->writeready    = NPP_WriteReady;
+  aNPPFuncs->write         = NPP_Write;
+  aNPPFuncs->print         = NPP_Print;
+  aNPPFuncs->event         = NPP_HandleEvent;
+  aNPPFuncs->urlnotify     = NPP_URLNotify;
+  aNPPFuncs->getvalue      = NPP_GetValue;
+  aNPPFuncs->setvalue      = NPP_SetValue;
+#endif
+#ifdef OJI
+  aNPPFuncs->javaClass     = NULL;
+#endif
+
+  return NPERR_NO_ERROR;
+}
+
+static NPError fillNetscapeFunctionTable(NPNetscapeFuncs* aNPNFuncs)
+{
+  if(aNPNFuncs == NULL)
+    return NPERR_INVALID_FUNCTABLE_ERROR;
+
+  if(HIBYTE(aNPNFuncs->version) > NP_VERSION_MAJOR)
+    return NPERR_INCOMPATIBLE_VERSION_ERROR;
+
+  if(aNPNFuncs->size < sizeof(NPNetscapeFuncs))
+    return NPERR_INVALID_FUNCTABLE_ERROR;
+
+  NPNFuncs.size             = aNPNFuncs->size;
+  NPNFuncs.version          = aNPNFuncs->version;
+  NPNFuncs.geturlnotify     = aNPNFuncs->geturlnotify;
+  NPNFuncs.geturl           = aNPNFuncs->geturl;
+  NPNFuncs.posturlnotify    = aNPNFuncs->posturlnotify;
+  NPNFuncs.posturl          = aNPNFuncs->posturl;
+  NPNFuncs.requestread      = aNPNFuncs->requestread;
+  NPNFuncs.newstream        = aNPNFuncs->newstream;
+  NPNFuncs.write            = aNPNFuncs->write;
+  NPNFuncs.destroystream    = aNPNFuncs->destroystream;
+  NPNFuncs.status           = aNPNFuncs->status;
+  NPNFuncs.uagent           = aNPNFuncs->uagent;
+  NPNFuncs.memalloc         = aNPNFuncs->memalloc;
+  NPNFuncs.memfree          = aNPNFuncs->memfree;
+  NPNFuncs.memflush         = aNPNFuncs->memflush;
+  NPNFuncs.reloadplugins    = aNPNFuncs->reloadplugins;
+#ifdef OJI
+  NPNFuncs.getJavaEnv       = aNPNFuncs->getJavaEnv;
+  NPNFuncs.getJavaPeer      = aNPNFuncs->getJavaPeer;
+#endif
+  NPNFuncs.getvalue         = aNPNFuncs->getvalue;
+  NPNFuncs.setvalue         = aNPNFuncs->setvalue;
+  NPNFuncs.invalidaterect   = aNPNFuncs->invalidaterect;
+  NPNFuncs.invalidateregion = aNPNFuncs->invalidateregion;
+  NPNFuncs.forceredraw      = aNPNFuncs->forceredraw;
+
+  return NPERR_NO_ERROR;
+}
+
+//
+// Some exports are different on different platforms
+//
+
+/**************************************************/
+/*                                                */
+/*                   Windows                      */
+/*                                                */
+/**************************************************/
+#ifdef XP_WIN
+
+NPError OSCALL NP_Initialize(NPNetscapeFuncs* aNPNFuncs)
+{
+  NPError rv = fillNetscapeFunctionTable(aNPNFuncs);
+  if(rv != NPERR_NO_ERROR)
+    return rv;
+
+  return NS_PluginInitialize();
+}
+
+NPError OSCALL NP_GetEntryPoints(NPPluginFuncs* aNPPFuncs)
+{
+  return fillPluginFunctionTable(aNPPFuncs);
+}
+
+#endif //XP_WIN
+
+/**************************************************/
+/*                                                */
+/*                    Unix                        */
+/*                                                */
+/**************************************************/
+#ifdef XP_UNIX
+
+NPError NP_Initialize(NPNetscapeFuncs* aNPNFuncs, NPPluginFuncs* aNPPFuncs)
+{
+  NPError rv = fillNetscapeFunctionTable(aNPNFuncs);
+  if(rv != NPERR_NO_ERROR)
+    return rv;
+
+  rv = fillPluginFunctionTable(aNPPFuncs);
+  if(rv != NPERR_NO_ERROR)
+    return rv;
+
+  return NS_PluginInitialize();
+}
+
+char * NP_GetMIMEDescription(void)
+{
+  return NPP_GetMIMEDescription();
+}
+
+NPError NP_GetValue(void *future, NPPVariable aVariable, void *aValue)
+{
+  return NS_PluginGetValue(aVariable, aValue);
+}
+
+#endif //XP_UNIX
+
+/**************************************************/
+/*                                                */
+/*                     Mac                        */
+/*                                                */
+/**************************************************/
+#ifdef XP_MAC
+
+#if !TARGET_API_MAC_CARBON
+QDGlobals* gQDPtr; // Pointer to Netscape's QuickDraw globals
+#endif
+
+short gResFile; // Refnum of the plugin's resource file
+
+NPError Private_Initialize(void)
+{
+  NPError rv = NS_PluginInitialize();
+  return rv;
+}
+
+void Private_Shutdown(void)
+{
+  NS_PluginShutdown();
+  __destroy_global_chain();
+}
+
+void SetUpQD(void);
+
+void SetUpQD(void)
+{
+  ProcessSerialNumber PSN;
+  FSSpec              myFSSpec;
+  Str63               name;
+  ProcessInfoRec      infoRec;
+  OSErr               result = noErr;
+  CFragConnectionID   connID;
+  Str255              errName;
+
+  // Memorize the plugin¹s resource file refnum for later use.
+  gResFile = CurResFile();
+
+#if !TARGET_API_MAC_CARBON
+  // Ask the system if CFM is available.
+  long response;
+  OSErr err = Gestalt(gestaltCFMAttr, &response);
+  Boolean hasCFM = BitTst(&response, 31-gestaltCFMPresent);
+
+  if (hasCFM) {
+    // GetProcessInformation takes a process serial number and 
+    // will give us back the name and FSSpec of the application.
+    // See the Process Manager in IM.
+    infoRec.processInfoLength = sizeof(ProcessInfoRec);
+    infoRec.processName = name;
+    infoRec.processAppSpec = &myFSSpec;
+
+    PSN.highLongOfPSN = 0;
+    PSN.lowLongOfPSN = kCurrentProcess;
+
+    result = GetProcessInformation(&PSN, &infoRec);
+  }
+	else
+    // If no CFM installed, assume it must be a 68K app.
+    result = -1;		
+
+  if (result == noErr) {
+    // Now that we know the app name and FSSpec, we can call GetDiskFragment
+    // to get a connID to use in a subsequent call to FindSymbol (it will also
+    // return the address of ³main² in app, which we ignore).  If GetDiskFragment 
+    // returns an error, we assume the app must be 68K.
+    Ptr mainAddr; 	
+    result =  GetDiskFragment(infoRec.processAppSpec, 0L, 0L, infoRec.processName,
+                              kReferenceCFrag, &connID, (Ptr*)&mainAddr, errName);
+  }
+
+  if (result == noErr) {
+    // The app is a PPC code fragment, so call FindSymbol
+    // to get the exported ³qd² symbol so we can access its
+    // QuickDraw globals.
+    CFragSymbolClass symClass;
+    result = FindSymbol(connID, "\pqd", (Ptr*)&gQDPtr, &symClass);
+  }
+  else {
+    // The app is 68K, so use its A5 to compute the address
+    // of its QuickDraw globals.
+    gQDPtr = (QDGlobals*)(*((long*)SetCurrentA5()) - (sizeof(QDGlobals) - sizeof(GrafPtr)));
+  }
+#endif /* !TARGET_API_MAC_CARBON */
+}
+
+NPError main(NPNetscapeFuncs* nsTable, NPPluginFuncs* pluginFuncs, NPP_ShutdownUPP* unloadUpp);
+
+#if !TARGET_API_MAC_CARBON
+#pragma export on
+#if GENERATINGCFM
+RoutineDescriptor mainRD = BUILD_ROUTINE_DESCRIPTOR(uppNPP_MainEntryProcInfo, main);
+#endif
+#pragma export off
+#endif /* !TARGET_API_MAC_CARBON */
+
+
+NPError main(NPNetscapeFuncs* aNPNFuncs, NPPluginFuncs* aNPPFuncs, NPP_ShutdownUPP* aUnloadUpp)
+{
+  NPError rv = NPERR_NO_ERROR;
+
+  if (aUnloadUpp == NULL)
+    rv = NPERR_INVALID_FUNCTABLE_ERROR;
+
+  if (rv == NPERR_NO_ERROR)
+    rv = fillNetscapeFunctionTable(aNPNFuncs);
+
+  if (rv == NPERR_NO_ERROR) {
+    // defer static constructors until the global functions are initialized.
+    __InitCode__();
+    rv = fillPluginFunctionTable(aNPPFuncs);
+  }
+
+  *aUnloadUpp = NewNPP_ShutdownProc(Private_Shutdown);
+  SetUpQD();
+  rv = Private_Initialize();
+	
+  return rv;
+}
+#endif //XP_MAC

+ 215 - 0
direct/src/plugin_npapi/npn_gate.cpp

@@ -0,0 +1,215 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: NPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is 
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or 
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the NPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the NPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+
+////////////////////////////////////////////////////////////
+//
+// Implementation of Netscape entry points (NPN_*)
+//
+#include "npplat.h"
+
+extern NPNetscapeFuncs NPNFuncs;
+
+void NPN_Version(int* plugin_major, int* plugin_minor, int* netscape_major, int* netscape_minor)
+{
+  *plugin_major   = NP_VERSION_MAJOR;
+  *plugin_minor   = NP_VERSION_MINOR;
+  *netscape_major = HIBYTE(NPNFuncs.version);
+  *netscape_minor = LOBYTE(NPNFuncs.version);
+}
+
+NPError NPN_GetURLNotify(NPP instance, const char *url, const char *target, void* notifyData)
+{
+	int navMinorVers = NPNFuncs.version & 0xFF;
+  NPError rv = NPERR_NO_ERROR;
+
+  if( navMinorVers >= NPVERS_HAS_NOTIFICATION )
+		rv = CallNPN_GetURLNotifyProc(NPNFuncs.geturlnotify, instance, url, target, notifyData);
+	else
+		rv = NPERR_INCOMPATIBLE_VERSION_ERROR;
+
+  return rv;
+}
+
+NPError NPN_GetURL(NPP instance, const char *url, const char *target)
+{
+  NPError rv = CallNPN_GetURLProc(NPNFuncs.geturl, instance, url, target);
+  return rv;
+}
+
+NPError NPN_PostURLNotify(NPP instance, const char* url, const char* window, uint32 len, const char* buf, NPBool file, void* notifyData)
+{
+	int navMinorVers = NPNFuncs.version & 0xFF;
+  NPError rv = NPERR_NO_ERROR;
+
+	if( navMinorVers >= NPVERS_HAS_NOTIFICATION )
+		rv = CallNPN_PostURLNotifyProc(NPNFuncs.posturlnotify, instance, url, window, len, buf, file, notifyData);
+	else
+		rv = NPERR_INCOMPATIBLE_VERSION_ERROR;
+
+  return rv;
+}
+
+NPError NPN_PostURL(NPP instance, const char* url, const char* window, uint32 len, const char* buf, NPBool file)
+{
+  NPError rv = CallNPN_PostURLProc(NPNFuncs.posturl, instance, url, window, len, buf, file);
+  return rv;
+} 
+
+NPError NPN_RequestRead(NPStream* stream, NPByteRange* rangeList)
+{
+  NPError rv = CallNPN_RequestReadProc(NPNFuncs.requestread, stream, rangeList);
+  return rv;
+}
+
+NPError NPN_NewStream(NPP instance, NPMIMEType type, const char* target, NPStream** stream)
+{
+	int navMinorVersion = NPNFuncs.version & 0xFF;
+
+  NPError rv = NPERR_NO_ERROR;
+
+	if( navMinorVersion >= NPVERS_HAS_STREAMOUTPUT )
+		rv = CallNPN_NewStreamProc(NPNFuncs.newstream, instance, type, target, stream);
+	else
+		rv = NPERR_INCOMPATIBLE_VERSION_ERROR;
+
+  return rv;
+}
+
+int32 NPN_Write(NPP instance, NPStream *stream, int32 len, void *buffer)
+{
+	int navMinorVersion = NPNFuncs.version & 0xFF;
+  int32 rv = 0;
+
+  if( navMinorVersion >= NPVERS_HAS_STREAMOUTPUT )
+		rv = CallNPN_WriteProc(NPNFuncs.write, instance, stream, len, buffer);
+	else
+		rv = -1;
+
+  return rv;
+}
+
+NPError NPN_DestroyStream(NPP instance, NPStream* stream, NPError reason)
+{
+	int navMinorVersion = NPNFuncs.version & 0xFF;
+  NPError rv = NPERR_NO_ERROR;
+
+  if( navMinorVersion >= NPVERS_HAS_STREAMOUTPUT )
+		rv = CallNPN_DestroyStreamProc(NPNFuncs.destroystream, instance, stream, reason);
+	else
+		rv = NPERR_INCOMPATIBLE_VERSION_ERROR;
+
+  return rv;
+}
+
+void NPN_Status(NPP instance, const char *message)
+{
+  CallNPN_StatusProc(NPNFuncs.status, instance, message);
+}
+
+const char* NPN_UserAgent(NPP instance)
+{
+  const char * rv = NULL;
+  rv = CallNPN_UserAgentProc(NPNFuncs.uagent, instance);
+  return rv;
+}
+
+void* NPN_MemAlloc(uint32 size)
+{
+  void * rv = NULL;
+  rv = CallNPN_MemAllocProc(NPNFuncs.memalloc, size);
+  return rv;
+}
+
+void NPN_MemFree(void* ptr)
+{
+  CallNPN_MemFreeProc(NPNFuncs.memfree, ptr);
+}
+
+uint32 NPN_MemFlush(uint32 size)
+{
+  uint32 rv = CallNPN_MemFlushProc(NPNFuncs.memflush, size);
+  return rv;
+}
+
+void NPN_ReloadPlugins(NPBool reloadPages)
+{
+  CallNPN_ReloadPluginsProc(NPNFuncs.reloadplugins, reloadPages);
+}
+
+#ifdef OJI
+JRIEnv* NPN_GetJavaEnv(void)
+{
+  JRIEnv * rv = NULL;
+	rv = CallNPN_GetJavaEnvProc(NPNFuncs.getJavaEnv);
+  return rv;
+}
+
+jref NPN_GetJavaPeer(NPP instance)
+{
+  jref rv;
+  rv = CallNPN_GetJavaPeerProc(NPNFuncs.getJavaPeer, instance);
+  return rv;
+}
+#endif
+
+NPError NPN_GetValue(NPP instance, NPNVariable variable, void *value)
+{
+  NPError rv = CallNPN_GetValueProc(NPNFuncs.getvalue, instance, variable, value);
+  return rv;
+}
+
+NPError NPN_SetValue(NPP instance, NPPVariable variable, void *value)
+{
+  NPError rv = CallNPN_SetValueProc(NPNFuncs.setvalue, instance, variable, value);
+  return rv;
+}
+
+void NPN_InvalidateRect(NPP instance, NPRect *invalidRect)
+{
+  CallNPN_InvalidateRectProc(NPNFuncs.invalidaterect, instance, invalidRect);
+}
+
+void NPN_InvalidateRegion(NPP instance, NPRegion invalidRegion)
+{
+  CallNPN_InvalidateRegionProc(NPNFuncs.invalidateregion, instance, invalidRegion);
+}
+
+void NPN_ForceRedraw(NPP instance)
+{
+  CallNPN_ForceRedrawProc(NPNFuncs.forceredraw, instance);
+}

+ 358 - 0
direct/src/plugin_npapi/npp_gate.cpp

@@ -0,0 +1,358 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: NPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is 
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or 
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the NPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the NPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+
+////////////////////////////////////////////////////////////
+//
+// Implementation of plugin entry points (NPP_*)
+//
+#include "pluginbase.h"
+
+// here the plugin creates a plugin instance object which 
+// will be associated with this newly created NPP instance and 
+// will do all the neccessary job
+NPError NPP_New(NPMIMEType pluginType, NPP instance, uint16 mode, int16 argc, char* argn[], char* argv[], NPSavedData* saved)
+{   
+  if(instance == NULL)
+    return NPERR_INVALID_INSTANCE_ERROR;
+
+  NPError rv = NPERR_NO_ERROR;
+
+  // create a new plugin instance object
+  // initialization will be done when the associated window is ready
+  nsPluginCreateData ds;
+  
+  ds.instance = instance;
+  ds.type     = pluginType; 
+  ds.mode     = mode; 
+  ds.argc     = argc; 
+  ds.argn     = argn; 
+  ds.argv     = argv; 
+  ds.saved    = saved;
+
+  nsPluginInstanceBase * plugin = NS_NewPluginInstance(&ds);
+  if(plugin == NULL)
+    return NPERR_OUT_OF_MEMORY_ERROR;
+
+  // associate the plugin instance object with NPP instance
+  instance->pdata = (void *)plugin;
+  return rv;
+}
+
+// here is the place to clean up and destroy the nsPluginInstance object
+NPError NPP_Destroy (NPP instance, NPSavedData** save)
+{
+  if(instance == NULL)
+    return NPERR_INVALID_INSTANCE_ERROR;
+
+  NPError rv = NPERR_NO_ERROR;
+
+  nsPluginInstanceBase * plugin = (nsPluginInstanceBase *)instance->pdata;
+  if(plugin != NULL) {
+    plugin->shut();
+    NS_DestroyPluginInstance(plugin);
+  }
+  return rv;
+}
+
+// during this call we know when the plugin window is ready or
+// is about to be destroyed so we can do some gui specific
+// initialization and shutdown
+NPError NPP_SetWindow (NPP instance, NPWindow* pNPWindow)
+{    
+  if(instance == NULL)
+    return NPERR_INVALID_INSTANCE_ERROR;
+
+  NPError rv = NPERR_NO_ERROR;
+
+  if(pNPWindow == NULL)
+    return NPERR_GENERIC_ERROR;
+
+  nsPluginInstanceBase * plugin = (nsPluginInstanceBase *)instance->pdata;
+
+  if(plugin == NULL) 
+    return NPERR_GENERIC_ERROR;
+
+  // window just created
+  if(!plugin->isInitialized() && (pNPWindow->window != NULL)) { 
+    if(!plugin->init(pNPWindow)) {
+      NS_DestroyPluginInstance(plugin);
+      return NPERR_MODULE_LOAD_FAILED_ERROR;
+    }
+  }
+
+  // window goes away
+  if((pNPWindow->window == NULL) && plugin->isInitialized())
+    return plugin->SetWindow(pNPWindow);
+
+  // window resized?
+  if(plugin->isInitialized() && (pNPWindow->window != NULL))
+    return plugin->SetWindow(pNPWindow);
+
+  // this should not happen, nothing to do
+  if((pNPWindow->window == NULL) && !plugin->isInitialized())
+    return plugin->SetWindow(pNPWindow);
+
+  return rv;
+}
+
+NPError NPP_NewStream(NPP instance, NPMIMEType type, NPStream* stream, NPBool seekable, uint16* stype)
+{
+  if(instance == NULL)
+    return NPERR_INVALID_INSTANCE_ERROR;
+
+  nsPluginInstanceBase * plugin = (nsPluginInstanceBase *)instance->pdata;
+  if(plugin == NULL) 
+    return NPERR_GENERIC_ERROR;
+
+  NPError rv = plugin->NewStream(type, stream, seekable, stype);
+  return rv;
+}
+
+int32 NPP_WriteReady (NPP instance, NPStream *stream)
+{
+  if(instance == NULL)
+    return 0x0fffffff;
+
+  nsPluginInstanceBase * plugin = (nsPluginInstanceBase *)instance->pdata;
+  if(plugin == NULL) 
+    return 0x0fffffff;
+
+  int32 rv = plugin->WriteReady(stream);
+  return rv;
+}
+
+int32 NPP_Write (NPP instance, NPStream *stream, int32 offset, int32 len, void *buffer)
+{   
+  if(instance == NULL)
+    return len;
+
+  nsPluginInstanceBase * plugin = (nsPluginInstanceBase *)instance->pdata;
+  if(plugin == NULL) 
+    return len;
+
+  int32 rv = plugin->Write(stream, offset, len, buffer);
+  return rv;
+}
+
+NPError NPP_DestroyStream (NPP instance, NPStream *stream, NPError reason)
+{
+  if(instance == NULL)
+    return NPERR_INVALID_INSTANCE_ERROR;
+
+  nsPluginInstanceBase * plugin = (nsPluginInstanceBase *)instance->pdata;
+  if(plugin == NULL) 
+    return NPERR_GENERIC_ERROR;
+
+  NPError rv = plugin->DestroyStream(stream, reason);
+  return rv;
+}
+
+void NPP_StreamAsFile (NPP instance, NPStream* stream, const char* fname)
+{
+  if(instance == NULL)
+    return;
+
+  nsPluginInstanceBase * plugin = (nsPluginInstanceBase *)instance->pdata;
+  if(plugin == NULL) 
+    return;
+
+  plugin->StreamAsFile(stream, fname);
+}
+
+void NPP_Print (NPP instance, NPPrint* printInfo)
+{
+  if(instance == NULL)
+    return;
+
+  nsPluginInstanceBase * plugin = (nsPluginInstanceBase *)instance->pdata;
+  if(plugin == NULL) 
+    return;
+
+  plugin->Print(printInfo);
+}
+
+void NPP_URLNotify(NPP instance, const char* url, NPReason reason, void* notifyData)
+{
+  if(instance == NULL)
+    return;
+
+  nsPluginInstanceBase * plugin = (nsPluginInstanceBase *)instance->pdata;
+  if(plugin == NULL) 
+    return;
+
+  plugin->URLNotify(url, reason, notifyData);
+}
+
+NPError	NPP_GetValue(NPP instance, NPPVariable variable, void *value)
+{
+  if(instance == NULL)
+    return NPERR_INVALID_INSTANCE_ERROR;
+
+  nsPluginInstanceBase * plugin = (nsPluginInstanceBase *)instance->pdata;
+  if(plugin == NULL) 
+    return NPERR_GENERIC_ERROR;
+
+  NPError rv = plugin->GetValue(variable, value);
+  return rv;
+}
+
+NPError NPP_SetValue(NPP instance, NPNVariable variable, void *value)
+{
+  if(instance == NULL)
+    return NPERR_INVALID_INSTANCE_ERROR;
+
+  nsPluginInstanceBase * plugin = (nsPluginInstanceBase *)instance->pdata;
+  if(plugin == NULL) 
+    return NPERR_GENERIC_ERROR;
+
+  NPError rv = plugin->SetValue(variable, value);
+  return rv;
+}
+
+int16	NPP_HandleEvent(NPP instance, void* event)
+{
+  if(instance == NULL)
+    return 0;
+
+  nsPluginInstanceBase * plugin = (nsPluginInstanceBase *)instance->pdata;
+  if(plugin == NULL) 
+    return 0;
+
+  uint16 rv = plugin->HandleEvent(event);
+  return rv;
+}
+
+#ifdef OJI
+jref NPP_GetJavaClass (void)
+{
+  return NULL;
+}
+#endif
+
+/**************************************************/
+/*                                                */
+/*                     Mac                        */
+/*                                                */
+/**************************************************/
+
+// Mac needs these wrappers, see npplat.h for more info
+
+#ifdef XP_MAC
+
+NPError	Private_New(NPMIMEType pluginType, NPP instance, uint16 mode, int16 argc, char* argn[], char* argv[], NPSavedData* saved)
+{
+  NPError rv = NPP_New(pluginType, instance, mode, argc, argn, argv, saved);
+  return rv;	
+}
+
+NPError Private_Destroy(NPP instance, NPSavedData** save)
+{
+  NPError rv = NPP_Destroy(instance, save);
+  return rv;
+}
+
+NPError Private_SetWindow(NPP instance, NPWindow* window)
+{
+  NPError rv = NPP_SetWindow(instance, window);
+  return rv;
+}
+
+NPError Private_NewStream(NPP instance, NPMIMEType type, NPStream* stream, NPBool seekable, uint16* stype)
+{
+  NPError rv = NPP_NewStream(instance, type, stream, seekable, stype);
+  return rv;
+}
+
+int32 Private_WriteReady(NPP instance, NPStream* stream)
+{
+  int32 rv = NPP_WriteReady(instance, stream);
+  return rv;
+}
+
+int32 Private_Write(NPP instance, NPStream* stream, int32 offset, int32 len, void* buffer)
+{
+  int32 rv = NPP_Write(instance, stream, offset, len, buffer);
+  return rv;
+}
+
+void Private_StreamAsFile(NPP instance, NPStream* stream, const char* fname)
+{
+  NPP_StreamAsFile(instance, stream, fname);
+}
+
+
+NPError Private_DestroyStream(NPP instance, NPStream* stream, NPError reason)
+{
+  NPError rv = NPP_DestroyStream(instance, stream, reason);
+  return rv;
+}
+
+int16 Private_HandleEvent(NPP instance, void* event)
+{
+  int16 rv = NPP_HandleEvent(instance, event);
+  return rv;
+}
+
+void Private_Print(NPP instance, NPPrint* platformPrint)
+{
+  NPP_Print(instance, platformPrint);
+}
+
+void Private_URLNotify(NPP instance, const char* url, NPReason reason, void* notifyData)
+{
+  NPP_URLNotify(instance, url, reason, notifyData);
+}
+
+jref Private_GetJavaClass(void)
+{
+  return NULL;
+}
+
+NPError Private_GetValue(NPP instance, NPPVariable variable, void *result)
+{
+  NPError rv = NPP_GetValue(instance, variable, result);
+  return rv;
+}
+
+NPError Private_SetValue(NPP instance, NPNVariable variable, void *value)
+{
+  NPError rv = NPP_SetValue(instance, variable, value);
+  return rv;
+}
+
+#endif //XP_MAC

+ 9 - 0
direct/src/plugin_npapi/nppanda3d.def

@@ -0,0 +1,9 @@
+; This file is required on Windows to export the appropriate symbols
+; from the DLL.
+
+LIBRARY   nppanda3d
+
+EXPORTS
+	NP_GetEntryPoints   @1
+	NP_Initialize       @2
+	NP_Shutdown         @3

+ 56 - 0
direct/src/plugin_npapi/nppanda3d.rc

@@ -0,0 +1,56 @@
+// This resource file is required on Windows to load the appropriate
+// text into the DLL, so Mozilla will recognize the DLL as a plugin.
+// It also defines the MIME type supported by the plugin.
+
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "winresrc.h"
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 1,0,0,1
+ PRODUCTVERSION 1,0,0,1
+ FILEFLAGSMASK 0x3fL
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x40004L
+ FILETYPE 0x2L
+ FILESUBTYPE 0x0L
+BEGIN
+    BLOCK "StringFileInfo"
+    BEGIN
+        BLOCK "040904e4"
+        BEGIN
+            VALUE "Comments", "\0"
+            VALUE "CompanyName", " \0"
+            VALUE "FileDescription", "nppanda3d\0"
+            VALUE "FileExtents", "bic\0"
+            VALUE "FileOpenName", "nppanda3d\0"
+            VALUE "FileVersion", "1, 0, 0, 1\0"
+            VALUE "InternalName", "nppanda3d\0"
+            VALUE "LegalCopyright", "Copyright © 2001\0"
+            VALUE "LegalTrademarks", "\0"
+            VALUE "MIMEType", "application/x-panda3d\0"
+            VALUE "OriginalFilename", "nppanda3d.dll\0"
+            VALUE "PrivateBuild", "\0"
+            VALUE "ProductName", "Panda3D Example Plugin for Mozilla\0"
+            VALUE "ProductVersion", "1, 0, 0, 1\0"
+            VALUE "SpecialBuild", "\0"
+        END
+    END
+    BLOCK "VarFileInfo"
+    BEGIN
+        VALUE "Translation", 0x409, 1252
+    END
+END

+ 52 - 0
direct/src/plugin_npapi/nppanda3d_common.h

@@ -0,0 +1,52 @@
+// Filename: nppanda3d_common.h
+// Created by:  drose (19Jun09)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 NPPANDA3D_COMMON
+#define NPPANDA3D_COMMON
+
+// This header file is included by all C++ files in this directory
+
+// We include this header file directly out of its source directory,
+// so we don't have to link with the library that builds it.
+#include "../plugin/p3d_plugin.h"
+
+#include <iostream>
+#include <fstream>
+#include <string>
+#include <assert.h>
+
+using namespace std;
+
+// Appears in nppanda3d_startup.cxx.
+extern ofstream log;
+
+#ifdef _WIN32
+
+// Gecko requires all these symbols to be defined.
+#define MOZILLA_STRICT_API
+#define XP_WIN
+#define _X86_
+#define _WINDOWS
+#define _USRDLL
+#define NPBASIC_EXPORTS
+
+// Panda already defines this one.
+//#define WIN32
+
+#include <windows.h>
+
+#endif  // _WIN32
+
+#endif
+

+ 4 - 0
direct/src/plugin_npapi/nppanda3d_composite1.cxx

@@ -0,0 +1,4 @@
+#include "nppanda3d_startup.cxx"
+#include "ppInstance.cxx"
+
+

+ 65 - 0
direct/src/plugin_npapi/nppanda3d_startup.cxx

@@ -0,0 +1,65 @@
+// Filename: nppanda3d_startup.cxx
+// Created by:  drose (17Jun09)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 "nppanda3d_startup.h"
+#include "ppInstance.h"
+
+// These source files are part of the Gecko SDK, sort of.  They're
+// distributed with the sample applications.  They provide a
+// higher-level wrapper around some of the NPAPI startup stuff, and
+// this appears to be the intended way to use the Gecko SDK.  It's a
+// weird system.
+#include "npplat.h"
+#include "pluginbase.h"
+#include "np_entry.cpp"
+#include "npn_gate.cpp"
+#include "npp_gate.cpp"
+
+#include "../plugin/load_plugin_src.cxx"
+
+ofstream log;
+
+NPError
+NS_PluginInitialize() {
+  log.open("c:/cygwin/home/drose/t.log");
+  log << "initializing\n" << flush;
+
+  if (!load_plugin("")) {
+    log << "couldn't load plugin\n" << flush;
+    return NPERR_INVALID_PLUGIN_ERROR;
+  }
+
+  return NPERR_NO_ERROR;
+}
+
+void
+NS_PluginShutdown() {
+  log << "shutdown\n" << flush;
+#ifdef _WIN32
+  FreeLibrary(module);
+  module = NULL;
+#endif
+}
+
+nsPluginInstanceBase *
+NS_NewPluginInstance(nsPluginCreateData *create_data) {
+  log << "new instance\n" << flush;
+  return new PPInstance(create_data);
+}
+
+void
+NS_DestroyPluginInstance(nsPluginInstanceBase *plugin) {
+  log << "destroy instance\n" << flush;
+  delete (PPInstance *)plugin;
+}

+ 22 - 0
direct/src/plugin_npapi/nppanda3d_startup.h

@@ -0,0 +1,22 @@
+// Filename: nppanda3d_startup.h
+// Created by:  drose (19Jun09)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 NPPANDA3D_STARTUP_H
+#define NPPANDA3D_STARTUP_H
+
+#include "nppanda3d_common.h"
+
+#include "../plugin/load_plugin_src.h"
+
+#endif

+ 150 - 0
direct/src/plugin_npapi/npplat.h

@@ -0,0 +1,150 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: NPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is 
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or 
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the NPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the NPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef _NPPLAT_H_
+#define _NPPLAT_H_
+
+#include "npapi.h"
+#include "npupp.h"
+
+/**************************************************/
+/*                                                */
+/*                   Windows                      */
+/*                                                */
+/**************************************************/
+#ifdef XP_WIN
+#include "windows.h"
+#endif //XP_WIN
+
+/**************************************************/
+/*                                                */
+/*                    Unix                        */
+/*                                                */
+/**************************************************/
+#ifdef XP_UNIX
+#include <stdio.h>
+#endif //XP_UNIX
+
+/**************************************************/
+/*                                                */
+/*                     Mac                        */
+/*                                                */
+/**************************************************/
+#ifdef XP_MAC
+
+#include <Processes.h>
+#include <Gestalt.h>
+#include <CodeFragments.h>
+#include <Timer.h>
+#include <Resources.h>
+#include <ToolUtils.h>
+
+#include "jri.h"
+
+// The Mixed Mode procInfos defined in npupp.h assume Think C-
+// style calling conventions.  These conventions are used by
+// Metrowerks with the exception of pointer return types, which
+// in Metrowerks 68K are returned in A0, instead of the standard
+// D0. Thus, since NPN_MemAlloc and NPN_UserAgent return pointers,
+// Mixed Mode will return the values to a 68K plugin in D0, but 
+// a 68K plugin compiled by Metrowerks will expect the result in
+// A0.  The following pragma forces Metrowerks to use D0 instead.
+//
+#ifdef __MWERKS__
+#ifndef powerc
+#pragma pointers_in_D0
+#endif
+#endif
+
+#ifdef __MWERKS__
+#ifndef powerc
+#pragma pointers_in_A0
+#endif
+#endif
+
+// The following fix for static initializers (which fixes a preious
+// incompatibility with some parts of PowerPlant, was submitted by 
+// Jan Ulbrich.
+#ifdef __MWERKS__
+	#ifdef __cplusplus
+	extern "C" {
+	#endif
+		#ifndef powerc
+			extern void	__InitCode__(void);
+		#else
+			extern void __sinit(void);
+			#define __InitCode__ __sinit
+		#endif
+		extern void	__destroy_global_chain(void);
+	#ifdef __cplusplus
+	}
+	#endif // __cplusplus
+#endif // __MWERKS__
+
+// Wrapper functions for all calls from Netscape to the plugin.
+// These functions let the plugin developer just create the APIs
+// as documented and defined in npapi.h, without needing to 
+// install those functions in the function table or worry about
+// setting up globals for 68K plugins.
+NPError Private_Initialize(void);
+void    Private_Shutdown(void);
+NPError Private_New(NPMIMEType pluginType, NPP instance, uint16 mode, int16 argc, char* argn[], char* argv[], NPSavedData* saved);
+NPError Private_Destroy(NPP instance, NPSavedData** save);
+NPError Private_SetWindow(NPP instance, NPWindow* window);
+NPError Private_NewStream(NPP instance, NPMIMEType type, NPStream* stream, NPBool seekable, uint16* stype);
+NPError Private_DestroyStream(NPP instance, NPStream* stream, NPError reason);
+int32   Private_WriteReady(NPP instance, NPStream* stream);
+int32   Private_Write(NPP instance, NPStream* stream, int32 offset, int32 len, void* buffer);
+void    Private_StreamAsFile(NPP instance, NPStream* stream, const char* fname);
+void    Private_Print(NPP instance, NPPrint* platformPrint);
+int16   Private_HandleEvent(NPP instance, void* event);
+void    Private_URLNotify(NPP instance, const char* url, NPReason reason, void* notifyData);
+jref    Private_GetJavaClass(void);
+NPError Private_GetValue(NPP instance, NPPVariable variable, void *result);
+NPError Private_SetValue(NPP instance, NPNVariable variable, void *value);
+
+#endif //XP_MAC
+
+#ifndef HIBYTE
+#define HIBYTE(i) (i >> 8)
+#endif
+
+#ifndef LOBYTE
+#define LOBYTE(i) (i & 0xff)
+#endif
+
+#endif //_NPPLAT_H_

+ 96 - 0
direct/src/plugin_npapi/pluginbase.h

@@ -0,0 +1,96 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: NPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is 
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or 
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the NPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the NPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef __PLUGININSTANCEBASE_H__
+#define __PLUGININSTANCEBASE_H__
+
+#include "npplat.h"
+
+struct nsPluginCreateData
+{
+  NPP instance;
+  NPMIMEType type; 
+  uint16 mode; 
+  int16 argc; 
+  char** argn; 
+  char** argv; 
+  NPSavedData* saved;
+};
+
+class nsPluginInstanceBase
+{
+public:
+  // these three methods must be implemented in the derived
+  // class platform specific way
+  virtual NPBool init(NPWindow* aWindow) = 0;
+  virtual void shut() = 0;
+  virtual NPBool isInitialized() = 0;
+
+  // implement all or part of those methods in the derived 
+  // class as needed
+  virtual NPError SetWindow(NPWindow* pNPWindow)                    { return NPERR_NO_ERROR; }
+  virtual NPError NewStream(NPMIMEType type, NPStream* stream, 
+                            NPBool seekable, uint16* stype)         { return NPERR_NO_ERROR; }
+  virtual NPError DestroyStream(NPStream *stream, NPError reason)   { return NPERR_NO_ERROR; }
+  virtual void    StreamAsFile(NPStream* stream, const char* fname) { return; }
+  virtual int32   WriteReady(NPStream *stream)                      { return 0x0fffffff; }
+  virtual int32   Write(NPStream *stream, int32 offset, 
+                        int32 len, void *buffer)                    { return len; }
+  virtual void    Print(NPPrint* printInfo)                         { return; }
+  virtual uint16  HandleEvent(void* event)                          { return 0; }
+  virtual void    URLNotify(const char* url, NPReason reason, 
+                            void* notifyData)                       { return; }
+  virtual NPError GetValue(NPPVariable variable, void *value)       { return NPERR_NO_ERROR; }
+  virtual NPError SetValue(NPNVariable variable, void *value)       { return NPERR_NO_ERROR; }
+};
+
+// functions that should be implemented for each specific plugin
+
+// creation and destruction of the object of the derived class
+nsPluginInstanceBase * NS_NewPluginInstance(nsPluginCreateData * aCreateDataStruct);
+void NS_DestroyPluginInstance(nsPluginInstanceBase * aPlugin);
+
+// global plugin initialization and shutdown
+NPError NS_PluginInitialize();
+void NS_PluginShutdown();
+
+#ifdef XP_UNIX
+// global to get plugins name & description 
+NPError NS_PluginGetValue(NPPVariable aVariable, void *aValue);
+#endif
+
+#endif // __PLUGININSTANCEBASE_H__

+ 14 - 0
direct/src/plugin_npapi/ppInstance.I

@@ -0,0 +1,14 @@
+// Filename: ppInstance.I
+// Created by:  drose (19Jun09)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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."
+//
+////////////////////////////////////////////////////////////////////
+

+ 136 - 0
direct/src/plugin_npapi/ppInstance.cxx

@@ -0,0 +1,136 @@
+// Filename: ppInstance.cxx
+// Created by:  drose (19Jun09)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 "ppInstance.h"
+
+////////////////////////////////////////////////////////////////////
+//     Function: PPInstance::Constructor
+//       Access: Public
+//  Description: Creates a new instance of a Panda3D plugin window.
+//               The create_data structure is supplied from NPAPI, and
+//               defines the initial parameters specified in the HTML
+//               document.
+////////////////////////////////////////////////////////////////////
+PPInstance::
+PPInstance(nsPluginCreateData *create_data) {
+  log << "constructing " << this << "\n" << flush;
+  _inst = NULL;
+
+  log << "  instance = " << create_data->instance
+      << "\n  type = " << create_data->type
+      << "\n  mode = " << create_data->mode << "\n";
+
+  // Copy the tokens from the create_data structure.
+  _tokens.reserve(create_data->argc);
+  for (int i = 0; i < create_data->argc; ++i) {
+    P3D_token token;
+    token._keyword = strdup(create_data->argn[i]);
+    token._value = strdup(create_data->argv[i]);
+    log << " " << i << ": " << token._keyword << " = " << token._value << "\n";
+    _tokens.push_back(token);
+  }
+
+  _npp_mode = create_data->mode;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PPInstance::Destructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+PPInstance::
+~PPInstance() {
+  assert(_inst == NULL);
+  log << "destructing " << this << "\n" << flush;
+
+  Tokens::iterator ti;
+  for (ti = _tokens.begin(); ti != _tokens.end(); ++ti) {
+    free((char *)(*ti)._keyword);
+    free((char *)(*ti)._value);
+  }
+  _tokens.clear();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PPInstance::init
+//       Access: Public, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+NPBool PPInstance::
+init(NPWindow *window) {
+  assert(_inst == NULL);
+  log << "init, window = " << window << "\n" << flush;
+  log << " x,y = " << window->x << "," << window->y
+      << " w,h = " << window->width << "," << window->height
+      << "\n" << flush;
+
+  P3D_window_handle parent_window;
+#ifdef _WIN32
+  parent_window._hwnd = (HWND)(window->window);
+#endif
+
+  P3D_window_type window_type = P3D_WT_embedded;
+
+  const P3D_token *tokens = NULL;
+  if (!_tokens.empty()) {
+    tokens = &_tokens[0];
+  }
+
+  _inst = P3D_create_instance
+    (NULL, NULL, window_type,
+     window->x, window->y, window->width, window->height,
+     parent_window, tokens, _tokens.size());
+
+  return (_inst != NULL);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PPInstance::shut
+//       Access: Public, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void PPInstance::
+shut() {
+  assert(_inst != NULL);
+  log << "shut\n";
+  P3D_instance_finish(_inst);
+  _inst = NULL;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PPInstance::isInitialized
+//       Access: Public, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+NPBool PPInstance::
+isInitialized() {
+  return _inst != NULL;
+}
+
+/*
+  virtual NPError SetWindow(NPWindow *pNPWindow);
+  virtual NPError NewStream(NPMIMEType type, NPStream *stream, 
+                            NPBool seekable, uint16 *stype);
+  virtual NPError DestroyStream(NPStream *stream, NPError reason);
+  virtual void    StreamAsFile(NPStream *stream, const char *fname);
+  virtual int32   WriteReady(NPStream *stream);
+  virtual int32   Write(NPStream *stream, int32 offset, 
+                        int32 len, void *buffer);
+  virtual void    Print(NPPrint *printInfo);
+  virtual uint16  HandleEvent(void *event);
+  virtual void    URLNotify(const char *url, NPReason reason, 
+                            void *notifyData);
+  virtual NPError GetValue(NPPVariable variable, void *value);
+  virtual NPError SetValue(NPNVariable variable, void *value);
+*/

+ 68 - 0
direct/src/plugin_npapi/ppInstance.h

@@ -0,0 +1,68 @@
+// Filename: ppInstance.h
+// Created by:  drose (19Jun09)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 PPINSTANCE_H
+#define PPINSTANCE_H
+
+#include "nppanda3d_common.h"
+#include "pluginbase.h"
+
+#include <vector>
+
+////////////////////////////////////////////////////////////////////
+//       Class : PPInstance
+// Description : This represents a single instance of the Panda3D
+//               plugin, via the NPAPI interface.  This instance
+//               brokers the communication with the P3D Core API, as
+//               defined in the plugin directory.
+////////////////////////////////////////////////////////////////////
+class PPInstance : public nsPluginInstanceBase {
+public:
+  PPInstance(nsPluginCreateData *create_data);
+  ~PPInstance();
+
+  // Methods inherited from base class
+
+  virtual NPBool init(NPWindow *aWindow);
+  virtual void shut();
+  virtual NPBool isInitialized();
+
+  /*
+  virtual NPError SetWindow(NPWindow *pNPWindow);
+  virtual NPError NewStream(NPMIMEType type, NPStream *stream, 
+                            NPBool seekable, uint16 *stype);
+  virtual NPError DestroyStream(NPStream *stream, NPError reason);
+  virtual void    StreamAsFile(NPStream *stream, const char *fname);
+  virtual int32   WriteReady(NPStream *stream);
+  virtual int32   Write(NPStream *stream, int32 offset, 
+                        int32 len, void *buffer);
+  virtual void    Print(NPPrint *printInfo);
+  virtual uint16  HandleEvent(void *event);
+  virtual void    URLNotify(const char *url, NPReason reason, 
+                            void *notifyData);
+  virtual NPError GetValue(NPPVariable variable, void *value);
+  virtual NPError SetValue(NPNVariable variable, void *value);
+  */
+
+private:
+  typedef vector<P3D_token> Tokens;
+  Tokens _tokens;
+  int _npp_mode;
+
+  P3D_instance *_inst;
+};
+
+#include "ppInstance.I"
+
+#endif

+ 24 - 0
direct/src/plugin_standalone/Sources.pp

@@ -0,0 +1,24 @@
+// This directory is still experimental.  Define HAVE_P3D_PLUGIN in
+// your Config.pp to build it.
+#define BUILD_DIRECTORY $[and $[HAVE_P3D_PLUGIN],$[HAVE_OPENSSL],$[HAVE_ZLIB]]
+
+#begin bin_target
+  #define USE_PACKAGES openssl zlib
+  #define TARGET panda3d
+
+  #define OTHER_LIBS \
+    prc:c dtoolutil:c dtoolbase:c dtool:m \
+    interrogatedb:c dconfig:c dtoolconfig:m \
+    express:c downloader:c pandaexpress:m \
+    pstatclient:c pandabase:c linmath:c putil:c \
+    pipeline:c panda:m \
+    pystub
+
+  #define OSX_SYS_FRAMEWORKS Foundation AppKit
+
+  #define SOURCES \
+    panda3d.cxx
+
+  #define WIN_SYS_LIBS user32.lib gdi32.lib shell32.lib
+
+#end bin_target

+ 42 - 112
direct/src/plugin/panda3d.cxx → direct/src/plugin_standalone/panda3d.cxx

@@ -30,11 +30,14 @@
 
 #include "httpClient.h"
 #include "httpChannel.h"
-#include "Ramfile.h"
+#include "ramfile.h"
 #include "thread.h"
-#include "p3d_plugin.h"
 #include "pset.h"
 
+#include "../plugin/p3d_plugin.h"
+#include "../plugin/load_plugin_src.h"
+#include "../plugin/load_plugin_src.cxx"
+
 #ifndef HAVE_GETOPT
   #include "gnu_getopt.h"
 #else
@@ -43,28 +46,6 @@
   #endif
 #endif
 
-#ifdef _WIN32
-static const string dll_ext = ".dll";
-#elif defined(__APPLE__)
-static const string dll_ext = ".dylib";
-#else
-static const string dll_ext = ".so";
-#endif
-
-static const string default_plugin_filename = "libp3d_plugin";
-
-P3D_initialize_func *P3D_initialize;
-P3D_free_string_func *P3D_free_string;
-P3D_create_instance_func *P3D_create_instance;
-P3D_instance_finish_func *P3D_instance_finish;
-P3D_instance_has_property_func *P3D_instance_has_property;
-P3D_instance_get_property_func *P3D_instance_get_property;
-P3D_instance_set_property_func *P3D_instance_set_property;
-P3D_instance_get_request_func *P3D_instance_get_request;
-P3D_check_request_func *P3D_check_request;
-P3D_request_finish_func *P3D_request_finish;
-P3D_instance_feed_url_stream_func *P3D_instance_feed_url_stream;
-
 typedef pset<P3D_instance *> Instances;
 Instances _instances;
 
@@ -154,81 +135,6 @@ thread_main() {
      bytes_sent, NULL, 0);
 }
 
-bool
-load_plugin(const string &p3d_plugin_filename) {
-  string filename = p3d_plugin_filename;
-  if (filename.empty()) {
-    // Look for the plugin along the path.
-    filename = default_plugin_filename + dll_ext;
-  }
-
-#ifdef _WIN32
-  HMODULE module = LoadLibrary(filename.c_str());
-  if (module == NULL) {
-    // Couldn't load the DLL.
-    return false;
-  }
-
-  // Now get all of the function pointers.
-  P3D_initialize = (P3D_initialize_func *)GetProcAddress(module, "P3D_initialize");  
-  P3D_free_string = (P3D_free_string_func *)GetProcAddress(module, "P3D_free_string");  
-  P3D_create_instance = (P3D_create_instance_func *)GetProcAddress(module, "P3D_create_instance");  
-  P3D_instance_finish = (P3D_instance_finish_func *)GetProcAddress(module, "P3D_instance_finish");  
-  P3D_instance_has_property = (P3D_instance_has_property_func *)GetProcAddress(module, "P3D_instance_has_property");  
-  P3D_instance_get_property = (P3D_instance_get_property_func *)GetProcAddress(module, "P3D_instance_get_property");  
-  P3D_instance_set_property = (P3D_instance_set_property_func *)GetProcAddress(module, "P3D_instance_set_property");  
-  P3D_instance_get_request = (P3D_instance_get_request_func *)GetProcAddress(module, "P3D_instance_get_request");  
-  P3D_check_request = (P3D_check_request_func *)GetProcAddress(module, "P3D_check_request");  
-  P3D_request_finish = (P3D_request_finish_func *)GetProcAddress(module, "P3D_request_finish");  
-  P3D_instance_feed_url_stream = (P3D_instance_feed_url_stream_func *)GetProcAddress(module, "P3D_instance_feed_url_stream");  
-
-#else  // _WIN32
-  // Posix case.
-  void *module = dlopen(filename.c_str(), RTLD_NOW | RTLD_LOCAL);
-  if (module == NULL) {
-    // Couldn't load the .so.
-    return false;
-  }
-  
-  // Now get all of the function pointers.
-  P3D_initialize = (P3D_initialize_func *)dlsym(module, "P3D_initialize");  
-  P3D_free_string = (P3D_free_string_func *)dlsym(module, "P3D_free_string");  
-  P3D_create_instance = (P3D_create_instance_func *)dlsym(module, "P3D_create_instance");  
-  P3D_instance_finish = (P3D_instance_finish_func *)dlsym(module, "P3D_instance_finish");  
-  P3D_instance_has_property = (P3D_instance_has_property_func *)dlsym(module, "P3D_instance_has_property");  
-  P3D_instance_get_property = (P3D_instance_get_property_func *)dlsym(module, "P3D_instance_get_property");  
-  P3D_instance_set_property = (P3D_instance_set_property_func *)dlsym(module, "P3D_instance_set_property");  
-  P3D_instance_get_request = (P3D_instance_get_request_func *)dlsym(module, "P3D_instance_get_request");  
-  P3D_check_request = (P3D_check_request_func *)dlsym(module, "P3D_check_request");  
-  P3D_request_finish = (P3D_request_finish_func *)dlsym(module, "P3D_request_finish");  
-  P3D_instance_feed_url_stream = (P3D_instance_feed_url_stream_func *)dlsym(module, "P3D_instance_feed_url_stream");  
-
-#endif  // _WIN32
-
-  // Ensure that all of the function pointers have been found.
-  if (P3D_initialize == NULL ||
-      P3D_free_string == NULL ||
-      P3D_create_instance == NULL ||
-      P3D_instance_finish == NULL ||
-      P3D_instance_has_property == NULL ||
-      P3D_instance_get_property == NULL ||
-      P3D_instance_set_property == NULL ||
-      P3D_instance_get_request == NULL ||
-      P3D_check_request == NULL ||
-      P3D_request_finish == NULL ||
-      P3D_instance_feed_url_stream == NULL) {
-    return false;
-  }
-
-  // Successfully loaded.
-  if (!P3D_initialize(P3D_API_VERSION)) {
-    // Oops, failure to initialize.
-    return false;
-  }
-
-  return true;
-}
-
 void
 handle_request(P3D_request *request) {
   bool handled = false;
@@ -335,6 +241,35 @@ make_parent_window(P3D_window_handle &parent_window,
 
 #endif  // __APPLE__
 
+P3D_instance *
+create_instance(const string &arg, P3D_window_type window_type,
+                int win_x, int win_y, int win_width, int win_height,
+                P3D_window_handle parent_window,
+                const string &output_filename) {
+
+  P3D_token tokens[] = {
+    { "output_filename", output_filename.c_str() },
+    { "src", arg.c_str() },
+  };
+  int num_tokens = sizeof(tokens) / sizeof(P3D_token);
+
+  // If the supplied parameter name is a real file, pass it in on the
+  // parameter list.  Otherwise, assume it's a URL and let the plugin
+  // download it.
+  Filename p3d_filename = Filename::from_os_specific(arg);
+  string os_p3d_filename;
+  if (p3d_filename.exists()) {
+    os_p3d_filename = p3d_filename.to_os_specific();
+  } 
+
+  P3D_instance *inst = P3D_create_instance
+    (NULL, os_p3d_filename.c_str(), 
+     window_type, win_x, win_y, win_width, win_height, parent_window,
+     tokens, num_tokens);
+
+  return inst;
+}
+
 void
 usage() {
   cerr
@@ -473,11 +408,6 @@ int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int) {
 
   int num_instances = argc - 1;
 
-  P3D_token tokens[] = {
-    { "output_filename", output_filename.c_str() },
-  };
-  int num_tokens = sizeof(tokens) / sizeof(P3D_token);
-
   P3D_window_handle parent_window;
   if (window_type == P3D_WT_embedded) {
     // The user asked for an embedded window.  Create a toplevel
@@ -518,10 +448,10 @@ int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int) {
         int inst_x = win_x + xi * inst_width;
         int inst_y = win_y + yi * inst_height;
 
-        P3D_instance *inst = P3D_create_instance
-          (NULL, argv[i + 1], 
-           P3D_WT_embedded, inst_x, inst_y, inst_width, inst_height, parent_window,
-           tokens, num_tokens);
+        P3D_instance *inst = create_instance
+          (argv[i + 1], P3D_WT_embedded, 
+           inst_x, inst_y, inst_width, inst_height, parent_window,
+           output_filename);
         _instances.insert(inst);
       }
     }
@@ -529,10 +459,10 @@ int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int) {
   } else {
     // Not an embedded window.  Create each window with the same parameters.
     for (int i = 0; i < num_instances; ++i) {
-      P3D_instance *inst = P3D_create_instance
-        (NULL, argv[i + 1], 
-         window_type, win_x, win_y, win_width, win_height, parent_window,
-         tokens, num_tokens);
+      P3D_instance *inst = create_instance
+        (argv[i + 1], window_type, 
+         win_x, win_y, win_width, win_height, parent_window,
+         output_filename);
       _instances.insert(inst);
     }
   }

+ 57 - 8
direct/src/showbase/RunAppMF.py

@@ -13,7 +13,7 @@ Also see MakeAppMF.py.
 
 import sys
 from direct.showbase import VFSImporter
-from pandac.PandaModules import VirtualFileSystem, Filename, Multifile, loadPrcFileData, getModelPath
+from pandac.PandaModules import VirtualFileSystem, Filename, Multifile, loadPrcFileData, getModelPath, HTTPClient
 from direct.stdpy import file
 from direct.task.TaskManagerGlobal import taskMgr
 import os
@@ -91,22 +91,45 @@ def initPackedAppEnvironment():
     # we plan to mount there.
     vfs.chdir(MultifileRoot)
 
-def runPackedApp(args):
-    if not args:
-        raise ArgumentError, "No Panda app specified.  Use:\npython RunAppMF.py app.mf"
+def runPackedApp(p3dFilename, tokens = []):
+    tokenDict = dict(tokens)
+    fname = Filename.fromOsSpecific(p3dFilename)
+    if not p3dFilename:
+        # If we didn't get a literal filename, we have to download it
+        # from the URL.  TODO: make this a smarter temporary filename?
+        fname = Filename.temporary('', 'p3d_')
+        fname.setExtension('p3d')
+        p3dFilename = fname.toOsSpecific()
+        src = tokenDict.get('src', None)
+        if not src:
+            raise ArgumentError, "No Panda app specified."
+            
+        http = HTTPClient.getGlobalPtr()
+        hc = http.getDocument(src)
+        if not hc.downloadToFile(fname):
+            fname.unlink()
+            raise ArgumentError, "Couldn't download %s" % (src)
+
+        # Set a hook on sys.exit to delete the temporary file.
+        oldexitfunc = getattr(sys, 'exitfunc', None)
+        def deleteTempFile(fname = fname, oldexitfunc = oldexitfunc):
+            fname.unlink()
+            if oldexitfunc:
+                oldexitfunc()
+
+        sys.exitfunc = deleteTempFile
 
     vfs = VirtualFileSystem.getGlobalPtr()
 
-    fname = Filename.fromOsSpecific(args[0])
     if not vfs.exists(fname):
-        raise ArgumentError, "No such file: %s" % (args[0])
+        raise ArgumentError, "No such file: %s" % (p3dFilename)
 
     fname.makeAbsolute()
     initPackedAppEnvironment()
 
     mf = Multifile()
     if not mf.openRead(fname):
-        raise ArgumentError, "Not a Panda Multifile: %s" % (args[0])
+        raise ArgumentError, "Not a Panda Multifile: %s" % (p3dFilename)
 
     # Mount the Multifile under /mf, by convention.
     vfs.mount(mf, MultifileRoot, vfs.MFReadOnly)
@@ -150,9 +173,35 @@ def setupWindow(windowType, x, y, width, height, parent):
 
     loadPrcFileData("setupWindow", data)
 
+def parseSysArgs():
+    """ Converts sys.argv into (p3dFilename, tokens). """
+    if len(sys.argv) < 2 or not sys.argv[1]:
+        raise ArgumentError, "No Panda app specified.  Use:\npython RunAppMF.py app.mf"
+
+    tokens = []
+    for token in sys.argv[2:]:
+        if '=' in token:
+            keyword, value = token.split('=', 1)
+        else:
+            keyword = token
+            value = ''
+        tokens.append((keyword.lower(), value))
+
+    p3dFilename = Filename.fromOsSpecific(sys.argv[1])
+    osFilename = p3dFilename.toOsSpecific()
+    if not p3dFilename.exists():
+        # If the filename doesn't exist, it must be a URL.
+        osFilename = ''
+        if 'src' not in dict(tokens):
+            tokens.append(('src', sys.argv[1]))
+
+    return (osFilename, tokens)
+        
+        
+
 if __name__ == '__main__':
     try:
-        runPackedApp(sys.argv[1:])
+        runPackedApp(*parseSysArgs())
     except ArgumentError, e:
         print e.args[0]
         sys.exit(1)

+ 12 - 0
direct/src/showbase/ShowBase.py

@@ -102,6 +102,11 @@ class ShowBase(DirectObject.DirectObject):
         # the program by closing the main window.
         self.exitFunc = None
 
+        # Add final-exit callbacks to this list.  These will be called
+        # when sys.exit() is called, after Panda has unloaded, and
+        # just before Python is about to shut down.
+        self.finalExitCallbacks = []
+
         Task.TaskManager.taskTimerVerbose = self.config.GetBool('task-timer-verbose', 0)
         Task.TaskManager.extendedExceptions = self.config.GetBool('extended-exceptions', 0)
         Task.TaskManager.pStatsTasks = self.config.GetBool('pstats-tasks', 0)
@@ -444,6 +449,13 @@ class ShowBase(DirectObject.DirectObject):
             del self.winList
             del self.pipe
 
+        vfs = VirtualFileSystem.getGlobalPtr()
+        vfs.unmountAll()
+
+        for cb in self.finalExitCallbacks:
+            cb()
+        
+
     def exitfunc(self):
         """
         This should be assigned to sys.exitfunc to be called just