Browse Source

inbound scripting controls

David Rose 16 years ago
parent
commit
ed037be160

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

@@ -115,6 +115,8 @@ find_extension_dot(const string &filename) {
   return string::npos;
   return string::npos;
 }
 }
 
 
+// Forward reference for function defined below.
+static void unload_dso();
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: load_plugin
 //     Function: load_plugin
@@ -265,8 +267,8 @@ load_plugin(const string &p3d_plugin_filename) {
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: unload_plugin
 //     Function: unload_plugin
-//  Description: Removes the plugin from memory space and clears all
-//               of the pointers.
+//  Description: Calls finalize, then removes the plugin from memory
+//               space and clears all of the pointers.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void
 void
 unload_plugin() {
 unload_plugin() {
@@ -274,8 +276,24 @@ unload_plugin() {
     return;
     return;
   }
   }
 
 
+  cerr << "unload_plugin called\n";
+
   P3D_finalize();
   P3D_finalize();
+  unload_dso();
+}
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: unload_dso
+//  Description: Removes the plugin from memory space and clears all
+//               of the pointers.  This is only intended to be called
+//               by load_plugin(), above, in the specific case that
+//               the plugin loaded but could not successfully
+//               initialize itself.  All user code should call
+//               unload_plugin(), above, which first calls
+//               P3D_finalize().
+////////////////////////////////////////////////////////////////////
+static void
+unload_dso() {
 #ifdef _WIN32
 #ifdef _WIN32
   assert(module != NULL);
   assert(module != NULL);
   FreeLibrary(module);
   FreeLibrary(module);

+ 13 - 0
direct/src/plugin/p3dInstance.I

@@ -71,6 +71,19 @@ get_python_version() const {
   return _python_version;
   return _python_version;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: P3DInstance::get_request_ready_func
+//       Access: Public
+//  Description: Returns a pointer to the asynchronous notification
+//               function that was passed to the constructor, if any,
+//               or NULL if asynchronous notifications are not
+//               required.
+////////////////////////////////////////////////////////////////////
+inline P3D_request_ready_func *P3DInstance::
+get_request_ready_func() const {
+  return _func;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: P3DInstance::is_started
 //     Function: P3DInstance::is_started
 //       Access: Public
 //       Access: Public

+ 7 - 9
direct/src/plugin/p3dInstance.cxx

@@ -88,6 +88,8 @@ P3DInstance::
 
 
   // TODO: Is it possible for someone to delete an instance while a
   // TODO: Is it possible for someone to delete an instance while a
   // download is still running?  Who will crash when this happens?
   // download is still running?  Who will crash when this happens?
+
+  nout << "deleting instance " << this << "\n" << flush;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -169,6 +171,7 @@ set_wparams(const P3DWindowParams &wparams) {
 P3DObject *P3DInstance::
 P3DObject *P3DInstance::
 get_script_object() const {
 get_script_object() const {
   assert(_session != NULL);
   assert(_session != NULL);
+  nout << "Called P3DInstance::get_script_object()\n";
 
 
   TiXmlDocument *doc = new TiXmlDocument;
   TiXmlDocument *doc = new TiXmlDocument;
   TiXmlDeclaration *decl = new TiXmlDeclaration("1.0", "utf-8", "");
   TiXmlDeclaration *decl = new TiXmlDeclaration("1.0", "utf-8", "");
@@ -239,10 +242,10 @@ get_request() {
       // If we received a notify request, process the notification
       // If we received a notify request, process the notification
       // immediately--it might be interesting to this instance.
       // immediately--it might be interesting to this instance.
       const char *message = result->_request._notify._message;
       const char *message = result->_request._notify._message;
-      if (strcmp(message, "window_opened") == 0) {
+      if (strcmp(message, "onwindowopen") == 0) {
         // The process told us that it just succesfully opened its
         // The process told us that it just succesfully opened its
         // window.
         // window.
-        nout << "Instance " << this << " got window_opened\n" << flush;
+        nout << "Instance " << this << " got onwindowopen\n" << flush;
         _instance_window_opened = true;
         _instance_window_opened = true;
         if (_splash_window != NULL) {
         if (_splash_window != NULL) {
           nout << "Deleting splash window\n" << flush;
           nout << "Deleting splash window\n" << flush;
@@ -273,14 +276,9 @@ add_request(P3D_request *request) {
   _request_pending = true;
   _request_pending = true;
   RELEASE_LOCK(_request_lock);
   RELEASE_LOCK(_request_lock);
 
 
-  // Asynchronous notification for anyone who cares.
-  if (_func != NULL) {
-    _func(this);
-  }
-
-  // Synchronous notification for pollers.
+  // Tell the world we've got a new request.
   P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
   P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
-  inst_mgr->signal_request_ready();
+  inst_mgr->signal_request_ready(this);
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////

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

@@ -64,6 +64,8 @@ public:
   inline const string &get_session_key() const;
   inline const string &get_session_key() const;
   inline const string &get_python_version() const;
   inline const string &get_python_version() const;
 
 
+  inline P3D_request_ready_func *get_request_ready_func() const;
+
   void add_package(P3DPackage *package);
   void add_package(P3DPackage *package);
   
   
   void start_download(P3DDownload *download);
   void start_download(P3DDownload *download);

+ 95 - 5
direct/src/plugin/p3dInstanceManager.cxx

@@ -38,6 +38,10 @@ P3DInstanceManager() {
   _is_initialized = false;
   _is_initialized = false;
   _unique_session_index = 0;
   _unique_session_index = 0;
 
 
+  _notify_thread_continue = false;
+  _started_notify_thread = false;
+  INIT_THREAD(_notify_thread);
+
 #ifdef _WIN32
 #ifdef _WIN32
   // Ensure the appropriate Windows common controls are available to
   // Ensure the appropriate Windows common controls are available to
   // this application.
   // this application.
@@ -55,11 +59,19 @@ P3DInstanceManager() {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 P3DInstanceManager::
 P3DInstanceManager::
 ~P3DInstanceManager() {
 ~P3DInstanceManager() {
-  // Actually, this destructor is never called, since this is a global
-  // object that never gets deleted.
+  nout << "~P3DInstanceManager\n" << flush;
+  if (_started_notify_thread) {
+    _notify_ready.acquire();
+    _notify_thread_continue = false;
+    _notify_ready.notify();
+    _notify_ready.release();
+    JOIN_THREAD(_notify_thread);
+    _started_notify_thread = false;
+  }
 
 
   assert(_instances.empty());
   assert(_instances.empty());
   assert(_sessions.empty());
   assert(_sessions.empty());
+  nout << "done ~P3DInstanceManager\n" << flush;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -254,11 +266,29 @@ get_unique_session_index() {
 //     Function: P3DInstanceManager::signal_request_ready
 //     Function: P3DInstanceManager::signal_request_ready
 //       Access: Public
 //       Access: Public
 //  Description: May be called in any thread to indicate that a new
 //  Description: May be called in any thread to indicate that a new
-//               P3D_request is available in some instance.  This will
-//               wake up a sleeping wait_request() call, if any.
+//               P3D_request is available in the indicated instance.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void P3DInstanceManager::
 void P3DInstanceManager::
-signal_request_ready() {
+signal_request_ready(P3DInstance *inst) {
+  if (inst->get_request_ready_func() != NULL) {
+    // This instance requires asynchronous notifications of requests.
+    // Thus, we should tell the notify thread to wake up and make the
+    // callback.
+    _notify_ready.acquire();
+    _notify_instances.push_back(inst);
+    _notify_ready.notify();
+    _notify_ready.release();
+
+    // Oh, and we should spawn the thread if we haven't already.
+    if (!_started_notify_thread) {
+      _notify_thread_continue = true;
+      SPAWN_THREAD(_notify_thread, nt_thread_run, this);
+      _started_notify_thread = true;
+    }
+  }
+
+  // Then, wake up the main thread, in case it's sleeping on
+  // wait_request().
   _request_ready.acquire();
   _request_ready.acquire();
   _request_ready.notify();
   _request_ready.notify();
   _request_ready.release();
   _request_ready.release();
@@ -287,3 +317,63 @@ get_global_ptr() {
   }
   }
   return _global_ptr;
   return _global_ptr;
 }
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: P3DInstanceManager::delete_global_ptr
+//       Access: Public, Static
+//  Description: This is called only at plugin shutdown time; it
+//               deletes the global instance manager pointer and
+//               clears it to NULL.
+////////////////////////////////////////////////////////////////////
+void P3DInstanceManager::
+delete_global_ptr() {
+  if (_global_ptr != NULL) {
+    delete _global_ptr;
+    _global_ptr = NULL;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: P3DInstanceManager::nt_thread_run
+//       Access: Private
+//  Description: The main function for the notify thread.
+////////////////////////////////////////////////////////////////////
+void P3DInstanceManager::
+nt_thread_run() {
+  // The notify thread exists because we need to be able to send
+  // asynchronous notifications of request events.  These request
+  // events were detected in the various read threads associated with
+  // each session, but we can't call back into the host space from the
+  // read thread, since if the host immediately response to a callback
+  // by calling back into the p3d_plugin space, now we have our read
+  // thread doing stuff in here that's not related to the read thread.
+  // Even worse, some of the things it might need to do might require
+  // a separate read thread to be running!
+
+  _notify_ready.acquire();
+  while (_notify_thread_continue) {
+    NotifyInstances instances;
+    while (!_notify_instances.empty()) {
+      instances.clear();
+      instances.swap(_notify_instances);
+
+      // Go ahead and drop the lock while we make the callback, to
+      // reduce the risk of deadlock.  We don't want to be holding any
+      // locks when we call into client code.
+      _notify_ready.release();
+      NotifyInstances::iterator ni;
+      for (ni = instances.begin(); ni != instances.end(); ++ni) {
+        // TODO: a race condition here when instances are deleted.
+        P3DInstance *inst = (*ni);
+        P3D_request_ready_func *func = inst->get_request_ready_func();
+        assert(inst != NULL);
+        (*func)(inst);
+      }
+      _notify_ready.acquire();
+    }
+
+    _notify_ready.wait();
+  }
+  _notify_ready.release();
+  nout << "exiting nt_thread_run\n" << flush;
+}

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

@@ -20,6 +20,7 @@
 
 
 #include <set>
 #include <set>
 #include <map>
 #include <map>
+#include <vector>
 
 
 class P3DInstance;
 class P3DInstance;
 class P3DSession;
 class P3DSession;
@@ -61,11 +62,19 @@ public:
   inline int get_num_instances() const;
   inline int get_num_instances() const;
 
 
   int get_unique_session_index();
   int get_unique_session_index();
-  void signal_request_ready();
+  void signal_request_ready(P3DInstance *inst);
 
 
   P3D_class_definition *make_class_definition() const;
   P3D_class_definition *make_class_definition() const;
 
 
   static P3DInstanceManager *get_global_ptr();
   static P3DInstanceManager *get_global_ptr();
+  static void delete_global_ptr();
+
+private:
+  // The notify thread.  This thread runs only for the purpose of
+  // generating asynchronous notifications of requests, to callers who
+  // ask for it.
+  THREAD_CALLBACK_DECLARATION(P3DInstanceManager, nt_thread_run);
+  void nt_thread_run();
 
 
 private:
 private:
   bool _is_initialized;
   bool _is_initialized;
@@ -84,7 +93,20 @@ private:
 
 
   int _unique_session_index;
   int _unique_session_index;
 
 
+  // This condition var is waited on the main thread and signaled in a
+  // sub-thread when new request notices arrive.
   P3DConditionVar _request_ready;
   P3DConditionVar _request_ready;
+
+  // We may need a thread to send async request notices to callers.
+  bool _notify_thread_continue;
+  bool _started_notify_thread;
+  THREAD _notify_thread;
+  // This queue of instances that need to send notifications is
+  // protected by _notify_ready's mutex.
+  typedef vector<P3DInstance *> NotifyInstances;
+  NotifyInstances _notify_instances;
+  P3DConditionVar _notify_ready;
+
   static P3DInstanceManager *_global_ptr;
   static P3DInstanceManager *_global_ptr;
 };
 };
 
 

+ 4 - 46
direct/src/plugin/p3dPythonRun.cxx

@@ -30,6 +30,7 @@ P3DPythonRun(int argc, char *argv[]) {
   _read_thread_continue = false;
   _read_thread_continue = false;
   _program_continue = true;
   _program_continue = true;
   INIT_LOCK(_commands_lock);
   INIT_LOCK(_commands_lock);
+  INIT_THREAD(_read_thread);
 
 
   _program_name = argv[0];
   _program_name = argv[0];
   _py_argc = 1;
   _py_argc = 1;
@@ -508,15 +509,7 @@ spawn_read_thread() {
   // anonymous pipe in Windows).
   // anonymous pipe in Windows).
 
 
   _read_thread_continue = true;
   _read_thread_continue = true;
-#ifdef _WIN32
-  _read_thread = CreateThread(NULL, 0, &win_rt_thread_run, this, 0, NULL);
-#else
-  pthread_attr_t attr;
-  pthread_attr_init(&attr);
-  pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM);
-  pthread_create(&_read_thread, &attr, &posix_rt_thread_run, (void *)this);
-  pthread_attr_destroy(&attr);
-#endif
+  SPAWN_THREAD(_read_thread, rt_thread_run, this);
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -529,16 +522,8 @@ join_read_thread() {
   nout << "waiting for thread\n";
   nout << "waiting for thread\n";
   _read_thread_continue = false;
   _read_thread_continue = false;
   _pipe_read.close();
   _pipe_read.close();
-  
-#ifdef _WIN32
-  assert(_read_thread != NULL);
-  WaitForSingleObject(_read_thread, INFINITE);
-  CloseHandle(_read_thread);
-  _read_thread = NULL;
-#else
-  void *return_val;
-  pthread_join(_read_thread, &return_val);
-#endif
+
+  JOIN_THREAD(_read_thread);
   nout << "done waiting for thread\n";
   nout << "done waiting for thread\n";
 }
 }
 
 
@@ -919,33 +904,6 @@ rt_thread_run() {
   }
   }
 }
 }
 
 
-#ifdef _WIN32
-////////////////////////////////////////////////////////////////////
-//     Function: P3DPythonRun::win_rt_thread_run
-//       Access: Private, Static
-//  Description: The Windows flavor of the thread callback function.
-////////////////////////////////////////////////////////////////////
-DWORD P3DPythonRun::
-win_rt_thread_run(LPVOID data) {
-  ((P3DPythonRun *)data)->rt_thread_run();
-  return 0;
-}
-#endif
-
-#ifndef _WIN32
-////////////////////////////////////////////////////////////////////
-//     Function: P3DPythonRun::posix_rt_thread_run
-//       Access: Private, Static
-//  Description: The Posix flavor of the thread callback function.
-////////////////////////////////////////////////////////////////////
-void *P3DPythonRun::
-posix_rt_thread_run(void *data) {
-  ((P3DPythonRun *)data)->rt_thread_run();
-  return NULL;
-}
-#endif
-
-
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: main
 //     Function: main
 //  Description: Starts the program running.
 //  Description: Starts the program running.

+ 2 - 11
direct/src/plugin/p3dPythonRun.h

@@ -95,13 +95,8 @@ private:
 
 
 private:
 private:
   // This method runs only within the read thread.
   // This method runs only within the read thread.
-
+  THREAD_CALLBACK_DECLARATION(P3DPythonRun, rt_thread_run);
   void rt_thread_run();
   void rt_thread_run();
-#ifdef _WIN32
-  static DWORD WINAPI win_rt_thread_run(LPVOID data);
-#else
-  static void *posix_rt_thread_run(void *data);
-#endif
 
 
 private:
 private:
   typedef pmap<int, P3DCInstance *> Instances;
   typedef pmap<int, P3DCInstance *> Instances;
@@ -130,11 +125,7 @@ private:
 
 
   bool _read_thread_continue;
   bool _read_thread_continue;
   bool _program_continue;
   bool _program_continue;
-#ifdef _WIN32
-  HANDLE _read_thread;
-#else
-  pthread_t _read_thread;
-#endif
+  THREAD _read_thread;
 
 
 public:
 public:
   static P3DPythonRun *_global_ptr;
   static P3DPythonRun *_global_ptr;

+ 5 - 52
direct/src/plugin/p3dSession.cxx

@@ -54,6 +54,7 @@ P3DSession(P3DInstance *inst) {
   _panda3d_callback = NULL;
   _panda3d_callback = NULL;
 
 
   INIT_LOCK(_instances_lock);
   INIT_LOCK(_instances_lock);
+  INIT_THREAD(_read_thread);
 
 
   P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
   P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
 
 
@@ -471,22 +472,8 @@ void P3DSession::
 spawn_read_thread() {
 spawn_read_thread() {
   assert(!_read_thread_continue);
   assert(!_read_thread_continue);
 
 
-  // We have to use direct OS calls to create the thread instead of
-  // Panda constructs, because it has to be an actual thread, not
-  // necessarily a Panda thread (we can't use Panda's simple threads
-  // implementation, because we can't get overlapped I/O on an
-  // anonymous pipe in Windows).
-
   _read_thread_continue = true;
   _read_thread_continue = true;
-#ifdef _WIN32
-  _read_thread = CreateThread(NULL, 0, &win_rt_thread_run, this, 0, NULL);
-#else
-  pthread_attr_t attr;
-  pthread_attr_init(&attr);
-  pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM);
-  pthread_create(&_read_thread, &attr, &posix_rt_thread_run, (void *)this);
-  pthread_attr_destroy(&attr);
-#endif
+  SPAWN_THREAD(_read_thread, rt_thread_run, this);
   _started_read_thread = true;
   _started_read_thread = true;
 }
 }
 
 
@@ -503,17 +490,8 @@ join_read_thread() {
 
 
   _read_thread_continue = false;
   _read_thread_continue = false;
   _pipe_read.close();
   _pipe_read.close();
-  
-#ifdef _WIN32
-  assert(_read_thread != NULL);
-  WaitForSingleObject(_read_thread, INFINITE);
-  CloseHandle(_read_thread);
-  _read_thread = NULL;
-#else
-  void *return_val;
-  pthread_join(_read_thread, &return_val);
-#endif
 
 
+  JOIN_THREAD(_read_thread);
   _started_read_thread = false;
   _started_read_thread = false;
 }
 }
 
 
@@ -576,6 +554,7 @@ rt_handle_request(TiXmlDocument *doc) {
 
 
   TiXmlElement *xrequest = doc->FirstChildElement("request");
   TiXmlElement *xrequest = doc->FirstChildElement("request");
   if (xrequest != (TiXmlElement *)NULL) {
   if (xrequest != (TiXmlElement *)NULL) {
+    nout << "Handling request\n" << flush;
     int instance_id;
     int instance_id;
     if (xrequest->QueryIntAttribute("instance_id", &instance_id) == TIXML_SUCCESS) {
     if (xrequest->QueryIntAttribute("instance_id", &instance_id) == TIXML_SUCCESS) {
       // Look up the particular instance this is related to.
       // Look up the particular instance this is related to.
@@ -591,6 +570,7 @@ rt_handle_request(TiXmlDocument *doc) {
       }
       }
       RELEASE_LOCK(_instances_lock);
       RELEASE_LOCK(_instances_lock);
     }
     }
+    nout << "done handling request\n" << flush;
   }
   }
 
 
   delete doc;
   delete doc;
@@ -645,33 +625,6 @@ rt_terminate() {
   }
   }
 }
 }
 
 
-#ifdef _WIN32
-////////////////////////////////////////////////////////////////////
-//     Function: P3DSession::win_rt_thread_run
-//       Access: Private, Static
-//  Description: The Windows flavor of the thread callback function.
-////////////////////////////////////////////////////////////////////
-DWORD P3DSession::
-win_rt_thread_run(LPVOID data) {
-  ((P3DSession *)data)->rt_thread_run();
-  return 0;
-}
-#endif
-
-#ifndef _WIN32
-////////////////////////////////////////////////////////////////////
-//     Function: P3DSession::posix_rt_thread_run
-//       Access: Private, Static
-//  Description: The Posix flavor of the thread callback function.
-////////////////////////////////////////////////////////////////////
-void *P3DSession::
-posix_rt_thread_run(void *data) {
-  ((P3DSession *)data)->rt_thread_run();
-  return NULL;
-}
-#endif
-
-
 #ifdef _WIN32
 #ifdef _WIN32
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: P3DSession::win_create_process
 //     Function: P3DSession::win_create_process

+ 2 - 11
direct/src/plugin/p3dSession.h

@@ -60,17 +60,12 @@ private:
 
 
 private:
 private:
   // These methods run only within the read thread.
   // These methods run only within the read thread.
+  THREAD_CALLBACK_DECLARATION(P3DSession, rt_thread_run);
   void rt_thread_run();
   void rt_thread_run();
   void rt_terminate();
   void rt_terminate();
   void rt_handle_request(TiXmlDocument *doc);
   void rt_handle_request(TiXmlDocument *doc);
   P3D_request *rt_make_p3d_request(TiXmlElement *xrequest);
   P3D_request *rt_make_p3d_request(TiXmlElement *xrequest);
 
 
-#ifdef _WIN32
-  static DWORD WINAPI win_rt_thread_run(LPVOID data);
-#else
-  static void *posix_rt_thread_run(void *data);
-#endif
-
 #ifdef _WIN32
 #ifdef _WIN32
   static HANDLE 
   static HANDLE 
   win_create_process(const string &program, const string &start_dir,
   win_create_process(const string &program, const string &start_dir,
@@ -135,11 +130,7 @@ private:
   HandleStream _pipe_write;
   HandleStream _pipe_write;
 
 
   bool _read_thread_continue;
   bool _read_thread_continue;
-#ifdef _WIN32
-  HANDLE _read_thread;
-#else
-  pthread_t _read_thread;
-#endif
+  THREAD _read_thread;
 
 
   friend class PackageCallback;
   friend class PackageCallback;
 };
 };

+ 49 - 1
direct/src/plugin/p3d_lock.h

@@ -16,17 +16,42 @@
 #define P3D_LOCK_H
 #define P3D_LOCK_H
 
 
 // Provides some simple macros that implement platform-independet
 // Provides some simple macros that implement platform-independet
-// mutex locks.
+// mutex locks, as well as platform-independent thread constructs.
 
 
 #ifdef _WIN32
 #ifdef _WIN32
 
 
 // Windows case
 // Windows case
+
+// Locks are straightforward.
 #define LOCK CRITICAL_SECTION
 #define LOCK CRITICAL_SECTION
 #define INIT_LOCK(lock) InitializeCriticalSection(&(lock))
 #define INIT_LOCK(lock) InitializeCriticalSection(&(lock))
 #define ACQUIRE_LOCK(lock) EnterCriticalSection(&(lock))
 #define ACQUIRE_LOCK(lock) EnterCriticalSection(&(lock))
 #define RELEASE_LOCK(lock) LeaveCriticalSection(&(lock))
 #define RELEASE_LOCK(lock) LeaveCriticalSection(&(lock))
 #define DESTROY_LOCK(lock) DeleteCriticalSection(&(lock))
 #define DESTROY_LOCK(lock) DeleteCriticalSection(&(lock))
 
 
+// Threads.
+#define THREAD HANDLE
+#define INIT_THREAD(thread) (thread) = NULL;
+#define SPAWN_THREAD(thread, callback_function, this) \
+  (thread) = CreateThread(NULL, 0, &win_ ## callback_function, (this), 0, NULL)
+#define JOIN_THREAD(thread) \
+  assert((thread) != NULL); \
+  WaitForSingleObject((thread), INFINITE); \
+  CloseHandle((thread)); \
+  (thread) = NULL;
+
+// Declare this macro within your class declaration.  This implements
+// the callback function wrapper necessary to hook into the above
+// SPAWN_THREAD call.  The wrapper will in turn call the method
+// function you provide.
+#define THREAD_CALLBACK_DECLARATION(class, callback_function) \
+  static DWORD class::                                        \
+  win_ ## callback_function(LPVOID data) {        \
+    ((class *)data)->callback_function();       \
+    return 0;                                   \
+  }
+
+
 #else  // _WIN32
 #else  // _WIN32
 
 
 // Posix case
 // Posix case
@@ -47,6 +72,29 @@
 #define RELEASE_LOCK(lock) pthread_mutex_unlock(&(lock))
 #define RELEASE_LOCK(lock) pthread_mutex_unlock(&(lock))
 #define DESTROY_LOCK(lock) pthread_mutex_destroy(&(lock))
 #define DESTROY_LOCK(lock) pthread_mutex_destroy(&(lock))
 
 
+#define THREAD pthread_t
+#define INIT_THREAD(thread) (thread) = 0;
+#define SPAWN_THREAD(thread, callback_function, this) \
+  pthread_attr_t attr; \
+  pthread_attr_init(&attr); \
+  pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM); \
+  pthread_create(&(thread), &attr, &posix_ ## callback_function, (void *)(this)); \
+  pthread_attr_destroy(&attr);
+
+#define JOIN_THREAD(thread) \
+  assert((thread) != 0); \
+  void *return_val; \
+pthread_join((thread), &return_val); \
+  (thread) = 0;
+
+// As above, declare this macro within your class declaration.
+#define THREAD_CALLBACK_DECLARATION(class, callback_function) \
+  static void *class::                                               \
+  posix_ ## callback_function(void *data) {        \
+    ((class *)data)->callback_function();       \
+    return NULL;                                   \
+  }
+
 #endif  // _WIN32
 #endif  // _WIN32
 
 
 #endif
 #endif

+ 42 - 41
direct/src/plugin/p3d_plugin.cxx

@@ -29,7 +29,7 @@
 // module from parallel access by multiple threads in the host.
 // module from parallel access by multiple threads in the host.
 
 
 bool initialized_lock = false;
 bool initialized_lock = false;
-LOCK _lock;
+LOCK _api_lock;
 
 
 ofstream log;
 ofstream log;
 string plugin_output_filename;
 string plugin_output_filename;
@@ -44,10 +44,10 @@ P3D_initialize(int api_version, const char *output_filename) {
   }
   }
 
 
   if (!initialized_lock) {
   if (!initialized_lock) {
-    INIT_LOCK(_lock);
+    INIT_LOCK(_api_lock);
     initialized_lock = true;
     initialized_lock = true;
   }
   }
-  ACQUIRE_LOCK(_lock);
+  ACQUIRE_LOCK(_api_lock);
 
 
   plugin_output_filename = string();
   plugin_output_filename = string();
   if (output_filename != NULL) {
   if (output_filename != NULL) {
@@ -63,21 +63,22 @@ P3D_initialize(int api_version, const char *output_filename) {
 
 
   P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
   P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
   bool result = inst_mgr->initialize();
   bool result = inst_mgr->initialize();
-  RELEASE_LOCK(_lock);
+  RELEASE_LOCK(_api_lock);
   return result;
   return result;
 }
 }
 
 
 void 
 void 
 P3D_finalize() {
 P3D_finalize() {
+  P3DInstanceManager::delete_global_ptr();
 }
 }
 
 
 P3D_instance *
 P3D_instance *
 P3D_new_instance(P3D_request_ready_func *func, void *user_data) {
 P3D_new_instance(P3D_request_ready_func *func, void *user_data) {
   assert(P3DInstanceManager::get_global_ptr()->is_initialized());
   assert(P3DInstanceManager::get_global_ptr()->is_initialized());
-  ACQUIRE_LOCK(_lock);
+  ACQUIRE_LOCK(_api_lock);
   P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
   P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
   P3DInstance *result = inst_mgr->create_instance(func, user_data);
   P3DInstance *result = inst_mgr->create_instance(func, user_data);
-  RELEASE_LOCK(_lock);
+  RELEASE_LOCK(_api_lock);
   return result;
   return result;
 }
 }
 
 
@@ -88,21 +89,21 @@ P3D_instance_start(P3D_instance *instance, const char *p3d_filename,
   if (p3d_filename == NULL) {
   if (p3d_filename == NULL) {
     p3d_filename = "";
     p3d_filename = "";
   }
   }
-  ACQUIRE_LOCK(_lock);
+  ACQUIRE_LOCK(_api_lock);
   P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
   P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
   bool result = inst_mgr->start_instance
   bool result = inst_mgr->start_instance
     ((P3DInstance *)instance, p3d_filename, tokens, num_tokens);
     ((P3DInstance *)instance, p3d_filename, tokens, num_tokens);
-  RELEASE_LOCK(_lock);
+  RELEASE_LOCK(_api_lock);
   return result;
   return result;
 }
 }
 
 
 void
 void
 P3D_instance_finish(P3D_instance *instance) {
 P3D_instance_finish(P3D_instance *instance) {
   assert(P3DInstanceManager::get_global_ptr()->is_initialized());
   assert(P3DInstanceManager::get_global_ptr()->is_initialized());
-  ACQUIRE_LOCK(_lock);
+  ACQUIRE_LOCK(_api_lock);
   P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
   P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
   inst_mgr->finish_instance((P3DInstance *)instance);
   inst_mgr->finish_instance((P3DInstance *)instance);
-  RELEASE_LOCK(_lock);
+  RELEASE_LOCK(_api_lock);
 }
 }
 
 
 void
 void
@@ -115,97 +116,97 @@ P3D_instance_setup_window(P3D_instance *instance,
   P3DWindowParams wparams(window_type, win_x, win_y,
   P3DWindowParams wparams(window_type, win_x, win_y,
                           win_width, win_height, parent_window);
                           win_width, win_height, parent_window);
 
 
-  ACQUIRE_LOCK(_lock);
+  ACQUIRE_LOCK(_api_lock);
   ((P3DInstance *)instance)->set_wparams(wparams);
   ((P3DInstance *)instance)->set_wparams(wparams);
-  RELEASE_LOCK(_lock);
+  RELEASE_LOCK(_api_lock);
 }
 }
 
 
 P3D_class_definition *
 P3D_class_definition *
 P3D_make_class_definition() {
 P3D_make_class_definition() {
   assert(P3DInstanceManager::get_global_ptr()->is_initialized());
   assert(P3DInstanceManager::get_global_ptr()->is_initialized());
-  ACQUIRE_LOCK(_lock);
+  ACQUIRE_LOCK(_api_lock);
 
 
   P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
   P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
   P3D_class_definition *result = inst_mgr->make_class_definition();
   P3D_class_definition *result = inst_mgr->make_class_definition();
   
   
-  RELEASE_LOCK(_lock);
+  RELEASE_LOCK(_api_lock);
   return result;
   return result;
 }
 }
 
 
 P3D_object *
 P3D_object *
 P3D_new_none_object() {
 P3D_new_none_object() {
   assert(P3DInstanceManager::get_global_ptr()->is_initialized());
   assert(P3DInstanceManager::get_global_ptr()->is_initialized());
-  ACQUIRE_LOCK(_lock);
+  ACQUIRE_LOCK(_api_lock);
 
 
   P3D_object *result = new P3DNoneObject();
   P3D_object *result = new P3DNoneObject();
   
   
-  RELEASE_LOCK(_lock);
+  RELEASE_LOCK(_api_lock);
   return result;
   return result;
 }
 }
 
 
 P3D_object *
 P3D_object *
 P3D_new_bool_object(bool value) {
 P3D_new_bool_object(bool value) {
   assert(P3DInstanceManager::get_global_ptr()->is_initialized());
   assert(P3DInstanceManager::get_global_ptr()->is_initialized());
-  ACQUIRE_LOCK(_lock);
+  ACQUIRE_LOCK(_api_lock);
 
 
   P3D_object *result = new P3DBoolObject(value);
   P3D_object *result = new P3DBoolObject(value);
   
   
-  RELEASE_LOCK(_lock);
+  RELEASE_LOCK(_api_lock);
   return result;
   return result;
 }
 }
 
 
 P3D_object *
 P3D_object *
 P3D_new_int_object(int value) {
 P3D_new_int_object(int value) {
   assert(P3DInstanceManager::get_global_ptr()->is_initialized());
   assert(P3DInstanceManager::get_global_ptr()->is_initialized());
-  ACQUIRE_LOCK(_lock);
+  ACQUIRE_LOCK(_api_lock);
 
 
   P3D_object *result = new P3DIntObject(value);
   P3D_object *result = new P3DIntObject(value);
   
   
-  RELEASE_LOCK(_lock);
+  RELEASE_LOCK(_api_lock);
   return result;
   return result;
 }
 }
 
 
 P3D_object *
 P3D_object *
 P3D_new_float_object(double value) {
 P3D_new_float_object(double value) {
   assert(P3DInstanceManager::get_global_ptr()->is_initialized());
   assert(P3DInstanceManager::get_global_ptr()->is_initialized());
-  ACQUIRE_LOCK(_lock);
+  ACQUIRE_LOCK(_api_lock);
 
 
   P3D_object *result = new P3DFloatObject(value);
   P3D_object *result = new P3DFloatObject(value);
   
   
-  RELEASE_LOCK(_lock);
+  RELEASE_LOCK(_api_lock);
   return result;
   return result;
 }
 }
 
 
 P3D_object *
 P3D_object *
 P3D_new_string_object(const char *str, int length) {
 P3D_new_string_object(const char *str, int length) {
   assert(P3DInstanceManager::get_global_ptr()->is_initialized());
   assert(P3DInstanceManager::get_global_ptr()->is_initialized());
-  ACQUIRE_LOCK(_lock);
+  ACQUIRE_LOCK(_api_lock);
 
 
   P3D_object *result = new P3DStringObject(string(str, length));
   P3D_object *result = new P3DStringObject(string(str, length));
   
   
-  RELEASE_LOCK(_lock);
+  RELEASE_LOCK(_api_lock);
   return result;
   return result;
 }
 }
 
 
 P3D_object *
 P3D_object *
 P3D_new_list_object() {
 P3D_new_list_object() {
   assert(P3DInstanceManager::get_global_ptr()->is_initialized());
   assert(P3DInstanceManager::get_global_ptr()->is_initialized());
-  ACQUIRE_LOCK(_lock);
+  ACQUIRE_LOCK(_api_lock);
 
 
   P3D_object *result = new P3DListObject;
   P3D_object *result = new P3DListObject;
   
   
-  RELEASE_LOCK(_lock);
+  RELEASE_LOCK(_api_lock);
   return result;
   return result;
 }
 }
 
 
 P3D_object *
 P3D_object *
 P3D_instance_get_script_object(P3D_instance *instance) {
 P3D_instance_get_script_object(P3D_instance *instance) {
   assert(P3DInstanceManager::get_global_ptr()->is_initialized());
   assert(P3DInstanceManager::get_global_ptr()->is_initialized());
-  ACQUIRE_LOCK(_lock);
+  ACQUIRE_LOCK(_api_lock);
 
 
   P3D_object *result = ((P3DInstance *)instance)->get_script_object();
   P3D_object *result = ((P3DInstance *)instance)->get_script_object();
   
   
-  RELEASE_LOCK(_lock);
+  RELEASE_LOCK(_api_lock);
   return result;
   return result;
 }
 }
 
 
@@ -213,55 +214,55 @@ void
 P3D_instance_set_script_object(P3D_instance *instance, 
 P3D_instance_set_script_object(P3D_instance *instance, 
                                P3D_object *object) {
                                P3D_object *object) {
   assert(P3DInstanceManager::get_global_ptr()->is_initialized());
   assert(P3DInstanceManager::get_global_ptr()->is_initialized());
-  ACQUIRE_LOCK(_lock);
+  ACQUIRE_LOCK(_api_lock);
 
 
   // TODO.
   // TODO.
   
   
-  RELEASE_LOCK(_lock);
+  RELEASE_LOCK(_api_lock);
 }
 }
 
 
 
 
 P3D_request *
 P3D_request *
 P3D_instance_get_request(P3D_instance *instance) {
 P3D_instance_get_request(P3D_instance *instance) {
   assert(P3DInstanceManager::get_global_ptr()->is_initialized());
   assert(P3DInstanceManager::get_global_ptr()->is_initialized());
-  ACQUIRE_LOCK(_lock);
+  ACQUIRE_LOCK(_api_lock);
   P3D_request *result = ((P3DInstance *)instance)->get_request();
   P3D_request *result = ((P3DInstance *)instance)->get_request();
-  RELEASE_LOCK(_lock);
+  RELEASE_LOCK(_api_lock);
   return result;
   return result;
 }
 }
 
 
 P3D_instance *
 P3D_instance *
 P3D_check_request(bool wait) {
 P3D_check_request(bool wait) {
   assert(P3DInstanceManager::get_global_ptr()->is_initialized());
   assert(P3DInstanceManager::get_global_ptr()->is_initialized());
-  ACQUIRE_LOCK(_lock);
+  ACQUIRE_LOCK(_api_lock);
   P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
   P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
   P3D_instance *inst = inst_mgr->check_request();
   P3D_instance *inst = inst_mgr->check_request();
 
 
   if (inst != NULL || !wait) {
   if (inst != NULL || !wait) {
-    RELEASE_LOCK(_lock);
+    RELEASE_LOCK(_api_lock);
     return inst;
     return inst;
   }
   }
   
   
   // Now we have to block until a request is available.
   // Now we have to block until a request is available.
   while (inst == NULL && inst_mgr->get_num_instances() != 0) {
   while (inst == NULL && inst_mgr->get_num_instances() != 0) {
-    RELEASE_LOCK(_lock);
+    RELEASE_LOCK(_api_lock);
     inst_mgr->wait_request();
     inst_mgr->wait_request();
-    ACQUIRE_LOCK(_lock);
+    ACQUIRE_LOCK(_api_lock);
     inst = inst_mgr->check_request();
     inst = inst_mgr->check_request();
   }
   }
 
 
-  RELEASE_LOCK(_lock);
+  RELEASE_LOCK(_api_lock);
   return inst;
   return inst;
 }
 }
 
 
 void
 void
 P3D_request_finish(P3D_request *request, bool handled) {
 P3D_request_finish(P3D_request *request, bool handled) {
   assert(P3DInstanceManager::get_global_ptr()->is_initialized());
   assert(P3DInstanceManager::get_global_ptr()->is_initialized());
-  ACQUIRE_LOCK(_lock);
+  ACQUIRE_LOCK(_api_lock);
   if (request != (P3D_request *)NULL) {
   if (request != (P3D_request *)NULL) {
     ((P3DInstance *)request->_instance)->finish_request(request, handled);
     ((P3DInstance *)request->_instance)->finish_request(request, handled);
   }
   }
-  RELEASE_LOCK(_lock);
+  RELEASE_LOCK(_api_lock);
 }
 }
 
 
 bool
 bool
@@ -272,11 +273,11 @@ P3D_instance_feed_url_stream(P3D_instance *instance, int unique_id,
                              const void *this_data, 
                              const void *this_data, 
                              size_t this_data_size) {
                              size_t this_data_size) {
   assert(P3DInstanceManager::get_global_ptr()->is_initialized());
   assert(P3DInstanceManager::get_global_ptr()->is_initialized());
-  ACQUIRE_LOCK(_lock);
+  ACQUIRE_LOCK(_api_lock);
   bool result = ((P3DInstance *)instance)->
   bool result = ((P3DInstance *)instance)->
     feed_url_stream(unique_id, result_code, http_status_code,
     feed_url_stream(unique_id, result_code, http_status_code,
                     total_expected_data, 
                     total_expected_data, 
                     (const unsigned char *)this_data, this_data_size);
                     (const unsigned char *)this_data, this_data_size);
-  RELEASE_LOCK(_lock);
+  RELEASE_LOCK(_api_lock);
   return result;
   return result;
 }
 }

+ 24 - 8
direct/src/plugin_npapi/ppInstance.cxx

@@ -79,6 +79,10 @@ PPInstance::
     _p3d_inst = NULL;
     _p3d_inst = NULL;
   }
   }
 
 
+  if (_script_object != NULL) {
+    browser->releaseobject(_script_object);
+  }
+
   // Free the tokens we allocated.
   // Free the tokens we allocated.
   Tokens::iterator ti;
   Tokens::iterator ti;
   for (ti = _tokens.begin(); ti != _tokens.end(); ++ti) {
   for (ti = _tokens.begin(); ti != _tokens.end(); ++ti) {
@@ -386,6 +390,22 @@ handle_request(P3D_request *request) {
     }
     }
     break;
     break;
 
 
+  case P3D_RT_notify:
+    {
+      logfile << "Got P3D_RT_notify: " << request->_request._notify._message
+              << "\n" << flush;
+
+      if (_script_object != NULL &&
+          strcmp(request->_request._notify._message, "onpythonload") == 0) {
+        // Now that Python is running, initialize our script_object
+        // with the proper P3D object pointer.
+        P3D_object *obj = P3D_instance_get_script_object(_p3d_inst);
+        logfile << "late obj = " << obj << "\n" << flush;
+        _script_object->set_p3d_object(obj);
+      }
+    }
+    break;
+
   default:
   default:
     // Some request types are not handled.
     // Some request types are not handled.
     logfile << "Unhandled request: " << request->_request_type << "\n";
     logfile << "Unhandled request: " << request->_request_type << "\n";
@@ -405,6 +425,7 @@ NPObject *PPInstance::
 get_script_object() {
 get_script_object() {
   logfile << "get_script_object\n" << flush;
   logfile << "get_script_object\n" << flush;
   if (_script_object != NULL) {
   if (_script_object != NULL) {
+    logfile << "returning _script_object ref = " << _script_object->referenceCount << "\n";
     return _script_object;
     return _script_object;
   }
   }
 
 
@@ -416,6 +437,9 @@ get_script_object() {
   }
   }
 
 
   _script_object = PPObject::make_new(this, obj);
   _script_object = PPObject::make_new(this, obj);
+  logfile << "_script_object ref = " << _script_object->referenceCount << "\n";
+  browser->retainobject(_script_object);
+  logfile << "after retain, _script_object ref = " << _script_object->referenceCount << "\n";
   logfile << "ppobj = " << _script_object << "\n" << flush;
   logfile << "ppobj = " << _script_object << "\n" << flush;
   return _script_object;
   return _script_object;
 }
 }
@@ -601,14 +625,6 @@ create_instance() {
     }
     }
     P3D_instance_start(_p3d_inst, _p3d_filename.c_str(), tokens, _tokens.size());
     P3D_instance_start(_p3d_inst, _p3d_filename.c_str(), tokens, _tokens.size());
     send_window();
     send_window();
-
-    if (_script_object != NULL) {
-      // Now that we have an instance, initialize our script_object
-      // with the proper P3D object pointer.
-      P3D_object *obj = P3D_instance_get_script_object(_p3d_inst);
-      logfile << "late obj = " << obj << "\n" << flush;
-      _script_object->set_p3d_object(obj);
-    }
   }
   }
 }
 }
 
 

+ 11 - 4
direct/src/plugin_npapi/startup.cxx

@@ -87,7 +87,7 @@ window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: request_ready
 //     Function: request_ready
-//  Description: This function is attached as an asyncronous callback
+//  Description: This function is attached as an asynchronous callback
 //               to each instance; it will be notified when the
 //               to each instance; it will be notified when the
 //               instance has a request ready.  This function may be
 //               instance has a request ready.  This function may be
 //               called in a sub-thread.
 //               called in a sub-thread.
@@ -99,14 +99,17 @@ request_ready(P3D_instance *instance) {
     //    << " thread = " << GetCurrentThreadId()
     //    << " thread = " << GetCurrentThreadId()
     << "\n" << flush;
     << "\n" << flush;
 
 
+#ifdef _WIN32
   // Since we might be in a sub-thread at this point, use a Windows
   // Since we might be in a sub-thread at this point, use a Windows
   // message to forward this event to the main thread.
   // message to forward this event to the main thread.
 
 
-#ifdef _WIN32
   PostThreadMessage(main_thread_id, WM_USER, 0, 0);
   PostThreadMessage(main_thread_id, WM_USER, 0, 0);
+
 #else
 #else
-  // TODO: send the message to the main thread properly.
-  handle_request_loop();
+  // On Mac, we ignore this asynchronous event, and rely on detecting
+  // it within HandleEvent().  TODO: enable a timer to ensure we get
+  // HandleEvent callbacks in a timely manner?  Or maybe we should
+  // enable a one-shot timer in response to this asynchronous event?
 #endif
 #endif
 }
 }
 
 
@@ -366,6 +369,10 @@ NPP_Print(NPP instance, NPPrint *platformPrint) {
 int16
 int16
 NPP_HandleEvent(NPP instance, void *event) {
 NPP_HandleEvent(NPP instance, void *event) {
   //  logfile << "HandleEvent\n";
   //  logfile << "HandleEvent\n";
+
+  // Here's a fine opportunity to check for new requests.
+  handle_request_loop();
+
   return 0;
   return 0;
 }
 }
 
 

+ 6 - 2
direct/src/plugin_standalone/panda3d.cxx

@@ -251,7 +251,8 @@ run(int argc, char *argv[]) {
   }
   }
 
 
   // All instances have finished; we can exit.
   // All instances have finished; we can exit.
-
+  cerr << "clean exit\n";
+  unload_plugin();
   return 0;
   return 0;
 }
 }
 
 
@@ -404,7 +405,8 @@ handle_request(P3D_request *request) {
     cerr << "Got P3D_RT_notify: " << request->_request._notify._message
     cerr << "Got P3D_RT_notify: " << request->_request._notify._message
          << "\n";
          << "\n";
     // Ignore notifications.
     // Ignore notifications.
-
+    
+    /*
     {
     {
       // Temporary.
       // Temporary.
       P3D_object *obj = P3D_instance_get_script_object(request->_instance);
       P3D_object *obj = P3D_instance_get_script_object(request->_instance);
@@ -428,6 +430,8 @@ handle_request(P3D_request *request) {
         P3D_OBJECT_FINISH(obj);
         P3D_OBJECT_FINISH(obj);
       }
       }
     }
     }
+    */
+
     break;
     break;
 
 
   default:
   default:

+ 9 - 2
direct/src/showutil/runp3d.py

@@ -22,7 +22,7 @@ See pack3d.py for a script that generates these p3d files.
 import sys
 import sys
 from direct.showbase import VFSImporter
 from direct.showbase import VFSImporter
 from direct.showbase.DirectObject import DirectObject
 from direct.showbase.DirectObject import DirectObject
-from pandac.PandaModules import VirtualFileSystem, Filename, Multifile, loadPrcFileData, getModelPath, HTTPClient
+from pandac.PandaModules import VirtualFileSystem, Filename, Multifile, loadPrcFileData, unloadPrcFile, getModelPath, HTTPClient
 from direct.stdpy import file
 from direct.stdpy import file
 from direct.task.TaskManagerGlobal import taskMgr
 from direct.task.TaskManagerGlobal import taskMgr
 from direct.showbase import AppRunnerGlobal
 from direct.showbase import AppRunnerGlobal
@@ -64,6 +64,9 @@ class AppRunner(DirectObject):
         # This is the default requestFunc that is installed if we
         # This is the default requestFunc that is installed if we
         # never call setRequestFunc().
         # never call setRequestFunc().
         def defaultRequestFunc(*args):
         def defaultRequestFunc(*args):
+            if args[1] == 'notify':
+                # Quietly ignore notifies.
+                return
             print "Ignoring request: %s" % (args,)
             print "Ignoring request: %s" % (args,)
         self.requestFunc = defaultRequestFunc
         self.requestFunc = defaultRequestFunc
 
 
@@ -149,6 +152,10 @@ class AppRunner(DirectObject):
         # for this instance.
         # for this instance.
         self.instanceId = instanceId
         self.instanceId = instanceId
         
         
+        # Now that we have an instanceId, we can response to queries
+        # and such.
+        self.sendRequest('notify', 'onpythonload')
+
         tokenDict = dict(tokens)
         tokenDict = dict(tokens)
         fname = Filename.fromOsSpecific(p3dFilename)
         fname = Filename.fromOsSpecific(p3dFilename)
         if not p3dFilename:
         if not p3dFilename:
@@ -246,7 +253,7 @@ class AppRunner(DirectObject):
     def windowEvent(self, win):
     def windowEvent(self, win):
         print "Got window event in runp3d"
         print "Got window event in runp3d"
 
 
-        self.sendRequest('notify', 'window_opened')
+        self.sendRequest('notify', 'onwindowopen')
 
 
     def parseSysArgs(self):
     def parseSysArgs(self):
         """ Converts sys.argv into (p3dFilename, tokens). """
         """ Converts sys.argv into (p3dFilename, tokens). """