diff --git a/direct/src/plugin/p3dCInstance.I b/direct/src/plugin/p3dCInstance.I new file mode 100755 index 0000000000..9b6f08d3ca --- /dev/null +++ b/direct/src/plugin/p3dCInstance.I @@ -0,0 +1,36 @@ +// Filename: p3dCInstance.I +// Created by: drose (08Jun09) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised BSD +// license. You should have received a copy of this license along +// with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: P3DCInstance::get_p3d_filename +// Access: Public +// Description: Returns the p3d filename that was passed to the +// constructor. +//////////////////////////////////////////////////////////////////// +inline const string &P3DCInstance:: +get_p3d_filename() const { + return _p3d_filename; +} + +//////////////////////////////////////////////////////////////////// +// Function: P3DCInstance::get_instance_id +// Access: Public +// Description: Returns a unique integer for each instance in the +// system. +//////////////////////////////////////////////////////////////////// +inline int P3DCInstance:: +get_instance_id() const { + return _instance_id; +} diff --git a/direct/src/plugin/p3dCInstance.cxx b/direct/src/plugin/p3dCInstance.cxx new file mode 100755 index 0000000000..2e2f505727 --- /dev/null +++ b/direct/src/plugin/p3dCInstance.cxx @@ -0,0 +1,70 @@ +// Filename: p3dCInstance.cxx +// Created by: drose (08Jun09) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised BSD +// license. You should have received a copy of this license along +// with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + +#include "p3dCInstance.h" + + +//////////////////////////////////////////////////////////////////// +// Function: P3DCInstance::Constructor +// Access: Public +// Description: Constructs a new Instance from an XML description. +//////////////////////////////////////////////////////////////////// +P3DCInstance:: +P3DCInstance(TiXmlElement *xinstance) : + _func(NULL), + _window_type(P3D_WT_toplevel), + _win_x(0), _win_y(0), + _win_width(0), _win_height(0) +{ + xinstance->Attribute("id", &_instance_id); + + const char *p3d_filename = xinstance->Attribute("p3d_filename"); + if (p3d_filename != NULL) { + _p3d_filename = p3d_filename; + } + + const char *window_type = xinstance->Attribute("window_type"); + if (window_type != NULL) { + if (strcmp(window_type, "embedded") == 0) { + _window_type = P3D_WT_embedded; + } else if (strcmp(window_type, "toplevel") == 0) { + _window_type = P3D_WT_toplevel; + } else if (strcmp(window_type, "fullscreen") == 0) { + _window_type = P3D_WT_fullscreen; + } else if (strcmp(window_type, "hidden") == 0) { + _window_type = P3D_WT_hidden; + } + } + + xinstance->Attribute("win_x", &_win_x); + xinstance->Attribute("win_y", &_win_y); + xinstance->Attribute("win_width", &_win_width); + xinstance->Attribute("win_height", &_win_height); + +#ifdef _WIN32 + int hwnd; + if (xinstance->Attribute("parent_hwnd", &hwnd)) { + _parent_window._hwnd = (HWND)hwnd; + } +#endif +} + +//////////////////////////////////////////////////////////////////// +// Function: P3DCInstance::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +P3DCInstance:: +~P3DCInstance() { +} diff --git a/direct/src/plugin/p3dCInstance.h b/direct/src/plugin/p3dCInstance.h new file mode 100755 index 0000000000..9e7968a2aa --- /dev/null +++ b/direct/src/plugin/p3dCInstance.h @@ -0,0 +1,62 @@ +// Filename: p3dCInstance.h +// Created by: drose (08Jun09) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised BSD +// license. You should have received a copy of this license along +// with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + +#ifndef P3DCINSTANCE_H +#define P3DCINSTANCE_H + +#include "p3d_plugin.h" + +#include +#include + +class P3DSession; + +//////////////////////////////////////////////////////////////////// +// Class : P3DCInstance +// Description : This is an instance of a Panda3D window, as seen in +// the parent-level process. +//////////////////////////////////////////////////////////////////// +class P3DCInstance : public P3D_instance { +public: + P3DCInstance(TiXmlElement *xinstance); + ~P3DCInstance(); + + inline const string &get_p3d_filename() const; + inline int get_instance_id() const; + +private: + class Token { + public: + string _keyword; + string _value; + }; + typedef vector Tokens; + + P3D_request_ready_func *_func; + string _p3d_filename; + P3D_window_type _window_type; + int _win_x, _win_y; + int _win_width, _win_height; + P3D_window_handle _parent_window; + + Tokens _tokens; + + int _instance_id; + + friend class P3DPythonRun; +}; + +#include "p3dCInstance.I" + +#endif diff --git a/direct/src/plugin/p3dInstance.I b/direct/src/plugin/p3dInstance.I index dc653c36be..811cc845f8 100644 --- a/direct/src/plugin/p3dInstance.I +++ b/direct/src/plugin/p3dInstance.I @@ -24,6 +24,17 @@ get_p3d_filename() const { return _p3d_filename; } +//////////////////////////////////////////////////////////////////// +// Function: P3DInstance::get_instance_id +// Access: Public +// Description: Returns a unique integer for each instance in the +// system. +//////////////////////////////////////////////////////////////////// +inline int P3DInstance:: +get_instance_id() const { + return _instance_id; +} + //////////////////////////////////////////////////////////////////// // Function: P3DInstance::get_session_key // Access: Public diff --git a/direct/src/plugin/p3dInstance.cxx b/direct/src/plugin/p3dInstance.cxx index 3252f7b128..09d78f3022 100644 --- a/direct/src/plugin/p3dInstance.cxx +++ b/direct/src/plugin/p3dInstance.cxx @@ -17,6 +17,8 @@ #include +int P3DInstance::_next_instance_id = 0; + //////////////////////////////////////////////////////////////////// // Function: P3DInstance::Constructor // Access: Public @@ -37,11 +39,13 @@ P3DInstance(P3D_request_ready_func *func, _win_width(win_width), _win_height(win_height), _parent_window(parent_window) { - P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr(); + _instance_id = _next_instance_id; + ++_next_instance_id; INIT_LOCK(_request_lock); // For the moment, all sessions will be unique. + P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr(); ostringstream strm; strm << inst_mgr->get_unique_session_index(); _session_key = strm.str(); @@ -200,6 +204,7 @@ feed_url_stream(int unique_id, TiXmlElement *P3DInstance:: make_xml() { TiXmlElement *xinstance = new TiXmlElement("instance"); + xinstance->SetAttribute("id", _instance_id); xinstance->SetAttribute("p3d_filename", _p3d_filename); switch (_window_type) { diff --git a/direct/src/plugin/p3dInstance.h b/direct/src/plugin/p3dInstance.h index d03bd84b08..95f4f46b15 100644 --- a/direct/src/plugin/p3dInstance.h +++ b/direct/src/plugin/p3dInstance.h @@ -56,6 +56,7 @@ public: size_t this_data_size); inline const string &get_p3d_filename() const; + inline int get_instance_id() const; inline const string &get_session_key() const; inline const string &get_python_version() const; @@ -79,6 +80,7 @@ private: P3D_window_handle _parent_window; Tokens _tokens; + int _instance_id; string _session_key; string _python_version; P3DSession *_session; @@ -87,6 +89,8 @@ private: typedef deque Requests; Requests _pending_requests; + static int _next_instance_id; + friend class P3DSession; }; diff --git a/direct/src/plugin/p3dPythonRun.cxx b/direct/src/plugin/p3dPythonRun.cxx index 6b1db5b223..9185996a51 100755 --- a/direct/src/plugin/p3dPythonRun.cxx +++ b/direct/src/plugin/p3dPythonRun.cxx @@ -21,7 +21,8 @@ //////////////////////////////////////////////////////////////////// P3DPythonRun:: P3DPythonRun(int argc, char *argv[]) { - _read_thread_alive = false; + _read_thread_continue = false; + _program_continue = true; INIT_LOCK(_commands_lock); _program_name = argv[0]; @@ -101,6 +102,11 @@ run_python() { PyErr_Print(); return false; } + _exit = PyObject_GetAttrString(appmf, "exit"); + if (_exit == NULL) { + PyErr_Print(); + return false; + } _setupWindow = PyObject_GetAttrString(appmf, "setupWindow"); if (_setupWindow == NULL) { PyErr_Print(); @@ -174,12 +180,27 @@ run_python() { void P3DPythonRun:: handle_command(TiXmlDocument *doc) { cerr << "got command: " << *doc << "\n"; - TiXmlHandle h(doc); - TiXmlElement *xinstance = h.FirstChild("instance").ToElement(); - if (xinstance != (TiXmlElement *)NULL) { - P3DCInstance *inst = new P3DCInstance(xinstance); - start_instance(inst); - } + TiXmlElement *xcommand = doc->FirstChildElement("command"); + if (xcommand != NULL) { + const char *cmd = xcommand->Attribute("cmd"); + if (cmd != NULL) { + if (strcmp(cmd, "start_instance") == 0) { + TiXmlElement *xinstance = xcommand->FirstChildElement("instance"); + if (xinstance != (TiXmlElement *)NULL) { + P3DCInstance *inst = new P3DCInstance(xinstance); + start_instance(inst); + } + } else if (strcmp(cmd, "terminate_instance") == 0) { + int id; + if (xcommand->Attribute("id", &id)) { + terminate_instance(id); + } + + } else { + cerr << "Unhandled command " << cmd << "\n"; + } + } + } } //////////////////////////////////////////////////////////////////// @@ -203,6 +224,13 @@ check_comm() { delete doc; ACQUIRE_LOCK(_commands_lock); } + + if (!_program_continue) { + // The low-level thread detected an error, for instance pipe + // closed. We should exit gracefully. + terminate_session(); + } + RELEASE_LOCK(_commands_lock); } @@ -236,9 +264,9 @@ py_check_comm(PyObject *, PyObject *args) { //////////////////////////////////////////////////////////////////// void P3DPythonRun:: spawn_read_thread() { - assert(!_read_thread_alive); + assert(!_read_thread_continue); - _read_thread_alive = true; + _read_thread_continue = true; #ifdef _WIN32 _read_thread = CreateThread(NULL, 0, &win_rt_thread_run, this, 0, NULL); #endif @@ -252,7 +280,7 @@ spawn_read_thread() { void P3DPythonRun:: join_read_thread() { cerr << "waiting for thread\n"; - _read_thread_alive = false; + _read_thread_continue = false; #ifdef _WIN32 assert(_read_thread != NULL); @@ -272,7 +300,7 @@ join_read_thread() { void P3DPythonRun:: start_instance(P3DCInstance *inst) { cerr << "starting instance " << inst->get_p3d_filename() << "\n"; - _instances.push_back(inst); + _instances[inst->get_instance_id()] = inst; string window_type; switch (inst->_window_type) { @@ -310,6 +338,51 @@ start_instance(P3DCInstance *inst) { Py_XDECREF(result); } +//////////////////////////////////////////////////////////////////// +// Function: P3DPythonRun::terminate_instance +// Access: Private +// Description: Stops the instance with the indicated id. +//////////////////////////////////////////////////////////////////// +void P3DPythonRun:: +terminate_instance(int id) { + Instances::iterator ii = _instances.find(id); + if (ii == _instances.end()) { + cerr << "Can't stop instance " << id << ": not started.\n"; + return; + } + + P3DCInstance *inst = (*ii).second; + _instances.erase(ii); + delete inst; + + // TODO: we don't currently have any way to stop just one instance + // of a multi-instance session. We could maybe close its window or + // something. + terminate_session(); +} + + +//////////////////////////////////////////////////////////////////// +// Function: P3DPythonRun::terminate_session +// Access: Private +// Description: Stops all currently-running instances. +//////////////////////////////////////////////////////////////////// +void P3DPythonRun:: +terminate_session() { + Instances::iterator ii; + for (ii = _instances.begin(); ii != _instances.end(); ++ii) { + P3DCInstance *inst = (*ii).second; + delete inst; + } + _instances.clear(); + + PyObject *result = PyObject_CallFunction(_exit, ""); + if (result == NULL) { + PyErr_Print(); + } + Py_XDECREF(result); +} + //////////////////////////////////////////////////////////////////// // Function: P3DPythonRun::rt_thread_run // Access: Private @@ -318,13 +391,14 @@ start_instance(P3DCInstance *inst) { void P3DPythonRun:: rt_thread_run() { cerr << "thread reading.\n"; - while (_read_thread_alive) { + while (_read_thread_continue) { TiXmlDocument *doc = new TiXmlDocument; _pipe_read >> *doc; if (!_pipe_read || _pipe_read.eof()) { // Some error on reading. Abort. cerr << "Error on reading.\n"; + _program_continue = false; return; } diff --git a/direct/src/plugin/p3dPythonRun.h b/direct/src/plugin/p3dPythonRun.h index c2394e7ba7..fa2cd06291 100755 --- a/direct/src/plugin/p3dPythonRun.h +++ b/direct/src/plugin/p3dPythonRun.h @@ -18,7 +18,7 @@ #include #include #include -#include +#include #include #include #include @@ -63,6 +63,8 @@ private: void join_read_thread(); void start_instance(P3DCInstance *inst); + void terminate_instance(int id); + void terminate_session(); private: // This method runs only within the read thread. @@ -72,7 +74,7 @@ private: #endif private: - typedef vector Instances; + typedef map Instances; Instances _instances; string _program_name; @@ -80,6 +82,7 @@ private: char **_py_argv; PyObject *_runPackedApp; + PyObject *_exit; PyObject *_setupWindow; // The remaining members are manipulated by the read thread. @@ -90,7 +93,8 @@ private: HandleStream _pipe_read; HandleStream _pipe_write; - bool _read_thread_alive; + bool _read_thread_continue; + bool _program_continue; #ifdef _WIN32 HANDLE _read_thread; #endif diff --git a/direct/src/plugin/p3dSession.cxx b/direct/src/plugin/p3dSession.cxx index 63478cf5be..3e7e5bd0be 100644 --- a/direct/src/plugin/p3dSession.cxx +++ b/direct/src/plugin/p3dSession.cxx @@ -167,24 +167,17 @@ start_instance(P3DInstance *inst) { bool inserted = _instances.insert(inst).second; assert(inserted); - P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr(); - TiXmlDocument doc; TiXmlDeclaration *decl = new TiXmlDeclaration("1.0", "", ""); + TiXmlElement *xcommand = new TiXmlElement("command"); + xcommand->SetAttribute("cmd", "start_instance"); TiXmlElement *xinstance = inst->make_xml(); doc.LinkEndChild(decl); - doc.LinkEndChild(xinstance); + doc.LinkEndChild(xcommand); + xcommand->LinkEndChild(xinstance); - cerr << "sending: " << doc << "\n"; _pipe_write << doc << flush; - - /* - P3DPython *python = inst_mgr->start_python(_python_version); - if (python != NULL) { - python->start_session(this, inst); - } - */ } //////////////////////////////////////////////////////////////////// @@ -196,12 +189,16 @@ start_instance(P3DInstance *inst) { //////////////////////////////////////////////////////////////////// void P3DSession:: terminate_instance(P3DInstance *inst) { - /* - if (_python != NULL) { - _python->terminate_session(this); - assert(_python == NULL); - } - */ + TiXmlDocument doc; + TiXmlDeclaration *decl = new TiXmlDeclaration("1.0", "", ""); + TiXmlElement *xcommand = new TiXmlElement("command"); + xcommand->SetAttribute("cmd", "terminate_instance"); + xcommand->SetAttribute("id", inst->get_instance_id()); + + doc.LinkEndChild(decl); + doc.LinkEndChild(xcommand); + + _pipe_write << doc << flush; if (inst->_session == this) { inst->_session = NULL; diff --git a/direct/src/showbase/RunAppMF.py b/direct/src/showbase/RunAppMF.py index fed5de0b73..9b64a98db6 100644 --- a/direct/src/showbase/RunAppMF.py +++ b/direct/src/showbase/RunAppMF.py @@ -153,6 +153,9 @@ def add_check_comm(func, this): return taskMgr.add(func, "check_comm", sort = -50, extraArgs = [this], appendTask = True) +def exit(): + taskMgr.stop() + if __name__ == '__main__': try: runPackedApp(sys.argv[1:])