mirror of
https://github.com/panda3d/panda3d.git
synced 2025-10-01 01:07:51 -04:00
support python in a child process, multiple pythons now possible
This commit is contained in:
parent
7b00b88f29
commit
10d51dafb6
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -19,12 +19,14 @@
|
||||
|
||||
#include <vector>
|
||||
#include <deque>
|
||||
#include <tinyxml.h>
|
||||
|
||||
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);
|
||||
|
@ -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.
|
||||
|
@ -18,11 +18,13 @@
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <deque>
|
||||
#include <vector>
|
||||
#include <assert.h>
|
||||
#include <Python.h>
|
||||
#include <tinyxml.h>
|
||||
#include "p3d_lock.h"
|
||||
#include "handleStream.h"
|
||||
#include "p3dCInstance.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
@ -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<P3DCInstance *> 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<TiXmlDocument *> 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"
|
||||
|
@ -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);
|
||||
|
@ -16,6 +16,7 @@
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <math.h>
|
||||
|
||||
#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.
|
||||
|
Loading…
x
Reference in New Issue
Block a user