upcall requests

This commit is contained in:
David Rose 2009-06-24 11:18:43 +00:00
parent 09d52bc110
commit 3d73a4434e
9 changed files with 219 additions and 15 deletions

View File

@ -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);
}

View File

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

View File

@ -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;
}

View File

@ -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"

View File

@ -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

View File

@ -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);

View File

@ -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;

View File

@ -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";

View File

@ -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). """