diff --git a/direct/src/plugin/p3dInstance.cxx b/direct/src/plugin/p3dInstance.cxx index 37a94672e3..c0caccc8c1 100644 --- a/direct/src/plugin/p3dInstance.cxx +++ b/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); } diff --git a/direct/src/plugin/p3dInstance.h b/direct/src/plugin/p3dInstance.h index b0e6841e07..d4a834ad67 100644 --- a/direct/src/plugin/p3dInstance.h +++ b/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: diff --git a/direct/src/plugin/p3dPythonRun.cxx b/direct/src/plugin/p3dPythonRun.cxx index 2d278f69d1..b16044a07e 100755 --- a/direct/src/plugin/p3dPythonRun.cxx +++ b/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; } diff --git a/direct/src/plugin/p3dPythonRun.h b/direct/src/plugin/p3dPythonRun.h index 8580c6edeb..4a4b4472f2 100755 --- a/direct/src/plugin/p3dPythonRun.h +++ b/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" diff --git a/direct/src/plugin/p3dSession.cxx b/direct/src/plugin/p3dSession.cxx index 25444b87a6..0c2a05daef 100644 --- a/direct/src/plugin/p3dSession.cxx +++ b/direct/src/plugin/p3dSession.cxx @@ -405,13 +405,66 @@ rt_thread_run() { } // Successfully read an XML document. - nout << "Session got request: " << *doc << "\n" << flush; - - // TODO: feed the request up to the parent. - delete doc; + 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; +} + +//////////////////////////////////////////////////////////////////// +// 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; +} + //////////////////////////////////////////////////////////////////// // Function: P3DSession::rt_terminate // Access: Private diff --git a/direct/src/plugin/p3dSession.h b/direct/src/plugin/p3dSession.h index c3f13b31f2..a4413d4402 100644 --- a/direct/src/plugin/p3dSession.h +++ b/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); diff --git a/direct/src/plugin/p3d_plugin.h b/direct/src/plugin/p3d_plugin.h index c40be6d464..9a8a20124b 100644 --- a/direct/src/plugin/p3d_plugin.h +++ b/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; diff --git a/direct/src/plugin_standalone/panda3d.cxx b/direct/src/plugin_standalone/panda3d.cxx index 46098f93fa..be8f95d7d2 100644 --- a/direct/src/plugin_standalone/panda3d.cxx +++ b/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"; diff --git a/direct/src/showutil/runp3d.py b/direct/src/showutil/runp3d.py index a67517b02b..487e0289e0 100755 --- a/direct/src/showutil/runp3d.py +++ b/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). """