// 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 "pandabase.h" #ifdef _WIN32 #define WIN32_LEAN_AND_MEAN #include #endif #include "run_p3dpython.h" #include "p3d_lock.h" #include "handleStream.h" #include "p3dCInstance.h" #include "pandaFileStreamBuf.h" #include "pmap.h" #include "pdeque.h" #include "pmutex.h" #include "get_tinyxml.h" #include "filename.h" #include // Python 2.5 adds Py_ssize_t; earlier versions don't have it. #if PY_VERSION_HEX < 0x02050000 && !defined(PY_SSIZE_T_MIN) typedef int Py_ssize_t; #define PY_SSIZE_T_MAX INT_MAX #define PY_SSIZE_T_MIN INT_MIN #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 Panda and Python, 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 isn't // terribly eficient, of course, but it's easy; and it's // a fairly low-bandwidth channel so efficiency is not // paramount. // // This executable is not designed to stand alone; it is // designed to be invoked only by p3d_plugin. //////////////////////////////////////////////////////////////////// class P3DPythonRun { public: P3DPythonRun(const char *program_name, const char *archive_file, FHandle input_handle, FHandle output_handle, const char *log_pathname, bool interactive_console); ~P3DPythonRun(); int run_python(); void set_window_open(P3DCInstance *inst, bool is_open); void request_keyboard_focus(P3DCInstance *inst); private: void run_interactive_console(); void handle_command(TiXmlDocument *doc); void handle_pyobj_command(TiXmlElement *xcommand, bool needs_response, int want_response_id); void handle_script_response_command(TiXmlElement *xcommand); void check_comm(); static PyObject *st_check_comm(PyObject *, PyObject *args); TiXmlDocument *wait_script_response(int response_id); PyObject *py_request_func(PyObject *args); static PyObject *st_request_func(PyObject *, PyObject *args); void spawn_read_thread(); void join_read_thread(); void start_instance(P3DCInstance *inst, TiXmlElement *xinstance); void terminate_instance(int id); void set_instance_info(P3DCInstance *inst, TiXmlElement *xinstance); void add_package_info(P3DCInstance *inst, TiXmlElement *xpackage); void set_p3d_filename(P3DCInstance *inst, TiXmlElement *xfparams); void setup_window(int id, TiXmlElement *xwparams); void setup_window(P3DCInstance *inst, TiXmlElement *xwparams); void send_windows_message(int id, unsigned int msg, int wparam, int lparam); void terminate_session(); private: TiXmlElement *pyobj_to_xml(PyObject *value); PyObject *xml_to_pyobj(TiXmlElement *xvalue); private: // This subclass of WindowHandle is associated with the parent // window we are given by the parent process. We use it to add // hooks for communicating with the parent window, for instance to // ask for the parent window to manage keyboard focus when // necessary. class P3DWindowHandle : public WindowHandle { public: P3DWindowHandle(P3DPythonRun *p3dpython, P3DCInstance *inst, const WindowHandle ©); public: virtual void attach_child(WindowHandle *child); virtual void detach_child(WindowHandle *child); virtual void request_keyboard_focus(WindowHandle *child); private: P3DPythonRun *_p3dpython; P3DCInstance *_inst; int _child_count; public: static TypeHandle get_class_type() { return _type_handle; } static void init_type() { WindowHandle::init_type(); register_type(_type_handle, "P3DWindowHandle", WindowHandle::get_class_type()); } virtual TypeHandle get_type() const { return get_class_type(); } virtual TypeHandle force_init_type() {init_type(); return get_class_type();} private: static TypeHandle _type_handle; }; private: // This method runs only within the read thread. THREAD_CALLBACK_DECLARATION(P3DPythonRun, rt_thread_run); void rt_thread_run(); private: typedef pmap Instances; Instances _instances; int _session_id; Filename _archive_file; int _py_argc; #if PY_MAJOR_VERSION >= 3 wchar_t *_py_argv[2]; wstring _program_name; #else char *_py_argv[2]; string _program_name; #endif bool _interactive_console; PyObject *_runner; PyObject *_undefined_object_class; PyObject *_undefined; PyObject *_concrete_struct_class; PyObject *_browser_object_class; PyObject *_taskMgr; // This map keeps track of the PyObject pointers we have delivered // to the parent process. We have to hold the reference count on // each of these until the parent process tells us it's safe to // release them. typedef pmap SentObjects; SentObjects _sent_objects; int _next_sent_id; typedef pdeque Commands; // This is a special queue of responses extracted from the _commands // queue, below. It's protected by the Panda mutex. Commands _responses; Mutex _responses_lock; // The remaining members are manipulated by the read thread. Commands _commands; // This has to be an actual OS LOCK instead of Panda's Mutex, // because we have to use a true thread here, not one of Panda's // simple threads. LOCK _commands_lock; HandleStream _pipe_read; HandleStream _pipe_write; pofstream _error_log; bool _read_thread_continue; bool _program_continue; bool _session_terminated; THREAD _read_thread; public: static P3DPythonRun *_global_ptr; }; #include "p3dPythonRun.I" #endif