Browse Source

upcall requests

David Rose 16 years ago
parent
commit
3d73a4434e

+ 21 - 4
direct/src/plugin/p3dInstance.cxx

@@ -76,6 +76,7 @@ P3DInstance::
   _packages.clear();
 
   if (_splash_window != NULL) {
+    _splash_window->close_window();
     delete _splash_window;
     _splash_window = NULL;
   }
@@ -226,6 +227,25 @@ get_request() {
   }
   RELEASE_LOCK(_request_lock);
 
+  if (result != NULL) {
+    if (result->_request_type == P3D_RT_notify) {
+      // If we received a notify request, process the notification
+      // immediately--it might be interesting to this instance.
+      const char *message = result->_request._notify._message;
+      if (strcmp(message, "window_opened") == 0) {
+        // The process told us that it just succesfully opened its
+        // window.
+        nout << "Instance " << this << " got window_opened\n" << flush;
+        _instance_window_opened = true;
+        if (_splash_window != NULL) {
+          _splash_window->close_window();
+          delete _splash_window;
+          _splash_window = NULL;
+        }
+      }
+    }
+  }
+
   return result;
 }
 
@@ -237,7 +257,7 @@ get_request() {
 ////////////////////////////////////////////////////////////////////
 void P3DInstance::
 add_request(P3D_request *request) {
-  assert(request->_instance == this);
+  request->_instance = this;
 
   ACQUIRE_LOCK(_request_lock);
   _pending_requests.push_back(request);
@@ -266,7 +286,6 @@ void P3DInstance::
 finish_request(P3D_request *request, bool handled) {
   assert(request != NULL);
 
-  // TODO.  Delete sub-pieces more aggressively.  Deal with handled flag.
   delete request;
 }
 
@@ -348,7 +367,6 @@ start_download(P3DDownload *download) {
        << "\n" << flush;
 
   P3D_request *request = new P3D_request;
-  request->_instance = this;
   request->_request_type = P3D_RT_get_url;
   request->_request._get_url._url = strdup(download->get_url().c_str());
   request->_request._get_url._unique_id = download_id;
@@ -368,7 +386,6 @@ request_stop() {
   if (!_requested_stop) {
     _requested_stop = true;
     P3D_request *request = new P3D_request;
-    request->_instance = this;
     request->_request_type = P3D_RT_stop;
     add_request(request);
   }

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

@@ -71,6 +71,8 @@ public:
   inline bool is_started() const;
   void request_stop();
 
+  void async_notify(const string &message);
+
   TiXmlElement *make_xml();
 
 private:

+ 92 - 5
direct/src/plugin/p3dPythonRun.cxx

@@ -15,6 +15,11 @@
 #include "p3dPythonRun.h"
 #include "asyncTaskManager.h"
 
+// There is only one P3DPythonRun object in any given process space.
+// Makes the statics easier to deal with, and we don't need multiple
+// instances of this think.
+P3DPythonRun *P3DPythonRun::_global_ptr = NULL;
+
 ////////////////////////////////////////////////////////////////////
 //     Function: P3DPythonRun::Constructor
 //       Access: Public
@@ -129,6 +134,35 @@ run_python() {
 
   Py_DECREF(runp3d);
 
+
+  // Construct a Python wrapper around our request_func() method.
+  static PyMethodDef p3dpython_methods[] = {
+    {"request_func", P3DPythonRun::st_request_func, METH_VARARGS,
+     "Check for communications to and from the plugin host."},
+    {NULL, NULL, 0, NULL}        /* Sentinel */
+  };
+  PyObject *p3dpython = Py_InitModule("p3dpython", p3dpython_methods);
+  if (p3dpython == NULL) {
+    PyErr_Print();
+    return false;
+  }
+  PyObject *request_func = PyObject_GetAttrString(p3dpython, "request_func");
+  if (request_func == NULL) {
+    PyErr_Print();
+    return false;
+  }
+
+  // Now pass that func pointer back to our AppRunner instance, so it
+  // can call up to us.
+  PyObject *result = PyObject_CallMethod(_runner, "setRequestFunc", "O", request_func);
+  if (result == NULL) {
+    PyErr_Print();
+    return false;
+  }
+  Py_DECREF(result);
+  Py_DECREF(request_func);
+ 
+
   // Now add check_comm() as a task.
   _check_comm_task = new GenericAsyncTask("check_comm", st_check_comm, this);
   AsyncTaskManager *task_mgr = AsyncTaskManager::get_global_ptr();
@@ -194,8 +228,7 @@ handle_command(TiXmlDocument *doc) {
 //  Description: This method is added to the Python task manager (via
 //               py_check_comm, below) so that it gets a call every
 //               frame.  Its job is to check for commands received
-//               from, and requests to be delivered to, the plugin
-//               host in the parent process.
+//               from the plugin host in the parent process.
 ////////////////////////////////////////////////////////////////////
 AsyncTask::DoneStatus P3DPythonRun::
 check_comm(GenericAsyncTask *task) {
@@ -234,6 +267,59 @@ st_check_comm(GenericAsyncTask *task, void *user_data) {
   return self->check_comm(task);
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: P3DPythonRun::py_request_func
+//       Access: Private
+//  Description: This method is a special Python function that is
+//               added as a callback to the AppRunner class, to allow
+//               Python to upcall into this object.
+////////////////////////////////////////////////////////////////////
+PyObject *P3DPythonRun::
+py_request_func(PyObject *args) {
+  int instance_id;
+  const char *request_type;
+  PyObject *extra_args;
+  if (!PyArg_ParseTuple(args, "isO", &instance_id, &request_type, &extra_args)) {
+    return NULL;
+  }
+
+  TiXmlDocument doc;
+  TiXmlDeclaration *decl = new TiXmlDeclaration("1.0", "", "");
+  TiXmlElement *xrequest = new TiXmlElement("request");
+  xrequest->SetAttribute("id", instance_id);
+  xrequest->SetAttribute("rtype", request_type);
+
+  if (strcmp(request_type, "notify") == 0) {
+    // A general notification to be sent directly to the instance.
+    const char *message;
+    if (!PyArg_ParseTuple(extra_args, "s", &message)) {
+      return NULL;
+    }
+
+    xrequest->SetAttribute("message", message);
+    doc.LinkEndChild(decl);
+    doc.LinkEndChild(xrequest);
+    _pipe_write << doc << flush;
+
+  } else {
+    string message = string("Unsupported request type: ") + string(request_type);
+    PyErr_SetString(PyExc_ValueError, message.c_str());
+    return NULL;
+  }
+
+  return Py_BuildValue("");
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: P3DPythonRun::st_request_func
+//       Access: Private, Static
+//  Description: This is the static wrapper around py_request_func.
+////////////////////////////////////////////////////////////////////
+PyObject *P3DPythonRun::
+st_request_func(PyObject *, PyObject *args) {
+  return P3DPythonRun::_global_ptr->py_request_func(args);
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: P3DPythonRun::spawn_read_thread
 //       Access: Private
@@ -369,7 +455,8 @@ set_p3d_filename(P3DCInstance *inst, TiXmlElement *xfparams) {
   }
   
   PyObject *result = PyObject_CallMethod
-    (_runner, "setP3DFilename", "sO", p3d_filename.c_str(), token_list);
+    (_runner, "setP3DFilename", "sOi", p3d_filename.c_str(), token_list,
+     inst->get_instance_id());
   Py_DECREF(token_list);
 
   if (result == NULL) {
@@ -534,9 +621,9 @@ posix_rt_thread_run(void *data) {
 ////////////////////////////////////////////////////////////////////
 int
 main(int argc, char *argv[]) {
-  P3DPythonRun run(argc, argv);
+  P3DPythonRun::_global_ptr = new P3DPythonRun(argc, argv);
   
-  if (!run.run_python()) {
+  if (!P3DPythonRun::_global_ptr->run_python()) {
     nout << "Couldn't initialize Python.\n";
     return 1;
   }

+ 6 - 0
direct/src/plugin/p3dPythonRun.h

@@ -66,6 +66,9 @@ private:
   AsyncTask::DoneStatus check_comm(GenericAsyncTask *task);
   static AsyncTask::DoneStatus st_check_comm(GenericAsyncTask *task, void *user_data);
 
+  PyObject *py_request_func(PyObject *args);
+  static PyObject *st_request_func(PyObject *, PyObject *args);
+
   void spawn_read_thread();
   void join_read_thread();
 
@@ -119,6 +122,9 @@ private:
 #else
   pthread_t _read_thread;
 #endif
+
+public:
+  static P3DPythonRun *_global_ptr;
 };
 
 #include "p3dPythonRun.I"

+ 56 - 3
direct/src/plugin/p3dSession.cxx

@@ -405,11 +405,64 @@ rt_thread_run() {
     }
 
     // Successfully read an XML document.
-    nout << "Session got request: " << *doc << "\n" << flush;
+    rt_handle_request(doc);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: P3DSession::rt_handle_request
+//       Access: Private
+//  Description: Processes a single request or notification received
+//               from an instance.
+////////////////////////////////////////////////////////////////////
+void P3DSession::
+rt_handle_request(TiXmlDocument *doc) {
+  nout << "Session got request: " << *doc << "\n" << flush;
+
+  TiXmlElement *xrequest = doc->FirstChildElement("request");
+  if (xrequest != (TiXmlElement *)NULL) {
+    int instance_id ;
+    if (xrequest->Attribute("id", &instance_id)) {
+      // Look up the particular instance this is related to.
+      ACQUIRE_LOCK(_instances_lock);
+      Instances::const_iterator ii;
+      ii = _instances.find(instance_id);
+      if (ii != _instances.end()) {
+        P3DInstance *inst = (*ii).second;
+        P3D_request *request = rt_make_p3d_request(xrequest);
+        if (request != NULL) {
+          inst->add_request(request);
+        }
+      }
+      RELEASE_LOCK(_instances_lock);
+    }
+  }
+
+  delete doc;
+}
 
-    // TODO: feed the request up to the parent.
-    delete doc;
+////////////////////////////////////////////////////////////////////
+//     Function: P3DSession::rt_make_p3d_request
+//       Access: Private
+//  Description: Creates a new P3D_request structure from the XML.
+////////////////////////////////////////////////////////////////////
+P3D_request *P3DSession::
+rt_make_p3d_request(TiXmlElement *xrequest) {
+  P3D_request *request = NULL;
+
+  const char *rtype = xrequest->Attribute("rtype");
+  if (rtype != NULL) {
+    if (strcmp(rtype, "notify") == 0) {
+      const char *message = xrequest->Attribute("message");
+      if (message != NULL) {
+        request = new P3D_request;
+        request->_request_type = P3D_RT_notify;
+        request->_request._notify._message = strdup(message);
+      }
+    }
   }
+
+  return request;
 }
 
 ////////////////////////////////////////////////////////////////////

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

@@ -58,6 +58,8 @@ private:
   // These methods run only within the read thread.
   void rt_thread_run();
   void rt_terminate();
+  void rt_handle_request(TiXmlDocument *doc);
+  P3D_request *rt_make_p3d_request(TiXmlElement *xrequest);
 
 #ifdef _WIN32
   static DWORD WINAPI win_rt_thread_run(LPVOID data);

+ 10 - 0
direct/src/plugin/p3d_plugin.h

@@ -322,6 +322,7 @@ typedef enum {
   P3D_RT_unused, //  P3D_RT_new_config_xml,
   P3D_RT_get_url,
   P3D_RT_post_url,
+  P3D_RT_notify,
 } P3D_request_type;
 
 /* Structures corresponding to the request types in the above enum. */
@@ -357,6 +358,14 @@ typedef struct {
   int _unique_id;
 } P3D_request_post_url;
 
+/* A general notification.  This is just a message of some event
+   having occurred within the Panda3D instance.  It may be safely
+   ignored.
+*/
+typedef struct {
+  const char *_message;
+} P3D_request_notify;
+
 /* This is the overall structure that represents a single request.  It
    is returned by P3D_instance_get_request(). */
 typedef struct {
@@ -366,6 +375,7 @@ typedef struct {
     P3D_request_stop _stop;
     P3D_request_get_url _get_url;
     P3D_request_post_url _post_url;
+    P3D_request_notify _notify;
   } _request;
 } P3D_request;
 

+ 4 - 0
direct/src/plugin_standalone/panda3d.cxx

@@ -171,6 +171,10 @@ handle_request(P3D_request *request) {
     }
     break;
 
+  case P3D_RT_notify:
+    // Ignore notifications.
+    break;
+
   default:
     // Some request types are not handled.
     cerr << "Unhandled request: " << request->_request_type << "\n";

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

@@ -49,6 +49,13 @@ class AppRunner(DirectObject):
         self.gotP3DFilename = False
         self.started = False
         self.windowPrc = None
+        self.instanceId = None
+
+        # This is the default requestFunc that is installed if we
+        # never call setRequestFunc().
+        def defaultRequestFunc(*args):
+            print "Ignoring request func: %s" % (args,)
+        self.requestFunc = defaultRequestFunc
 
     def initPackedAppEnvironment(self):
         """ This function sets up the Python environment suitably for
@@ -121,7 +128,14 @@ class AppRunner(DirectObject):
             if hasattr(main, 'main') and callable(main.main):
                 main.main()
 
-    def setP3DFilename(self, p3dFilename, tokens = []):
+    def setP3DFilename(self, p3dFilename, tokens = [],
+                       instanceId = None):
+        # One day we will have support for multiple instances within a
+        # Python session.  Against that day, we save the instance ID
+        # for this instance.
+        print "setP3DFilename(%s, %s, %s)" % (p3dFilename, tokens, instanceId)
+        self.instanceId = instanceId
+        
         tokenDict = dict(tokens)
         fname = Filename.fromOsSpecific(p3dFilename)
         if not p3dFilename:
@@ -207,10 +221,19 @@ class AppRunner(DirectObject):
         self.gotWindow = True
         self.startIfReady()
 
+    def setRequestFunc(self, func):
+        """ This method is called by the plugin at startup to supply a
+        function that can be used to deliver requests upstream, to the
+        plugin, and thereby to the browser. """
+        self.requestFunc = func
+
+    def sendRequest(self, request, *args):
+        self.requestFunc(self.instanceId, request, args)
+
     def windowEvent(self, win):
         print "Got window event in runp3d"
 
-        # TODO: feed this information back to the plugin host.
+        self.sendRequest('notify', 'window_opened')
 
     def parseSysArgs(self):
         """ Converts sys.argv into (p3dFilename, tokens). """