upcall requests
This commit is contained in:
parent
09d52bc110
commit
3d73a4434e
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -71,6 +71,8 @@ public:
|
|||
inline bool is_started() const;
|
||||
void request_stop();
|
||||
|
||||
void async_notify(const string &message);
|
||||
|
||||
TiXmlElement *make_xml();
|
||||
|
||||
private:
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
|
|
|
|||
|
|
@ -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). """
|
||||
|
|
|
|||
Loading…
Reference in New Issue