p3dPythonRun.h 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  1. // Filename: p3dPythonRun.h
  2. // Created by: drose (05Jun09)
  3. //
  4. ////////////////////////////////////////////////////////////////////
  5. //
  6. // PANDA 3D SOFTWARE
  7. // Copyright (c) Carnegie Mellon University. All rights reserved.
  8. //
  9. // All use of this software is subject to the terms of the revised BSD
  10. // license. You should have received a copy of this license along
  11. // with this source code in a file named "LICENSE."
  12. //
  13. ////////////////////////////////////////////////////////////////////
  14. #ifndef P3DPYTHONRUN_H
  15. #define P3DPYTHONRUN_H
  16. #include "pandabase.h"
  17. #ifdef _WIN32
  18. #define WIN32_LEAN_AND_MEAN
  19. #include <windows.h>
  20. #endif
  21. #include "run_p3dpython.h"
  22. #include "p3d_lock.h"
  23. #include "handleStream.h"
  24. #include "p3dCInstance.h"
  25. #include "pandaFileStreamBuf.h"
  26. #include "pmap.h"
  27. #include "pdeque.h"
  28. #include "pmutex.h"
  29. #include "get_tinyxml.h"
  30. #include "filename.h"
  31. #include <Python.h>
  32. // Python 2.5 adds Py_ssize_t; earlier versions don't have it.
  33. #if PY_VERSION_HEX < 0x02050000 && !defined(PY_SSIZE_T_MIN)
  34. typedef int Py_ssize_t;
  35. #define PY_SSIZE_T_MAX INT_MAX
  36. #define PY_SSIZE_T_MIN INT_MIN
  37. #endif
  38. using namespace std;
  39. ////////////////////////////////////////////////////////////////////
  40. // Class : P3DPythonRun
  41. // Description : This class is used to run, and communicate with,
  42. // embedded Python in a sub-process. It is compiled and
  43. // launched as a separate executable from the p3d_plugin
  44. // dll, because that's the only way Windows can launch a
  45. // sub-process, and also because it makes it possible to
  46. // compile-time link with Panda and Python, instead of
  47. // having to go through the clumsy dynamic-loading
  48. // interface.
  49. //
  50. // Communication is via XML files exchanged via
  51. // anonymous pipes from the parent process. This isn't
  52. // terribly eficient, of course, but it's easy; and it's
  53. // a fairly low-bandwidth channel so efficiency is not
  54. // paramount.
  55. //
  56. // This executable is not designed to stand alone; it is
  57. // designed to be invoked only by p3d_plugin.
  58. ////////////////////////////////////////////////////////////////////
  59. class P3DPythonRun {
  60. public:
  61. P3DPythonRun(const char *program_name, const char *archive_file,
  62. FHandle input_handle, FHandle output_handle,
  63. const char *log_pathname, bool interactive_console);
  64. ~P3DPythonRun();
  65. int run_python();
  66. void set_window_open(P3DCInstance *inst, bool is_open);
  67. void request_keyboard_focus(P3DCInstance *inst);
  68. private:
  69. void run_interactive_console();
  70. void handle_command(TiXmlDocument *doc);
  71. void handle_pyobj_command(TiXmlElement *xcommand, bool needs_response,
  72. int want_response_id);
  73. void handle_script_response_command(TiXmlElement *xcommand);
  74. void check_comm();
  75. static PyObject *st_check_comm(PyObject *, PyObject *args);
  76. TiXmlDocument *wait_script_response(int response_id);
  77. PyObject *py_request_func(PyObject *args);
  78. static PyObject *st_request_func(PyObject *, PyObject *args);
  79. void spawn_read_thread();
  80. void join_read_thread();
  81. void start_instance(P3DCInstance *inst, TiXmlElement *xinstance);
  82. void terminate_instance(int id);
  83. void set_instance_info(P3DCInstance *inst, TiXmlElement *xinstance);
  84. void add_package_info(P3DCInstance *inst, TiXmlElement *xpackage);
  85. void set_p3d_filename(P3DCInstance *inst, TiXmlElement *xfparams);
  86. void setup_window(int id, TiXmlElement *xwparams);
  87. void setup_window(P3DCInstance *inst, TiXmlElement *xwparams);
  88. void send_windows_message(int id, unsigned int msg, int wparam, int lparam);
  89. void terminate_session();
  90. private:
  91. TiXmlElement *pyobj_to_xml(PyObject *value);
  92. PyObject *xml_to_pyobj(TiXmlElement *xvalue);
  93. private:
  94. // This subclass of WindowHandle is associated with the parent
  95. // window we are given by the parent process. We use it to add
  96. // hooks for communicating with the parent window, for instance to
  97. // ask for the parent window to manage keyboard focus when
  98. // necessary.
  99. class P3DWindowHandle : public WindowHandle {
  100. public:
  101. P3DWindowHandle(P3DPythonRun *p3dpython, P3DCInstance *inst,
  102. const WindowHandle &copy);
  103. public:
  104. virtual void attach_child(WindowHandle *child);
  105. virtual void detach_child(WindowHandle *child);
  106. virtual void request_keyboard_focus(WindowHandle *child);
  107. private:
  108. P3DPythonRun *_p3dpython;
  109. P3DCInstance *_inst;
  110. int _child_count;
  111. public:
  112. static TypeHandle get_class_type() {
  113. return _type_handle;
  114. }
  115. static void init_type() {
  116. WindowHandle::init_type();
  117. register_type(_type_handle, "P3DWindowHandle",
  118. WindowHandle::get_class_type());
  119. }
  120. virtual TypeHandle get_type() const {
  121. return get_class_type();
  122. }
  123. virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
  124. private:
  125. static TypeHandle _type_handle;
  126. };
  127. private:
  128. // This method runs only within the read thread.
  129. THREAD_CALLBACK_DECLARATION(P3DPythonRun, rt_thread_run);
  130. void rt_thread_run();
  131. private:
  132. typedef pmap<int, P3DCInstance *> Instances;
  133. Instances _instances;
  134. int _session_id;
  135. string _program_name;
  136. Filename _archive_file;
  137. int _py_argc;
  138. char **_py_argv;
  139. bool _interactive_console;
  140. PyObject *_runner;
  141. PyObject *_undefined_object_class;
  142. PyObject *_undefined;
  143. PyObject *_concrete_struct_class;
  144. PyObject *_browser_object_class;
  145. PyObject *_taskMgr;
  146. // This map keeps track of the PyObject pointers we have delivered
  147. // to the parent process. We have to hold the reference count on
  148. // each of these until the parent process tells us it's safe to
  149. // release them.
  150. typedef pmap<int, PyObject *> SentObjects;
  151. SentObjects _sent_objects;
  152. int _next_sent_id;
  153. typedef pdeque<TiXmlDocument *> Commands;
  154. // This is a special queue of responses extracted from the _commands
  155. // queue, below. It's protected by the Panda mutex.
  156. Commands _responses;
  157. Mutex _responses_lock;
  158. // The remaining members are manipulated by the read thread.
  159. Commands _commands;
  160. // This has to be an actual OS LOCK instead of Panda's Mutex,
  161. // because we have to use a true thread here, not one of Panda's
  162. // simple threads.
  163. LOCK _commands_lock;
  164. HandleStream _pipe_read;
  165. HandleStream _pipe_write;
  166. pofstream _error_log;
  167. bool _read_thread_continue;
  168. bool _program_continue;
  169. bool _session_terminated;
  170. THREAD _read_thread;
  171. public:
  172. static P3DPythonRun *_global_ptr;
  173. };
  174. #include "p3dPythonRun.I"
  175. #endif