From 027e5fdc9e81dbe1e1a8063cc8c4eea2de789974 Mon Sep 17 00:00:00 2001 From: David Rose Date: Mon, 8 Jun 2009 18:08:53 +0000 Subject: [PATCH] more plugin work --- direct/src/plugin/Sources.pp | 19 +- direct/src/plugin/handleStream.I | 74 ++++ direct/src/plugin/handleStream.cxx | 15 + direct/src/plugin/handleStream.h | 42 +++ direct/src/plugin/handleStreamBuf.I | 14 + direct/src/plugin/handleStreamBuf.cxx | 339 ++++++++++++++++++ direct/src/plugin/handleStreamBuf.h | 64 ++++ direct/src/plugin/p3dInstance.cxx | 7 +- direct/src/plugin/p3dInstance.h | 1 - direct/src/plugin/p3dInstanceManager.cxx | 44 +-- direct/src/plugin/p3dInstanceManager.h | 4 - direct/src/plugin/p3dPython.I | 49 --- direct/src/plugin/p3dPython.cxx | 372 -------------------- direct/src/plugin/p3dPython.h | 125 ------- direct/src/plugin/p3dPythonRun.I | 14 + direct/src/plugin/p3dPythonRun.cxx | 308 ++++++++++++++++ direct/src/plugin/p3dPythonRun.h | 94 +++++ direct/src/plugin/p3dSession.cxx | 110 +++++- direct/src/plugin/p3dSession.h | 11 +- direct/src/plugin/p3d_lock.h | 30 ++ direct/src/plugin/p3d_plugin_common.h | 14 +- direct/src/plugin/p3d_plugin_composite1.cxx | 1 - direct/src/plugin/wingetopt.c | 3 +- direct/src/showbase/RunAppMF.py | 10 +- 24 files changed, 1145 insertions(+), 619 deletions(-) create mode 100644 direct/src/plugin/handleStream.I create mode 100644 direct/src/plugin/handleStream.cxx create mode 100644 direct/src/plugin/handleStream.h create mode 100755 direct/src/plugin/handleStreamBuf.I create mode 100644 direct/src/plugin/handleStreamBuf.cxx create mode 100644 direct/src/plugin/handleStreamBuf.h delete mode 100644 direct/src/plugin/p3dPython.I delete mode 100644 direct/src/plugin/p3dPython.cxx delete mode 100644 direct/src/plugin/p3dPython.h create mode 100755 direct/src/plugin/p3dPythonRun.I create mode 100755 direct/src/plugin/p3dPythonRun.cxx create mode 100755 direct/src/plugin/p3dPythonRun.h create mode 100755 direct/src/plugin/p3d_lock.h diff --git a/direct/src/plugin/Sources.pp b/direct/src/plugin/Sources.pp index 16f9b28ce4..64d71086f1 100644 --- a/direct/src/plugin/Sources.pp +++ b/direct/src/plugin/Sources.pp @@ -3,24 +3,25 @@ #define BUILD_DIRECTORY $[HAVE_P3D_PLUGIN] #begin lib_target + #define USE_PACKAGES tinyxml #define TARGET p3d_plugin #define COMBINED_SOURCES \ $[TARGET]_composite1.cxx #define SOURCES \ - p3d_plugin.h \ + handleStream.cxx handleStream.h handleStream.I \ + handleStreamBuf.cxx handleStreamBuf.h \ + p3d_lock.h p3d_plugin.h \ p3d_plugin_common.h \ p3dInstance.h p3dInstance.I \ p3dInstanceManager.h p3dInstanceManager.I \ - p3dPython.h p3dPython.I \ p3dSession.h p3dSession.I #define INCLUDED_SOURCES \ p3d_plugin.cxx \ p3dInstance.cxx \ p3dInstanceManager.cxx \ - p3dPython.cxx \ p3dSession.cxx #define INSTALL_HEADERS \ @@ -28,6 +29,18 @@ #end lib_target +#begin bin_target + #define USE_PACKAGES tinyxml + #define TARGET p3dpython + + #define SOURCES \ + handleStream.cxx handleStream.h handleStream.I \ + handleStreamBuf.cxx handleStreamBuf.h \ + p3d_lock.h \ + p3dPythonRun.cxx p3dPythonRun.h p3dPythonRun.I + +#end bin_target + #begin bin_target #define TARGET panda3d diff --git a/direct/src/plugin/handleStream.I b/direct/src/plugin/handleStream.I new file mode 100644 index 0000000000..5e26f288e9 --- /dev/null +++ b/direct/src/plugin/handleStream.I @@ -0,0 +1,74 @@ +// Filename: handleStream.I +// Created by: drose (05Jun09) +// +//////////////////////////////////////////////////////////////////// +// +// 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: HandleStream::Constructor +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// +inline HandleStream:: +HandleStream() : iostream(&_buf) { +} + +//////////////////////////////////////////////////////////////////// +// Function: HandleStream::Destructor +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// +inline HandleStream:: +~HandleStream() { + close(); +} + +//////////////////////////////////////////////////////////////////// +// Function: HandleStream::open_read +// Access: Published +// Description: Attempts to open the given handle for input. The +// stream may not be simultaneously open for input and +// output. +//////////////////////////////////////////////////////////////////// +inline void HandleStream:: +open_read(Handle handle) { + clear((ios::iostate)0); + _buf.open_read(handle); + if (!_buf.is_open_read()) { + clear(ios::failbit); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: HandleStream::open_write +// Access: Published +// Description: Attempts to open the given handle for output. The +// stream may not be simultaneously open for input and +// output. +//////////////////////////////////////////////////////////////////// +inline void HandleStream:: +open_write(Handle handle) { + clear((ios::iostate)0); + _buf.open_write(handle); + if (!_buf.is_open_write()) { + clear(ios::failbit); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: HandleStream::close +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// +inline void HandleStream:: +close() { + _buf.close(); +} diff --git a/direct/src/plugin/handleStream.cxx b/direct/src/plugin/handleStream.cxx new file mode 100644 index 0000000000..36ed98522a --- /dev/null +++ b/direct/src/plugin/handleStream.cxx @@ -0,0 +1,15 @@ +// Filename: handleStream.cxx +// Created by: drose (05Jun09) +// +//////////////////////////////////////////////////////////////////// +// +// 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 "handleStream.h" diff --git a/direct/src/plugin/handleStream.h b/direct/src/plugin/handleStream.h new file mode 100644 index 0000000000..5eafcb11a4 --- /dev/null +++ b/direct/src/plugin/handleStream.h @@ -0,0 +1,42 @@ +// Filename: handleStream.h +// Created by: drose (05Jun09) +// +//////////////////////////////////////////////////////////////////// +// +// 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 HANDLESTREAM_H +#define HANDLESTREAM_H + +#include "handleStreamBuf.h" + +//////////////////////////////////////////////////////////////////// +// Class : HandleStream +// Description : Implements a C++ stream object suitable for reading +// from and writing to Windows' HANDLE objects, or Posix +// file descriptors. This is necessary to map low-level +// pipes into an iostream for tinyxml. +//////////////////////////////////////////////////////////////////// +class HandleStream : public iostream { +public: + inline HandleStream(); + inline ~HandleStream(); + + inline void open_read(Handle handle); + inline void open_write(Handle handle); + inline void close(); + +private: + HandleStreamBuf _buf; +}; + +#include "handleStream.I" + +#endif diff --git a/direct/src/plugin/handleStreamBuf.I b/direct/src/plugin/handleStreamBuf.I new file mode 100755 index 0000000000..22dff6f076 --- /dev/null +++ b/direct/src/plugin/handleStreamBuf.I @@ -0,0 +1,14 @@ +// Filename: handleStreamBuf.I +// Created by: drose (05Jun09) +// +//////////////////////////////////////////////////////////////////// +// +// 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." +// +//////////////////////////////////////////////////////////////////// + diff --git a/direct/src/plugin/handleStreamBuf.cxx b/direct/src/plugin/handleStreamBuf.cxx new file mode 100644 index 0000000000..53169d47af --- /dev/null +++ b/direct/src/plugin/handleStreamBuf.cxx @@ -0,0 +1,339 @@ +// Filename: handleStreamBuf.cxx +// Created by: drose (05Jun09) +// +//////////////////////////////////////////////////////////////////// +// +// 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 "handleStreamBuf.h" + +#include + +#ifndef _WIN32 +#include +#include +#include +#include +#endif // _WIN32 + +static const size_t handle_buffer_size = 4096; + +//////////////////////////////////////////////////////////////////// +// Function: HandleStreamBuf::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +HandleStreamBuf:: +HandleStreamBuf() { + _is_open_read = false; + _is_open_write = false; + +#ifdef _WIN32 + // Windows case. + _handle = NULL; +#else + _handle = -1; +#endif // _WIN32 + + _buffer = new char[handle_buffer_size]; + char *ebuf = _buffer + handle_buffer_size; + setg(_buffer, ebuf, ebuf); + setp(_buffer, ebuf); +} + +//////////////////////////////////////////////////////////////////// +// Function: HandleStreamBuf::Destructor +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +HandleStreamBuf:: +~HandleStreamBuf() { + close(); + + delete[] _buffer; +} + +//////////////////////////////////////////////////////////////////// +// Function: HandleStreamBuf::open_read +// Access: Public +// Description: Attempts to open the given handle for input. The +// stream may not be simultaneously open for input and +// output. +//////////////////////////////////////////////////////////////////// +void HandleStreamBuf:: +open_read(Handle handle) { + close(); + + _handle = handle; + _is_open_read = true; +} + +//////////////////////////////////////////////////////////////////// +// Function: HandleStreamBuf::open_write +// Access: Public +// Description: Attempts to open the given handle for output. The +// stream may not be simultaneously open for input and +// output. +//////////////////////////////////////////////////////////////////// +void HandleStreamBuf:: +open_write(Handle handle) { + close(); + + _handle = handle; + _is_open_write = true; +} + +//////////////////////////////////////////////////////////////////// +// Function: HandleStreamBuf::is_open_read +// Access: Public +// Description: Returns true if the file is open for input, false +// otherwise. +//////////////////////////////////////////////////////////////////// +bool HandleStreamBuf:: +is_open_read() const { + return _is_open_read; +} + +//////////////////////////////////////////////////////////////////// +// Function: HandleStreamBuf::is_open_write +// Access: Public +// Description: Returns true if the file is open for output, false +// otherwise. +//////////////////////////////////////////////////////////////////// +bool HandleStreamBuf:: +is_open_write() const { + return _is_open_write; +} + +//////////////////////////////////////////////////////////////////// +// Function: HandleStreamBuf::close +// Access: Public +// Description: Empties the buffer and closes the file. +//////////////////////////////////////////////////////////////////// +void HandleStreamBuf:: +close() { + // Make sure the write buffer is flushed. + sync(); + +#ifdef _WIN32 + if (_handle != NULL) { + CloseHandle(_handle); + } + _handle = NULL; +#else + if (_handle != -1) { + ::close(_handle); + } + _handle = -1; +#endif // _WIN32 + + _is_open_read = false; + _is_open_write = false; + + pbump(pbase() - pptr()); + gbump(egptr() - gptr()); +} + +//////////////////////////////////////////////////////////////////// +// Function: HandleStreamBuf::overflow +// Access: Protected, Virtual +// Description: Called by the system ostream implementation when its +// internal buffer is filled, plus one character. +//////////////////////////////////////////////////////////////////// +int HandleStreamBuf:: +overflow(int ch) { + bool okflag = true; + + size_t n = pptr() - pbase(); + if (n != 0) { + size_t wrote = write_chars(pbase(), n); + pbump(-(int)wrote); + if (wrote != n) { + okflag = false; + } + } + + if (okflag && ch != EOF) { + if (pptr() != epptr()) { + // Store the extra character back in the buffer. + *(pptr()) = ch; + pbump(1); + } else { + // No room to store ch. + okflag = false; + } + } + + if (!okflag) { + return EOF; + } + return 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: HandleStreamBuf::sync +// Access: Protected, Virtual +// Description: Called by the system iostream implementation to +// implement a flush operation. +//////////////////////////////////////////////////////////////////// +int HandleStreamBuf:: +sync() { + size_t n = pptr() - pbase(); + + size_t wrote = write_chars(pbase(), n); + pbump(-(int)wrote); + + if (n != wrote) { + return EOF; + } + return 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: HandleStreamBuf::underflow +// Access: Protected, Virtual +// Description: Called by the system istream implementation when its +// internal buffer needs more characters. +//////////////////////////////////////////////////////////////////// +int HandleStreamBuf:: +underflow() { + // Sometimes underflow() is called even if the buffer is not empty. + if (gptr() >= egptr()) { + // Mark the buffer filled (with buffer_size bytes). + size_t buffer_size = egptr() - eback(); + gbump(-(int)buffer_size); + + size_t num_bytes = buffer_size; + size_t read_count = read_chars(gptr(), buffer_size); + + if (read_count != num_bytes) { + // Oops, we didn't read what we thought we would. + if (read_count == 0) { + gbump(num_bytes); + return EOF; + } + + // Slide what we did read to the top of the buffer. + assert(read_count < num_bytes); + size_t delta = num_bytes - read_count; + memmove(gptr() + delta, gptr(), read_count); + gbump(delta); + } + } + + return (unsigned char)*gptr(); +} + +//////////////////////////////////////////////////////////////////// +// Function: HandleStreamBuf::read_chars +// Access: Private +// Description: Attempts to extract the indicated number of +// characters from the current file position. Returns +// the number of characters extracted. +//////////////////////////////////////////////////////////////////// +size_t HandleStreamBuf:: +read_chars(char *start, size_t length) { + if (length == 0 || !_is_open_read) { + // Trivial no-op. + return 0; + } + + // Make sure the write buffer is flushed. + sync(); + + if (length == 0) { + return 0; + } + +#ifdef _WIN32 + // Windows case. + DWORD bytes_read = 0; + BOOL success = ReadFile(_handle, start, length, &bytes_read, NULL); + if (!success) { + DWORD error = GetLastError(); + if (error != ERROR_HANDLE_EOF) { + cerr + << "Error reading " << length + << " bytes, windows error code 0x" << hex + << error << dec << ".\n"; + return 0; + } + } + + length = bytes_read; + +#else + // Posix case. + ssize_t result = ::read(_handle, start, length); + if (result < 0) { + cerr + << "Error reading " << length << " bytes\n"; + return 0; + } + + length = result; +#endif // _WIN32 + + return length; +} + +//////////////////////////////////////////////////////////////////// +// Function: HandleStreamBuf::write_chars +// Access: Private +// Description: Outputs the indicated stream of characters to the +// current file position. +//////////////////////////////////////////////////////////////////// +size_t HandleStreamBuf:: +write_chars(const char *start, size_t length) { + if (length == 0) { + // Trivial no-op. + return 0; + } + + // Make sure the read buffer is flushed. + size_t n = egptr() - gptr(); + gbump(n); + + if (length == 0 || !_is_open_write) { + return 0; + } + +#ifdef _WIN32 + // Windows case. + DWORD bytes_written = 0; + 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"; + return bytes_written; + } + assert(bytes_written == length); + +#else + // Posix case. + size_t remaining = length; + while (remaining > 0) { + ssize_t result = ::write(_handle, start, remaining); + if (result < 0) { + cerr + << "Error writing " << remaining << " bytes\n"; + return length - remaining; + } + + start += result; + remaining -= result; + } +#endif // _WIN32 + + return length; +} diff --git a/direct/src/plugin/handleStreamBuf.h b/direct/src/plugin/handleStreamBuf.h new file mode 100644 index 0000000000..f5245467fa --- /dev/null +++ b/direct/src/plugin/handleStreamBuf.h @@ -0,0 +1,64 @@ +// Filename: handleStreamBuf.h +// Created by: drose (05Jun09) +// +//////////////////////////////////////////////////////////////////// +// +// 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 HANDLESTREAMBUF_H +#define HANDLESTREAMBUF_H + +#ifdef _WIN32 +#include +typedef HANDLE Handle; +#else +// On POSIX, we use a file descriptor as a "handle". +typedef int Handle; +#endif + +#include +#include + +using namespace std; + +//////////////////////////////////////////////////////////////////// +// Class : HandleStreamBuf +// Description : +//////////////////////////////////////////////////////////////////// +class HandleStreamBuf : public streambuf { +public: + HandleStreamBuf(); + virtual ~HandleStreamBuf(); + + void open_read(Handle handle); + void open_write(Handle handle); + bool is_open_read() const; + bool is_open_write() const; + void close(); + +protected: + virtual int overflow(int c); + virtual int sync(); + virtual int underflow(); + +private: + size_t read_chars(char *start, size_t length); + size_t write_chars(const char *start, size_t length); + +private: + bool _is_open_read; + bool _is_open_write; + + Handle _handle; + + char *_buffer; +}; + +#endif diff --git a/direct/src/plugin/p3dInstance.cxx b/direct/src/plugin/p3dInstance.cxx index 0a7a3d5d86..3e4006059e 100644 --- a/direct/src/plugin/p3dInstance.cxx +++ b/direct/src/plugin/p3dInstance.cxx @@ -38,16 +38,15 @@ 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); - // For the moment, all sessions will be shared. - /* + // For the moment, all sessions will be unique. ostringstream strm; strm << inst_mgr->get_unique_session_index(); _session_key = strm.str(); - */ - _session_key = "common"; + _python_version = "python24"; _session = NULL; diff --git a/direct/src/plugin/p3dInstance.h b/direct/src/plugin/p3dInstance.h index f8ac023aac..4977cab61a 100644 --- a/direct/src/plugin/p3dInstance.h +++ b/direct/src/plugin/p3dInstance.h @@ -84,7 +84,6 @@ private: Requests _pending_requests; friend class P3DSession; - friend class P3DPython; }; #include "p3dInstance.I" diff --git a/direct/src/plugin/p3dInstanceManager.cxx b/direct/src/plugin/p3dInstanceManager.cxx index 208eb39c1b..578744ee6e 100644 --- a/direct/src/plugin/p3dInstanceManager.cxx +++ b/direct/src/plugin/p3dInstanceManager.cxx @@ -15,7 +15,6 @@ #include "p3dInstanceManager.h" #include "p3dInstance.h" #include "p3dSession.h" -#include "p3dPython.h" P3DInstanceManager *P3DInstanceManager::_global_ptr; @@ -26,8 +25,8 @@ P3DInstanceManager *P3DInstanceManager::_global_ptr; //////////////////////////////////////////////////////////////////// P3DInstanceManager:: P3DInstanceManager() { + cerr << "creating instance manager\n"; _unique_session_index = 0; - _current_python = NULL; _request_seq = 0; #ifdef _WIN32 @@ -172,47 +171,6 @@ wait_request() { } } -//////////////////////////////////////////////////////////////////// -// Function: P3DInstanceManager::start_python -// Access: Public -// Description: Returns a P3DPython object corresponding to the -// requested Python version. The P3DPython object is -// loaded into the current memory address space; because -// of Python version conflicts, only one version of -// Python may be resident at a given time. Thus, -// calling this will implicitly terminate any running -// instances that are currently using a different -// version of Python. -// -// This may return NULL if the Python interpreter cannot -// be successfully started for some reason. -//////////////////////////////////////////////////////////////////// -P3DPython *P3DInstanceManager:: -start_python(const string &python_version) { - if (_current_python != NULL && !_current_python->is_valid()) { - // The current python has gone bad. - delete _current_python; - _current_python = NULL; - } - - if (_current_python != NULL && _current_python->get_python_version() != python_version) { - // The current python is the wrong version. We should kill it and - // all of the instances using it. TODO. - return NULL; - } - - if (_current_python == NULL) { - _current_python = new P3DPython(python_version); - if (!_current_python->is_valid()) { - // Couldn't successfully start Python. - delete _current_python; - _current_python = NULL; - } - } - - return _current_python; -} - //////////////////////////////////////////////////////////////////// // Function: P3DInstanceManager::get_unique_session_index // Access: Public diff --git a/direct/src/plugin/p3dInstanceManager.h b/direct/src/plugin/p3dInstanceManager.h index cfec88606b..d5c9103a60 100644 --- a/direct/src/plugin/p3dInstanceManager.h +++ b/direct/src/plugin/p3dInstanceManager.h @@ -22,7 +22,6 @@ class P3DInstance; class P3DSession; -class P3DPython; //////////////////////////////////////////////////////////////////// // Class : P3DInstanceManager @@ -52,8 +51,6 @@ public: P3DInstance *check_request(); void wait_request(); - P3DPython *start_python(const string &python_version); - INLINE int get_num_instances() const; INLINE const string &get_dll_filename() const; @@ -73,7 +70,6 @@ private: typedef map Sessions; Sessions _sessions; - P3DPython *_current_python; int _unique_session_index; // Implements a condition-var like behavior. diff --git a/direct/src/plugin/p3dPython.I b/direct/src/plugin/p3dPython.I deleted file mode 100644 index 900bb81445..0000000000 --- a/direct/src/plugin/p3dPython.I +++ /dev/null @@ -1,49 +0,0 @@ -// Filename: p3dPython.I -// Created by: drose (04Jun09) -// -//////////////////////////////////////////////////////////////////// -// -// 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: P3DPython::get_python_version -// Access: Public -// Description: Returns a string that uniquely identifies this -// Python version. You cannot have two different -// versions of Python loaded simultaneously within the -// same address space. -//////////////////////////////////////////////////////////////////// -INLINE const string &P3DPython:: -get_python_version() const { - return _python_version; -} - -//////////////////////////////////////////////////////////////////// -// Function: P3DPython::get_num_sessions -// Access: Public -// Description: Returns the number of sessions currently sharing -// this Python address space. -//////////////////////////////////////////////////////////////////// -INLINE int P3DPython:: -get_num_sessions() const { - return _sessions.size(); -} - -//////////////////////////////////////////////////////////////////// -// Function: P3DPython::is_valid -// Access: Public -// Description: Returns true if the Python interpreter is up and -// ready to run code, false otherwise. -//////////////////////////////////////////////////////////////////// -INLINE bool P3DPython:: -is_valid() const { - return _is_valid; -} diff --git a/direct/src/plugin/p3dPython.cxx b/direct/src/plugin/p3dPython.cxx deleted file mode 100644 index e69aa6356f..0000000000 --- a/direct/src/plugin/p3dPython.cxx +++ /dev/null @@ -1,372 +0,0 @@ -// Filename: p3dPython.cxx -// Created by: drose (04Jun09) -// -//////////////////////////////////////////////////////////////////// -// -// 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 "p3dPython.h" -#include "p3dSession.h" -#include "p3dInstanceManager.h" - -#include - -//////////////////////////////////////////////////////////////////// -// Function: P3DPython::Constructor -// Access: Public -// Description: -//////////////////////////////////////////////////////////////////// -P3DPython:: -P3DPython(const string &python_version) { - _python_version = python_version; - _is_valid = false; - - _runPackedApp = NULL; - _setupWindow = NULL; - _run = NULL; - - - P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr(); - - _program_name = inst_mgr->get_dll_filename(); - - size_t slash = _program_name.rfind('/'); - -#ifdef _WIN32 - // Windows tolerates slashes or backslashes. Look for the rightmost - // of either. - size_t backslash = _program_name.rfind('\\'); - if (backslash != string::npos && (slash == string::npos || backslash > slash)) { - slash = backslash; - } -#endif - - // _root_dir = _program_name.substr(0, slash); - _root_dir = "C:/p3drun"; - - _py_argc = 1; - _py_argv = (char **)malloc(2 * sizeof(char *)); - _py_argv[0] = (char *)_program_name.c_str(); - _py_argv[1] = NULL; - - // Guess everything's OK. - _is_valid = true; -} - -//////////////////////////////////////////////////////////////////// -// Function: P3DPython::Destructor -// Access: Public -// Description: Terminates the python by shutting down Python and -// stopping the subprocess. -//////////////////////////////////////////////////////////////////// -P3DPython:: -~P3DPython() { - if (_python_module != NULL) { - if (_is_valid) { - Py_XDECREF(_runPackedApp); - Py_XDECREF(_setupWindow); - Py_XDECREF(_run); - } - - Py_Finalize(); - FreeLibrary(_python_module); - _python_module = NULL; - } -} - -//////////////////////////////////////////////////////////////////// -// Function: P3DPython::start_session -// Access: Public -// Description: Starts the indicated session running within the -// Python interpreter. For the moment, each session -// must contain only a single P3DInstance, which is also -// started. -//////////////////////////////////////////////////////////////////// -void P3DPython:: -start_session(P3DSession *session, P3DInstance *inst) { - assert(_is_valid); - - assert(session->_python == NULL); - assert(session->get_python_version() == _python_version); - - // For now, only one session at a time is allowed. - assert(_sessions.empty()); - - session->_python = this; - bool inserted = _sessions.insert(session).second; - assert(inserted); - - _inst = inst; - spawn_thread(); -} - -//////////////////////////////////////////////////////////////////// -// Function: P3DPython::terminate_session -// Access: Public -// Description: Removes the indicated session from the python, and -// stops it. It is an error if the session is not -// already running on this python. -//////////////////////////////////////////////////////////////////// -void P3DPython:: -terminate_session(P3DSession *session) { - join_thread(); - - if (session->_python == this) { - session->_python = NULL; - _sessions.erase(session); - } -} - -//////////////////////////////////////////////////////////////////// -// Function: P3DPython::spawn_thread -// Access: Private -// Description: Starts the sub-thread. All calls to Python are made -// within the sub-thread. -//////////////////////////////////////////////////////////////////// -void P3DPython:: -spawn_thread() { -#ifdef _WIN32 - _thread = CreateThread(NULL, 0, &win_thread_func, this, 0, NULL); -#endif -} - -//////////////////////////////////////////////////////////////////// -// Function: P3DPython::join_thread -// Access: Private -// Description: Waits for the sub-thread to stop. -//////////////////////////////////////////////////////////////////// -void P3DPython:: -join_thread() { - cerr << "waiting for thread\n"; - -#ifdef _WIN32 - assert(_thread != NULL); - WaitForSingleObject(_thread, INFINITE); - CloseHandle(_thread); - _thread = NULL; -#endif - cerr << "done waiting for thread\n"; -} - - -//////////////////////////////////////////////////////////////////// -// Function: P3DPython::tr_init_python -// Access: Private -// Description: Initializes the Python interpreter. This method, and -// all methods that interact with Python, is called only -// within the sub-thread. Returns true if successful, -// false on failure. -//////////////////////////////////////////////////////////////////// -bool P3DPython:: -tr_init_python() { -#ifdef _WIN32 - string python_dll_filename = _root_dir + string("/python24.dll"); - _python_module = LoadLibrary(python_dll_filename.c_str()); - if (_python_module == NULL) { - // Couldn't load Python. - cerr << "Unable to load " << python_dll_filename << "\n"; - return false; - } - - Py_SetProgramName = (Py_SetProgramName_func *)GetProcAddress(_python_module, "Py_SetProgramName"); - PySys_SetArgv = (PySys_SetArgv_func *)GetProcAddress(_python_module, "PySys_SetArgv"); - Py_SetPythonHome = (Py_SetPythonHome_func *)GetProcAddress(_python_module, "Py_SetPythonHome"); - Py_Initialize = (Py_Initialize_func *)GetProcAddress(_python_module, "Py_Initialize"); - Py_Finalize = (Py_Finalize_func *)GetProcAddress(_python_module, "Py_Finalize"); - PyEval_InitThreads = (PyEval_InitThreads_func *)GetProcAddress(_python_module, "PyEval_InitThreads"); - PyEval_AcquireLock = (PyEval_AcquireLock_func *)GetProcAddress(_python_module, "PyEval_AcquireLock"); - PyEval_ReleaseLock = (PyEval_ReleaseLock_func *)GetProcAddress(_python_module, "PyEval_ReleaseLock"); - PyRun_SimpleString = (PyRun_SimpleString_func *)GetProcAddress(_python_module, "PyRun_SimpleString"); - PyErr_Print = (PyErr_Print_func *)GetProcAddress(_python_module, "PyErr_Print"); - Py_XDECREF = (Py_XDECREF_func *)GetProcAddress(_python_module, "Py_DecRef"); - PyImport_ImportModule = (PyImport_ImportModule_func *)GetProcAddress(_python_module, "PyImport_ImportModule"); - PyObject_SetAttrString = (PyObject_SetAttrString_func *)GetProcAddress(_python_module, "PyObject_SetAttrString"); - PyObject_GetAttrString = (PyObject_GetAttrString_func *)GetProcAddress(_python_module, "PyObject_GetAttrString"); - Py_BuildValue = (Py_BuildValue_func *)GetProcAddress(_python_module, "Py_BuildValue"); - PyObject_CallFunction = (PyObject_CallFunction_func *)GetProcAddress(_python_module, "PyObject_CallFunction"); - -#endif // _WIN32 - - if (Py_SetProgramName == NULL || - PySys_SetArgv == NULL || - Py_SetPythonHome == NULL || - Py_Initialize == NULL || - Py_Finalize == NULL || - PyEval_InitThreads == NULL || - PyEval_AcquireLock == NULL || - PyEval_ReleaseLock == NULL || - PyRun_SimpleString == NULL || - PyErr_Print == NULL || - Py_XDECREF == NULL || - PyImport_ImportModule == NULL || - PyObject_SetAttrString == NULL || - PyObject_GetAttrString == NULL || - Py_BuildValue == NULL || - PyObject_CallFunction == NULL) { - // Couldn't get all of the needed Python functions for some reason. - cerr << "Py_SetProgramName = " << Py_SetProgramName << "\n" - << "PySys_SetArgv = " << PySys_SetArgv << "\n" - << "Py_SetPythonHome = " << Py_SetPythonHome << "\n" - << "Py_Initialize = " << Py_Initialize << "\n" - << "Py_Finalize = " << Py_Finalize << "\n" - << "PyEval_InitThreads = " << PyEval_InitThreads << "\n" - << "PyEval_AcquireLock = " << PyEval_AcquireLock << "\n" - << "PyEval_ReleaseLock = " << PyEval_ReleaseLock << "\n" - << "PyRun_SimpleString = " << PyRun_SimpleString << "\n" - << "PyErr_Print = " << PyErr_Print << "\n" - << "Py_XDECREF = " << Py_XDECREF << "\n" - << "PyImport_ImportModule = " << PyImport_ImportModule << "\n" - << "PyObject_SetAttrString = " << PyObject_SetAttrString << "\n" - << "PyObject_GetAttrString = " << PyObject_GetAttrString << "\n" - << "Py_BuildValue = " << Py_BuildValue << "\n" - << "PyObject_CallFunction = " << PyObject_CallFunction << "\n" - << "\n"; - FreeLibrary(_python_module); - _python_module = NULL; - return false; - } - - // All right, initialize Python. - Py_SetProgramName((char *)_program_name.c_str()); - Py_Initialize(); - Py_SetPythonHome((char *)_root_dir.c_str()); - PySys_SetArgv(_py_argc, _py_argv); - - // Set sys.path appropriately. - PyObject *sys = PyImport_ImportModule("sys"); - if (sys == NULL) { - PyErr_Print(); - return false; - } - - PyObject *path = Py_BuildValue("[s]", _root_dir.c_str()); - PyObject_SetAttrString(sys, "path", path); - Py_XDECREF(path); - Py_XDECREF(sys); - - // Now load runappmf.pyd. - PyObject *runappmf = PyImport_ImportModule("runappmf"); - if (runappmf == NULL) { - PyErr_Print(); - return false; - } - Py_XDECREF(runappmf); - - // And get the pointers to the functions needed within the module. - PyObject *appmf = PyImport_ImportModule("direct.showbase.RunAppMF"); - if (appmf == NULL) { - PyErr_Print(); - return false; - } - _runPackedApp = PyObject_GetAttrString(appmf, "runPackedApp"); - if (_runPackedApp == NULL) { - PyErr_Print(); - return false; - } - _setupWindow = PyObject_GetAttrString(appmf, "setupWindow"); - if (_setupWindow == NULL) { - PyErr_Print(); - return false; - } - _run = PyObject_GetAttrString(appmf, "run"); - if (_run == NULL) { - PyErr_Print(); - return false; - } - - return true; -} - -//////////////////////////////////////////////////////////////////// -// Function: P3DPython::tr_thread_run -// Access: Private -// Description: The main function for the sub-thread. -//////////////////////////////////////////////////////////////////// -void P3DPython:: -tr_thread_run() { - bool valid = true; - if (!tr_init_python()) { - // Couldn't get Python going. - valid = false; - } - - if (valid) { - 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(); - valid = false; - } - Py_XDECREF(result); - } - - if (valid) { - PyObject *result = PyObject_CallFunction(_runPackedApp, "[s]", _inst->get_p3d_filename().c_str()); - if (result == NULL) { - PyErr_Print(); - valid = false; - } - Py_XDECREF(result); - } - - _is_valid = valid; - // Maybe signal the parent that we're ready? - - if (valid) { - // Call run(). This function won't return until the p3d app - // exits. - PyObject *result = PyObject_CallFunction(_run, ""); - if (result == NULL) { - PyErr_Print(); - } - Py_XDECREF(result); - } - - // The instance has finished. - P3D_request *request = new P3D_request; - request->_instance = _inst; - request->_request_type = P3D_RT_stop; - _inst->add_request(request); -} - -#ifdef _WIN32 -//////////////////////////////////////////////////////////////////// -// Function: P3DPython::win_thread_func -// Access: Private, Static -// Description: The Windows flavor of the thread callback function. -//////////////////////////////////////////////////////////////////// -DWORD P3DPython:: -win_thread_func(LPVOID data) { - ((P3DPython *)data)->tr_thread_run(); - return 0; -} -#endif diff --git a/direct/src/plugin/p3dPython.h b/direct/src/plugin/p3dPython.h deleted file mode 100644 index 834ab9cc5b..0000000000 --- a/direct/src/plugin/p3dPython.h +++ /dev/null @@ -1,125 +0,0 @@ -// Filename: p3dPython.h -// Created by: drose (04Jun09) -// -//////////////////////////////////////////////////////////////////// -// -// 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 P3DPYTHON_H -#define P3DPYTHON_H - -#include "p3d_plugin_common.h" - -//////////////////////////////////////////////////////////////////// -// Class : P3DPython -// Description : Corresponds to a single instance of the Python -// interpreter. Since Python is single-threaded and -// global-namespace, there can only be one Python -// instance in a given address space. -// -// Note that, due to Python's "NewInterpreter" -// mechanism, it *might* be possible to have multiple -// virtual interpreters within a single Python instance. -// This will require some work to integrate successfully -// with Panda, though, so it is not currently -// implemented. -//////////////////////////////////////////////////////////////////// -class P3DPython { -public: - P3DPython(const string &python_version); - ~P3DPython(); - - INLINE const string &get_python_version() const; - - void start_session(P3DSession *session, P3DInstance *inst); - void terminate_session(P3DSession *session); - - INLINE int get_num_sessions() const; - INLINE bool is_valid() const; - -private: - void spawn_thread(); - void join_thread(); - -private: - // Methods that run only within the sub-thread. - bool tr_init_python(); - void tr_thread_run(); - -#ifdef _WIN32 - static DWORD WINAPI win_thread_func(LPVOID data); -#endif - -private: - string _python_version; - bool _is_valid; - - string _root_dir; - string _program_name; - - int _py_argc; - char **_py_argv; - - typedef set Sessions; - Sessions _sessions; - - // Temporary. - P3DInstance *_inst; - -#ifdef _WIN32 - HMODULE _python_module; - HANDLE _thread; -#endif - - typedef struct _object PyObject; - - PyObject *_runPackedApp; - PyObject *_setupWindow; - PyObject *_run; - - // Pointers to dynamically-loaded Python functions. - typedef void Py_SetProgramName_func(char *name); - typedef void PySys_SetArgv_func(int argc, char **argv); - typedef void Py_SetPythonHome_func(char *name); - typedef void Py_Initialize_func(void); - typedef void Py_Finalize_func(void); - typedef void PyEval_InitThreads_func(void); - typedef void PyEval_AcquireLock_func(void); - typedef void PyEval_ReleaseLock_func(void); - typedef int PyRun_SimpleString_func(const char *command); - typedef void PyErr_Print_func(void); - typedef void Py_XDECREF_func(PyObject *o); - typedef PyObject *PyImport_ImportModule_func(const char *name); - typedef int PyObject_SetAttrString_func(PyObject *o, const char *attr_name, PyObject *v); - typedef PyObject *PyObject_GetAttrString_func(PyObject *o, const char *attr_name); - typedef PyObject *Py_BuildValue_func(const char *format, ...); - typedef PyObject *PyObject_CallFunction_func(PyObject *callable, char *format, ...); - - Py_SetProgramName_func *Py_SetProgramName; - PySys_SetArgv_func *PySys_SetArgv; - Py_SetPythonHome_func *Py_SetPythonHome; - Py_Initialize_func *Py_Initialize; - Py_Finalize_func *Py_Finalize; - PyEval_InitThreads_func *PyEval_InitThreads; - PyEval_AcquireLock_func *PyEval_AcquireLock; - PyEval_ReleaseLock_func *PyEval_ReleaseLock; - PyRun_SimpleString_func *PyRun_SimpleString; - PyErr_Print_func *PyErr_Print; - Py_XDECREF_func *Py_XDECREF; - PyImport_ImportModule_func *PyImport_ImportModule; - PyObject_SetAttrString_func *PyObject_SetAttrString; - PyObject_GetAttrString_func *PyObject_GetAttrString; - Py_BuildValue_func *Py_BuildValue; - PyObject_CallFunction_func *PyObject_CallFunction; -}; - -#include "p3dPython.I" - -#endif diff --git a/direct/src/plugin/p3dPythonRun.I b/direct/src/plugin/p3dPythonRun.I new file mode 100755 index 0000000000..c16c136d8b --- /dev/null +++ b/direct/src/plugin/p3dPythonRun.I @@ -0,0 +1,14 @@ +// Filename: p3dPythonRun.I +// Created by: drose (05Jun09) +// +//////////////////////////////////////////////////////////////////// +// +// 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." +// +//////////////////////////////////////////////////////////////////// + diff --git a/direct/src/plugin/p3dPythonRun.cxx b/direct/src/plugin/p3dPythonRun.cxx new file mode 100755 index 0000000000..4d4b751290 --- /dev/null +++ b/direct/src/plugin/p3dPythonRun.cxx @@ -0,0 +1,308 @@ +// Filename: p3dPythonRun.cxx +// Created by: drose (05Jun09) +// +//////////////////////////////////////////////////////////////////// +// +// 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 "p3dPythonRun.h" + +//////////////////////////////////////////////////////////////////// +// Function: P3DPythonRun::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +P3DPythonRun:: +P3DPythonRun(int argc, char *argv[]) { + _read_thread_alive = false; + INIT_LOCK(_commands_lock); + + // Open the pipe streams with the input and output handles from the + // parent. +#ifdef _WIN32 + HANDLE read = GetStdHandle(STD_INPUT_HANDLE); + HANDLE write = GetStdHandle(STD_OUTPUT_HANDLE); + if (!SetStdHandle(STD_INPUT_HANDLE, INVALID_HANDLE_VALUE)) { + cerr << "unable to reset input handle\n"; + } + 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 + if (!_pipe_read) { + cerr << "unable to open read pipe\n"; + } + if (!_pipe_write) { + cerr << "unable to open write pipe\n"; + } + + 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); +} + +//////////////////////////////////////////////////////////////////// +// Function: P3DPythonRun::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +P3DPythonRun:: +~P3DPythonRun() { + Py_Finalize(); + + join_read_thread(); + DESTROY_LOCK(_commands_lock); +} + +//////////////////////////////////////////////////////////////////// +// Function: P3DPythonRun::run_python +// Access: Public +// Description: Runs the embedded Python process. This method does +// not return until the plugin is ready to exit. +//////////////////////////////////////////////////////////////////// +bool P3DPythonRun:: +run_python() { + // Now load runappmf.pyd. + PyObject *runappmf = PyImport_ImportModule("runappmf"); + if (runappmf == NULL) { + PyErr_Print(); + return false; + } + Py_DECREF(runappmf); + + // And get the pointers to the functions needed within the module. + PyObject *appmf = PyImport_ImportModule("direct.showbase.RunAppMF"); + if (appmf == NULL) { + PyErr_Print(); + return false; + } + _runPackedApp = PyObject_GetAttrString(appmf, "runPackedApp"); + if (_runPackedApp == NULL) { + PyErr_Print(); + return false; + } + _setupWindow = PyObject_GetAttrString(appmf, "setupWindow"); + if (_setupWindow == NULL) { + PyErr_Print(); + return false; + } + Py_DECREF(appmf); + + // Construct a Python wrapper around our check_comm() method. + + static PyMethodDef p3dpython_methods[] = { + {"check_comm", P3DPythonRun::py_check_comm, 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 *check_comm = PyObject_GetAttrString(p3dpython, "check_comm"); + if (check_comm == NULL) { + PyErr_Print(); + return false; + } + + // Now add check_comm() as a Python task. + PyObject *add_check_comm = PyObject_GetAttrString(appmf, "add_check_comm"); + if (add_check_comm == NULL) { + PyErr_Print(); + return false; + } + + PyObject *task = PyObject_CallFunction + (add_check_comm, "Ol", check_comm, (long)this); + if (task == NULL) { + PyErr_Print(); + return false; + } + Py_DECREF(task); + Py_DECREF(add_check_comm); + Py_DECREF(check_comm); + + // Finally, get lost in taskMgr.run(). + + PyObject *taskMgr = PyObject_GetAttrString(appmf, "taskMgr"); + if (taskMgr == NULL) { + PyErr_Print(); + return false; + } + cerr << "calling run()\n"; + PyObject *done = PyObject_CallMethod(taskMgr, "run", ""); + if (done == NULL) { + PyErr_Print(); + return false; + } + Py_DECREF(done); + cerr << "done calling run()\n"; + + Py_DECREF(taskMgr); + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: P3DPythonRun::handle_command +// Access: Private +// Description: Handles a command received from the plugin host, via +// an XML syntax on the wire. +//////////////////////////////////////////////////////////////////// +void P3DPythonRun:: +handle_command(TiXmlDocument *doc) { + cerr << "got command: " << *doc << "\n"; +} + +//////////////////////////////////////////////////////////////////// +// Function: P3DPythonRun::check_comm +// Access: Private +// 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. +//////////////////////////////////////////////////////////////////// +void P3DPythonRun:: +check_comm() { + ACQUIRE_LOCK(_commands_lock); + while (!_commands.empty()) { + TiXmlDocument *doc = _commands.front(); + _commands.pop_front(); + assert(_commands.size() < 10); + RELEASE_LOCK(_commands_lock); + handle_command(doc); + delete doc; + ACQUIRE_LOCK(_commands_lock); + } + RELEASE_LOCK(_commands_lock); +} + +//////////////////////////////////////////////////////////////////// +// Function: P3DPythonRun::py_check_comm +// Access: Private, Static +// Description: This method is added as a Python task function on the +// task manager. It is the Python wrapper around the +// check_comm method. +//////////////////////////////////////////////////////////////////// +PyObject *P3DPythonRun:: +py_check_comm(PyObject *, PyObject *args) { + long this_int; + PyObject *task; + if (!PyArg_ParseTuple(args, "lO:check_comm", &this_int, &task)) { + return NULL; + } + + P3DPythonRun *self = (P3DPythonRun *)(void *)this_int; + self->check_comm(); + + return PyObject_GetAttrString(task, "cont"); +} + +//////////////////////////////////////////////////////////////////// +// Function: P3DPythonRun::spawn_read_thread +// Access: Private +// Description: Starts the read thread. This thread is responsible +// for reading the standard input socket for XML +// commands and storing them in the _commands queue. +//////////////////////////////////////////////////////////////////// +void P3DPythonRun:: +spawn_read_thread() { + assert(!_read_thread_alive); + + _read_thread_alive = true; +#ifdef _WIN32 + _read_thread = CreateThread(NULL, 0, &win_rt_thread_run, this, 0, NULL); +#endif +} + +//////////////////////////////////////////////////////////////////// +// Function: P3DPythonRun::join_read_thread +// Access: Private +// Description: Waits for the read thread to stop. +//////////////////////////////////////////////////////////////////// +void P3DPythonRun:: +join_read_thread() { + cerr << "waiting for thread\n"; + _read_thread_alive = false; + +#ifdef _WIN32 + assert(_read_thread != NULL); + WaitForSingleObject(_read_thread, INFINITE); + CloseHandle(_read_thread); + _read_thread = NULL; +#endif + cerr << "done waiting for thread\n"; +} + +//////////////////////////////////////////////////////////////////// +// Function: P3DPythonRun::rt_thread_run +// Access: Private +// Description: The main function for the read thread. +//////////////////////////////////////////////////////////////////// +void P3DPythonRun:: +rt_thread_run() { + while (_read_thread_alive) { + TiXmlDocument *doc = new TiXmlDocument; + _pipe_read >> *doc; + if (!_pipe_read || _pipe_read.eof()) { + // Some error on reading. Abort. + cerr << "Error on reading.\n"; + return; + } + + // Successfully read an XML document. Feed it to the parent. + ACQUIRE_LOCK(_commands_lock); + _commands.push_back(doc); + RELEASE_LOCK(_commands_lock); + } +} + +#ifdef _WIN32 +//////////////////////////////////////////////////////////////////// +// Function: P3DPython::win_rt_thread_run +// Access: Private, Static +// Description: The Windows flavor of the thread callback function. +//////////////////////////////////////////////////////////////////// +DWORD P3DPythonRun:: +win_rt_thread_run(LPVOID data) { + ((P3DPythonRun *)data)->rt_thread_run(); + return 0; +} +#endif + + +//////////////////////////////////////////////////////////////////// +// Function: main +// Description: Starts the program running. +//////////////////////////////////////////////////////////////////// +int +main(int argc, char *argv[]) { + P3DPythonRun run(argc, argv); + + if (!run.run_python()) { + cerr << "Couldn't initialize Python.\n"; + return 1; + } + return 0; +} diff --git a/direct/src/plugin/p3dPythonRun.h b/direct/src/plugin/p3dPythonRun.h new file mode 100755 index 0000000000..318497a9fb --- /dev/null +++ b/direct/src/plugin/p3dPythonRun.h @@ -0,0 +1,94 @@ +// Filename: p3dPythonRun.h +// Created by: drose (05Jun09) +// +//////////////////////////////////////////////////////////////////// +// +// 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 P3DPYTHONRUN_H +#define P3DPYTHONRUN_H + +#include +#include +#include +#include +#include +#include +#include "p3d_lock.h" +#include "handleStream.h" + +#ifdef _WIN32 +#include +#endif + +using namespace std; + +//////////////////////////////////////////////////////////////////// +// Class : P3DPythonRun +// Description : This class is used to run, and communicate with, +// embedded Python in a sub-process. It is compiled and +// launched as a separate executable from the p3d_plugin +// dll, because that's the only way Windows can launch a +// sub-process, and also because it makes it possible to +// compile-time link with the Python dll, instead of +// having to go through the clumsy dynamic-loading +// interface. +// +// Communication is via XML files exchanged via +// anonymous pipes from the parent process. This +// executable is not designed to stand alone. +//////////////////////////////////////////////////////////////////// +class P3DPythonRun { +public: + P3DPythonRun(int argc, char *argv[]); + ~P3DPythonRun(); + + bool run_python(); + +private: + void handle_command(TiXmlDocument *doc); + void check_comm(); + static PyObject *py_check_comm(PyObject *, PyObject *args); + + void spawn_read_thread(); + void join_read_thread(); + +private: + // This method runs only within the read thread. + void rt_thread_run(); +#ifdef _WIN32 + static DWORD WINAPI win_rt_thread_run(LPVOID data); +#endif + +private: + typedef deque Commands; + Commands _commands; + LOCK _commands_lock; + + HandleStream _pipe_read; + HandleStream _pipe_write; + + bool _read_thread_alive; +#ifdef _WIN32 + HANDLE _read_thread; +#endif + + string _program_name; + int _py_argc; + char **_py_argv; + + PyObject *_runPackedApp; + PyObject *_setupWindow; +}; + +#include "p3dPythonRun.I" + +#endif + diff --git a/direct/src/plugin/p3dSession.cxx b/direct/src/plugin/p3dSession.cxx index 4f8e082072..4d0b2607c1 100644 --- a/direct/src/plugin/p3dSession.cxx +++ b/direct/src/plugin/p3dSession.cxx @@ -15,6 +15,7 @@ #include "p3dSession.h" #include "p3dInstance.h" #include "p3dInstanceManager.h" +#include #include @@ -31,7 +32,92 @@ P3DSession:: P3DSession(P3DInstance *inst) { _session_key = inst->get_session_key(); _python_version = inst->get_python_version(); - _python = NULL; + _python_root_dir = "C:/p3drun"; + + string p3dpython = "c:/cygwin/home/drose/player/direct/built/bin/p3dpython.exe"; + + // Populate the new process' environment. + string env; + + // These are the enviroment variables we forward from the current + // environment, if they are set. + const char *keep[] = { + "TEMP", "HOME", "USER", + NULL + }; + for (int ki = 0; keep[ki] != NULL; ++ki) { + char *value = getenv(keep[ki]); + if (value != NULL) { + env += keep[ki]; + env += "="; + env += value; + env += '\0'; + } + } + + // Define some new environment variables. + env += "PATH="; + env += _python_root_dir; + env += '\0'; + + env += "PYTHONPATH="; + env += _python_root_dir; + env += '\0'; + + // 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)) { + cerr << "failed to create pipe\n"; + } else { + SetStdHandle(STD_INPUT_HANDLE, r_to); + } + + // Create the pipe from the process. + if (!CreatePipe(&r_from, &w_from, &inheritable, 0)) { + cerr << "failed to create pipe\n"; + } else { + SetStdHandle(STD_OUTPUT_HANDLE, w_from); + } + + cerr << "creating process " << p3dpython << "\n"; + 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"; + _started_p3dpython = (result != 0); + + if (!_started_p3dpython) { + cerr << "Failed to create process.\n"; + } else { + cerr << "Created process: " << _p3dpython.dwProcessId << "\n"; + } + + CloseHandle(r_to); + CloseHandle(w_from); + SetStdHandle(STD_INPUT_HANDLE, orig_stdin); + SetStdHandle(STD_OUTPUT_HANDLE, orig_stdout); + + _pipe_read.open_read(r_from); + _pipe_write.open_write(w_to); +#endif // _WIN32 + if (!_pipe_read) { + cerr << "unable to open read pipe\n"; + } + if (!_pipe_write) { + cerr << "unable to open write pipe\n"; + } } //////////////////////////////////////////////////////////////////// @@ -42,6 +128,14 @@ P3DSession(P3DInstance *inst) { //////////////////////////////////////////////////////////////////// P3DSession:: ~P3DSession() { + if (_started_p3dpython) { + cerr << "Terminating process.\n"; + // Messy. Shouldn't use TerminateProcess unless necessary. + TerminateProcess(_p3dpython.hProcess, 2); + + CloseHandle(_p3dpython.hProcess); + CloseHandle(_p3dpython.hThread); + } } //////////////////////////////////////////////////////////////////// @@ -56,7 +150,6 @@ P3DSession:: //////////////////////////////////////////////////////////////////// void P3DSession:: start_instance(P3DInstance *inst) { - assert(_python == NULL); assert(inst->_session == NULL); assert(inst->get_session_key() == _session_key); assert(inst->get_python_version() == _python_version); @@ -66,10 +159,21 @@ start_instance(P3DInstance *inst) { assert(inserted); P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr(); + + TiXmlDocument doc; + TiXmlDeclaration *decl = new TiXmlDeclaration("1.0", "", ""); + TiXmlElement *element = new TiXmlElement("instance"); + doc.LinkEndChild(decl); + doc.LinkEndChild(element); + + _pipe_write << doc; + + /* P3DPython *python = inst_mgr->start_python(_python_version); if (python != NULL) { python->start_session(this, inst); } + */ } //////////////////////////////////////////////////////////////////// @@ -81,10 +185,12 @@ start_instance(P3DInstance *inst) { //////////////////////////////////////////////////////////////////// void P3DSession:: terminate_instance(P3DInstance *inst) { + /* if (_python != NULL) { _python->terminate_session(this); assert(_python == NULL); } + */ if (inst->_session == this) { inst->_session = NULL; diff --git a/direct/src/plugin/p3dSession.h b/direct/src/plugin/p3dSession.h index 3dbbc6d532..fb0758ac8c 100644 --- a/direct/src/plugin/p3dSession.h +++ b/direct/src/plugin/p3dSession.h @@ -16,6 +16,7 @@ #define P3DSESSION_H #include "p3d_plugin_common.h" +#include "handleStream.h" #include @@ -45,12 +46,18 @@ private: string _session_key; string _python_version; - P3DPython *_python; + string _python_root_dir; typedef set Instances; Instances _instances; - friend class P3DPython; + // Members for communicating with the p3dpython child process. + bool _started_p3dpython; +#ifdef _WIN32 + PROCESS_INFORMATION _p3dpython; +#endif + HandleStream _pipe_read; + HandleStream _pipe_write; }; #include "p3dSession.I" diff --git a/direct/src/plugin/p3d_lock.h b/direct/src/plugin/p3d_lock.h new file mode 100755 index 0000000000..ddf58a6837 --- /dev/null +++ b/direct/src/plugin/p3d_lock.h @@ -0,0 +1,30 @@ +// Filename: p3d_lock.h +// Created by: drose (05Jun09) +// +//////////////////////////////////////////////////////////////////// +// +// 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 P3D_LOCK_H +#define P3D_LOCK_H + +// Provides some simple macros that implement platform-independet +// mutex locks. + +#ifdef _WIN32 +#define LOCK CRITICAL_SECTION +#define INIT_LOCK(lock) InitializeCriticalSection(&(lock)) +#define ACQUIRE_LOCK(lock) EnterCriticalSection(&(lock)) +#define RELEASE_LOCK(lock) LeaveCriticalSection(&(lock)) +#define DESTROY_LOCK(lock) DeleteCriticalSection(&(lock)) +#endif + +#endif + diff --git a/direct/src/plugin/p3d_plugin_common.h b/direct/src/plugin/p3d_plugin_common.h index 00c27c2e37..8e1dff6e2d 100644 --- a/direct/src/plugin/p3d_plugin_common.h +++ b/direct/src/plugin/p3d_plugin_common.h @@ -15,13 +15,15 @@ #ifndef P3D_PLUGIN_COMMON #define P3D_PLUGIN_COMMON -// This header file is included by all C++ files in this directory; it -// provides some common symbol declarations. +// This header file is included by all C++ files in this directory +// that contribute to p3d_plugin; provides some common symbol +// declarations. #define P3D_FUNCTION_PROTOTYPES #define BUILDING_P3D_PLUGIN #include "p3d_plugin.h" +#include "p3d_lock.h" #include #include @@ -31,13 +33,5 @@ using namespace std; #define INLINE inline -#ifdef _WIN32 -#define LOCK CRITICAL_SECTION -#define INIT_LOCK(lock) InitializeCriticalSection(&(lock)) -#define ACQUIRE_LOCK(lock) EnterCriticalSection(&(lock)) -#define RELEASE_LOCK(lock) LeaveCriticalSection(&(lock)) -#define DESTROY_LOCK(lock) DeleteCriticalSection(&(lock)) -#endif - #endif diff --git a/direct/src/plugin/p3d_plugin_composite1.cxx b/direct/src/plugin/p3d_plugin_composite1.cxx index 069fc76349..26f0c404f4 100644 --- a/direct/src/plugin/p3d_plugin_composite1.cxx +++ b/direct/src/plugin/p3d_plugin_composite1.cxx @@ -1,5 +1,4 @@ #include "p3d_plugin.cxx" #include "p3dInstance.cxx" #include "p3dInstanceManager.cxx" -#include "p3dPython.cxx" #include "p3dSession.cxx" diff --git a/direct/src/plugin/wingetopt.c b/direct/src/plugin/wingetopt.c index 30b24f0601..bf82b6435b 100644 --- a/direct/src/plugin/wingetopt.c +++ b/direct/src/plugin/wingetopt.c @@ -10,8 +10,9 @@ Code given out at the 1985 UNIFORUM conference in Dallas. #include "wingetopt.h" #include +#include -#define NULL 0 +//#define NULL 0 #define EOF (-1) #define ERR(s, c) if(opterr){\ char errbuf[2];\ diff --git a/direct/src/showbase/RunAppMF.py b/direct/src/showbase/RunAppMF.py index b791d3d379..fed5de0b73 100644 --- a/direct/src/showbase/RunAppMF.py +++ b/direct/src/showbase/RunAppMF.py @@ -15,6 +15,7 @@ import sys from direct.showbase import VFSImporter from pandac.PandaModules import VirtualFileSystem, Filename, Multifile, loadPrcFileData, getModelPath from direct.stdpy import file +from direct.task.TaskManagerGlobal import taskMgr import os import __builtin__ @@ -144,8 +145,13 @@ def setupWindow(windowType, x, y, width, height, parent): loadPrcFileData("setupWindow", data) -def run(): - taskMgr.run() +def add_check_comm(func, this): + """ This function is provided just a convenience for + p3dPythonRun.cxx. It adds the indicated function to the task + manager with the appropriate parameters for the check_comm + task. """ + return taskMgr.add(func, "check_comm", sort = -50, extraArgs = [this], + appendTask = True) if __name__ == '__main__': try: