diff --git a/direct/src/plugin/Sources.pp b/direct/src/plugin/Sources.pp index 64d71086f1..06f7378a83 100644 --- a/direct/src/plugin/Sources.pp +++ b/direct/src/plugin/Sources.pp @@ -37,6 +37,8 @@ handleStream.cxx handleStream.h handleStream.I \ handleStreamBuf.cxx handleStreamBuf.h \ p3d_lock.h \ + p3dCInstance.cxx \ + p3dCInstance.h p3dCInstance.I \ p3dPythonRun.cxx p3dPythonRun.h p3dPythonRun.I #end bin_target diff --git a/direct/src/plugin/handleStreamBuf.cxx b/direct/src/plugin/handleStreamBuf.cxx index 53169d47af..465e39318d 100644 --- a/direct/src/plugin/handleStreamBuf.cxx +++ b/direct/src/plugin/handleStreamBuf.cxx @@ -258,7 +258,7 @@ read_chars(char *start, size_t length) { BOOL success = ReadFile(_handle, start, length, &bytes_read, NULL); if (!success) { DWORD error = GetLastError(); - if (error != ERROR_HANDLE_EOF) { + if (error != ERROR_HANDLE_EOF && error != ERROR_BROKEN_PIPE) { cerr << "Error reading " << length << " bytes, windows error code 0x" << hex @@ -311,10 +311,12 @@ write_chars(const char *start, size_t length) { BOOL success = WriteFile(_handle, start, length, &bytes_written, NULL); if (!success) { DWORD error = GetLastError(); - cerr - << "Error writing " << length - << " bytes, windows error code 0x" << hex - << error << dec << ".\n"; + if (error != ERROR_BROKEN_PIPE) { + cerr + << "Error writing " << length + << " bytes, windows error code 0x" << hex + << error << dec << ".\n"; + } return bytes_written; } assert(bytes_written == length); diff --git a/direct/src/plugin/p3dInstance.I b/direct/src/plugin/p3dInstance.I index 3f37f32e4c..dc653c36be 100644 --- a/direct/src/plugin/p3dInstance.I +++ b/direct/src/plugin/p3dInstance.I @@ -19,7 +19,7 @@ // Description: Returns the p3d filename that was passed to the // constructor. //////////////////////////////////////////////////////////////////// -INLINE const string &P3DInstance:: +inline const string &P3DInstance:: get_p3d_filename() const { return _p3d_filename; } @@ -35,7 +35,7 @@ get_p3d_filename() const { // each unique session required for different // P3DInstances. //////////////////////////////////////////////////////////////////// -INLINE const string &P3DInstance:: +inline const string &P3DInstance:: get_session_key() const { return _session_key; } @@ -46,7 +46,7 @@ get_session_key() const { // Description: Returns a string that uniquely identifies this // instance's required Python version. //////////////////////////////////////////////////////////////////// -INLINE const string &P3DInstance:: +inline const string &P3DInstance:: get_python_version() const { return _python_version; } diff --git a/direct/src/plugin/p3dInstance.cxx b/direct/src/plugin/p3dInstance.cxx index 3e4006059e..3252f7b128 100644 --- a/direct/src/plugin/p3dInstance.cxx +++ b/direct/src/plugin/p3dInstance.cxx @@ -38,7 +38,6 @@ P3DInstance(P3D_request_ready_func *func, _parent_window(parent_window) { P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr(); - cerr << "creating instance\n"; INIT_LOCK(_request_lock); @@ -192,6 +191,51 @@ feed_url_stream(int unique_id, size_t this_data_size) { } +//////////////////////////////////////////////////////////////////// +// Function: P3DInstance::make_xml +// Access: Public +// Description: Returns a newly-allocated XML structure that +// corresponds to the data within this instance. +//////////////////////////////////////////////////////////////////// +TiXmlElement *P3DInstance:: +make_xml() { + TiXmlElement *xinstance = new TiXmlElement("instance"); + xinstance->SetAttribute("p3d_filename", _p3d_filename); + + switch (_window_type) { + case P3D_WT_embedded: + xinstance->SetAttribute("window_type", "embedded"); + xinstance->SetAttribute("win_x", _win_x); + xinstance->SetAttribute("win_y", _win_y); + xinstance->SetAttribute("win_width", _win_width); + xinstance->SetAttribute("win_height", _win_height); +#ifdef _WIN32 + xinstance->SetAttribute("parent_hwnd", (int)_parent_window._hwnd); +#endif + break; + + case P3D_WT_toplevel: + xinstance->SetAttribute("window_type", "toplevel"); + xinstance->SetAttribute("win_x", _win_x); + xinstance->SetAttribute("win_y", _win_y); + xinstance->SetAttribute("win_width", _win_width); + xinstance->SetAttribute("win_height", _win_height); + break; + + case P3D_WT_fullscreen: + xinstance->SetAttribute("window_type", "fullscreen"); + xinstance->SetAttribute("win_width", _win_width); + xinstance->SetAttribute("win_height", _win_height); + break; + + case P3D_WT_hidden: + xinstance->SetAttribute("window_type", "hidden"); + break; + } + + return xinstance; +} + //////////////////////////////////////////////////////////////////// // Function: P3DInstance::fill_tokens // Access: Private @@ -211,3 +255,4 @@ fill_tokens(const P3D_token *tokens[], size_t tokens_size) { _tokens.push_back(token); } } + diff --git a/direct/src/plugin/p3dInstance.h b/direct/src/plugin/p3dInstance.h index 4977cab61a..d03bd84b08 100644 --- a/direct/src/plugin/p3dInstance.h +++ b/direct/src/plugin/p3dInstance.h @@ -19,12 +19,14 @@ #include #include +#include class P3DSession; //////////////////////////////////////////////////////////////////// // Class : P3DInstance -// Description : This is an instance of a Panda3D window. +// Description : This is an instance of a Panda3D window, as seen in +// the parent-level process. //////////////////////////////////////////////////////////////////// class P3DInstance : public P3D_instance { public: @@ -53,9 +55,11 @@ public: const unsigned char *this_data, size_t this_data_size); - INLINE const string &get_p3d_filename() const; - INLINE const string &get_session_key() const; - INLINE const string &get_python_version() const; + inline const string &get_p3d_filename() const; + inline const string &get_session_key() const; + inline const string &get_python_version() const; + + TiXmlElement *make_xml(); private: void fill_tokens(const P3D_token *tokens[], size_t tokens_size); diff --git a/direct/src/plugin/p3dPythonRun.cxx b/direct/src/plugin/p3dPythonRun.cxx index 4d4b751290..6b1db5b223 100755 --- a/direct/src/plugin/p3dPythonRun.cxx +++ b/direct/src/plugin/p3dPythonRun.cxx @@ -24,6 +24,18 @@ P3DPythonRun(int argc, char *argv[]) { _read_thread_alive = false; INIT_LOCK(_commands_lock); + _program_name = argv[0]; + _py_argc = 1; + _py_argv = (char **)malloc(2 * sizeof(char *)); + _py_argv[0] = argv[0]; + _py_argv[1] = NULL; + + // Initialize Python. It appears to be important to do this before + // we open the pipe streams and spawn the thread, below. + Py_SetProgramName((char *)_program_name.c_str()); + Py_Initialize(); + PySys_SetArgv(_py_argc, _py_argv); + // Open the pipe streams with the input and output handles from the // parent. #ifdef _WIN32 @@ -35,6 +47,7 @@ P3DPythonRun(int argc, char *argv[]) { if (!SetStdHandle(STD_OUTPUT_HANDLE, INVALID_HANDLE_VALUE)) { cerr << "unable to reset input handle\n"; } + _pipe_read.open_read(read); _pipe_write.open_write(write); #endif // _WIN32 @@ -46,17 +59,6 @@ P3DPythonRun(int argc, char *argv[]) { } spawn_read_thread(); - - _program_name = argv[0]; - _py_argc = 1; - _py_argv = (char **)malloc(2 * sizeof(char *)); - _py_argv[0] = argv[0]; - _py_argv[1] = NULL; - - // Initialize Python. - Py_SetProgramName((char *)_program_name.c_str()); - Py_Initialize(); - PySys_SetArgv(_py_argc, _py_argv); } //////////////////////////////////////////////////////////////////// @@ -172,6 +174,12 @@ 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); + } } //////////////////////////////////////////////////////////////////// @@ -255,6 +263,53 @@ join_read_thread() { cerr << "done waiting for thread\n"; } +//////////////////////////////////////////////////////////////////// +// Function: P3DPythonRun::start_instance +// Access: Private +// Description: Starts the indicated instance running within the +// Python process. +//////////////////////////////////////////////////////////////////// +void P3DPythonRun:: +start_instance(P3DCInstance *inst) { + cerr << "starting instance " << inst->get_p3d_filename() << "\n"; + _instances.push_back(inst); + + string window_type; + switch (inst->_window_type) { + case P3D_WT_embedded: + window_type = "embedded"; + break; + case P3D_WT_toplevel: + window_type = "toplevel"; + break; + case P3D_WT_fullscreen: + window_type = "fullscreen"; + break; + case P3D_WT_hidden: + window_type = "hidden"; + break; + } + + PyObject *result = PyObject_CallFunction + (_setupWindow, "siiiii", window_type.c_str(), + inst->_win_x, inst->_win_y, + inst->_win_width, inst->_win_height, +#ifdef _WIN32 + (int)(inst->_parent_window._hwnd) +#endif + ); + if (result == NULL) { + PyErr_Print(); + } + Py_XDECREF(result); + + result = PyObject_CallFunction(_runPackedApp, "[s]", inst->get_p3d_filename().c_str()); + if (result == NULL) { + PyErr_Print(); + } + Py_XDECREF(result); +} + //////////////////////////////////////////////////////////////////// // Function: P3DPythonRun::rt_thread_run // Access: Private @@ -262,8 +317,10 @@ join_read_thread() { //////////////////////////////////////////////////////////////////// void P3DPythonRun:: rt_thread_run() { + cerr << "thread reading.\n"; while (_read_thread_alive) { TiXmlDocument *doc = new TiXmlDocument; + _pipe_read >> *doc; if (!_pipe_read || _pipe_read.eof()) { // Some error on reading. Abort. diff --git a/direct/src/plugin/p3dPythonRun.h b/direct/src/plugin/p3dPythonRun.h index 318497a9fb..c2394e7ba7 100755 --- a/direct/src/plugin/p3dPythonRun.h +++ b/direct/src/plugin/p3dPythonRun.h @@ -18,11 +18,13 @@ #include #include #include +#include #include #include #include #include "p3d_lock.h" #include "handleStream.h" +#include "p3dCInstance.h" #ifdef _WIN32 #include @@ -60,6 +62,8 @@ private: void spawn_read_thread(); void join_read_thread(); + void start_instance(P3DCInstance *inst); + private: // This method runs only within the read thread. void rt_thread_run(); @@ -68,6 +72,17 @@ private: #endif private: + typedef vector Instances; + Instances _instances; + + string _program_name; + int _py_argc; + char **_py_argv; + + PyObject *_runPackedApp; + PyObject *_setupWindow; + + // The remaining members are manipulated by the read thread. typedef deque Commands; Commands _commands; LOCK _commands_lock; @@ -79,13 +94,6 @@ private: #ifdef _WIN32 HANDLE _read_thread; #endif - - string _program_name; - int _py_argc; - char **_py_argv; - - PyObject *_runPackedApp; - PyObject *_setupWindow; }; #include "p3dPythonRun.I" diff --git a/direct/src/plugin/p3dSession.cxx b/direct/src/plugin/p3dSession.cxx index 4d0b2607c1..63478cf5be 100644 --- a/direct/src/plugin/p3dSession.cxx +++ b/direct/src/plugin/p3dSession.cxx @@ -43,6 +43,9 @@ P3DSession(P3DInstance *inst) { // environment, if they are set. const char *keep[] = { "TEMP", "HOME", "USER", +#ifdef _WIN32 + "SYSTEMROOT", +#endif NULL }; for (int ki = 0; keep[ki] != NULL; ++ki) { @@ -66,36 +69,43 @@ P3DSession(P3DInstance *inst) { // Create a bi-directional pipe to communicate with the sub-process. #ifdef _WIN32 - cerr << "creating pipe\n"; - HANDLE orig_stdin = GetStdHandle(STD_INPUT_HANDLE); - HANDLE orig_stdout = GetStdHandle(STD_OUTPUT_HANDLE); - - SECURITY_ATTRIBUTES inheritable; - inheritable.nLength = sizeof(inheritable); - inheritable.lpSecurityDescriptor = NULL; - inheritable.bInheritHandle = true; - HANDLE r_to, w_to, r_from, w_from; // Create the pipe to the process. - if (!CreatePipe(&r_to, &w_to, &inheritable, 0)) { + if (!CreatePipe(&r_to, &w_to, NULL, 0)) { cerr << "failed to create pipe\n"; } else { - SetStdHandle(STD_INPUT_HANDLE, r_to); + // Make sure the right end of the pipe is inheritable. + SetHandleInformation(r_to, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT); + SetHandleInformation(w_to, HANDLE_FLAG_INHERIT, 0); } // Create the pipe from the process. - if (!CreatePipe(&r_from, &w_from, &inheritable, 0)) { + if (!CreatePipe(&r_from, &w_from, NULL, 0)) { cerr << "failed to create pipe\n"; - } else { - SetStdHandle(STD_OUTPUT_HANDLE, w_from); + } else { + // Make sure the right end of the pipe is inheritable. + SetHandleInformation(w_from, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT); + SetHandleInformation(r_from, HANDLE_FLAG_INHERIT, 0); } - cerr << "creating process " << p3dpython << "\n"; + // Make sure we see an error dialog if there is a missing DLL. + SetErrorMode(0); + + // Pass the appropriate ends of the bi-directional pipe as the + // standard input and standard output of the child process. + STARTUPINFO startup_info; + ZeroMemory(&startup_info, sizeof(STARTUPINFO)); + startup_info.cb = sizeof(startup_info); + startup_info.hStdError = GetStdHandle(STD_ERROR_HANDLE); + startup_info.hStdOutput = w_from; + startup_info.hStdInput = r_to; + startup_info.dwFlags |= STARTF_USESTDHANDLES; + BOOL result = CreateProcess (p3dpython.c_str(), NULL, NULL, NULL, TRUE, 0, - /* (void *)env.c_str() */NULL, _python_root_dir.c_str(), NULL, &_p3dpython); - cerr << "result = " << result << "\n"; + (void *)env.c_str(), _python_root_dir.c_str(), + &startup_info, &_p3dpython); _started_p3dpython = (result != 0); if (!_started_p3dpython) { @@ -104,10 +114,9 @@ P3DSession(P3DInstance *inst) { cerr << "Created process: " << _p3dpython.dwProcessId << "\n"; } - CloseHandle(r_to); + // Close the pipe handles that are now owned by the child. CloseHandle(w_from); - SetStdHandle(STD_INPUT_HANDLE, orig_stdin); - SetStdHandle(STD_OUTPUT_HANDLE, orig_stdout); + CloseHandle(r_to); _pipe_read.open_read(r_from); _pipe_write.open_write(w_to); @@ -162,11 +171,13 @@ start_instance(P3DInstance *inst) { TiXmlDocument doc; TiXmlDeclaration *decl = new TiXmlDeclaration("1.0", "", ""); - TiXmlElement *element = new TiXmlElement("instance"); + TiXmlElement *xinstance = inst->make_xml(); + doc.LinkEndChild(decl); - doc.LinkEndChild(element); + doc.LinkEndChild(xinstance); - _pipe_write << doc; + cerr << "sending: " << doc << "\n"; + _pipe_write << doc << flush; /* P3DPython *python = inst_mgr->start_python(_python_version); diff --git a/direct/src/plugin/panda3d.cxx b/direct/src/plugin/panda3d.cxx index dfbdc42c0c..8ec3128e56 100644 --- a/direct/src/plugin/panda3d.cxx +++ b/direct/src/plugin/panda3d.cxx @@ -16,6 +16,7 @@ #include #include +#include #ifdef _WIN32 #include "wingetopt.h" @@ -301,26 +302,63 @@ main(int argc, char *argv[]) { return 1; } + int num_instances = argc - 1; + P3D_window_handle parent_window; if (window_type == P3D_WT_embedded) { // The user asked for an embedded window. Create a toplevel // window to be its parent, of the requested size. + if (win_width == 0 && win_height == 0) { + win_width = 640; + win_height = 480; + } + make_parent_window(parent_window, win_width, win_height); + + // Center the child window(s) within the parent window. + RECT rect; + GetClientRect(parent_window._hwnd, &rect); - // And center the graphics window within that parent window. - win_x = (int)(win_width * 0.1); - win_y = (int)(win_height * 0.1); - win_width = (int)(win_width * 0.8); - win_height = (int)(win_height * 0.8); + win_x = (int)(rect.right * 0.1); + win_y = (int)(rect.bottom * 0.1); + win_width = (int)(rect.right * 0.8); + win_height = (int)(rect.bottom * 0.8); + + // Subdivide the window into num_x_spans * num_y_spans sub-windows. + int num_y_spans = int(sqrt((double)num_instances)); + int num_x_spans = (num_instances + num_y_spans - 1) / num_y_spans; + + int inst_width = win_width / num_x_spans; + int inst_height = win_height / num_y_spans; + + for (int yi = 0; yi < num_y_spans; ++yi) { + for (int xi = 0; xi < num_x_spans; ++xi) { + int i = yi * num_x_spans + xi; + if (i >= num_instances) { + continue; + } + + // Create instance i at window slot (xi, yi). + int inst_x = win_x + xi * inst_width; + int inst_y = win_y + yi * inst_height; + + P3D_create_instance + (NULL, argv[i + 1], + P3D_WT_embedded, inst_x, inst_y, inst_width, inst_height, parent_window, + NULL, 0); + } + } + + } else { + // Not an embedded window. Create each window with the same parameters. + for (int i = 0; i < num_instances; ++i) { + P3D_create_instance + (NULL, argv[i + 1], + window_type, win_x, win_y, win_width, win_height, parent_window, + NULL, 0); + } } - // For now, only one instance at a time is supported. Ignore the - // remaining command-line parameters. - P3D_create_instance - (NULL, argv[1], - window_type, win_x, win_y, win_width, win_height, parent_window, - NULL, 0); - #ifdef _WIN32 // Wait for new messages from Windows, and new requests from the // plugin.