p3dPythonRun.cxx 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842
  1. // Filename: p3dPythonRun.cxx
  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. #include "p3dPythonRun.h"
  15. #include "asyncTaskManager.h"
  16. // There is only one P3DPythonRun object in any given process space.
  17. // Makes the statics easier to deal with, and we don't need multiple
  18. // instances of this think.
  19. P3DPythonRun *P3DPythonRun::_global_ptr = NULL;
  20. ////////////////////////////////////////////////////////////////////
  21. // Function: P3DPythonRun::Constructor
  22. // Access: Public
  23. // Description:
  24. ////////////////////////////////////////////////////////////////////
  25. P3DPythonRun::
  26. P3DPythonRun(int argc, char *argv[]) {
  27. _read_thread_continue = false;
  28. _program_continue = true;
  29. INIT_LOCK(_commands_lock);
  30. _program_name = argv[0];
  31. _py_argc = 1;
  32. _py_argv = (char **)malloc(2 * sizeof(char *));
  33. _py_argv[0] = argv[0];
  34. _py_argv[1] = NULL;
  35. // Initialize Python. It appears to be important to do this before
  36. // we open the pipe streams and spawn the thread, below.
  37. Py_SetProgramName((char *)_program_name.c_str());
  38. Py_Initialize();
  39. PySys_SetArgv(_py_argc, _py_argv);
  40. // Open the pipe streams with the input and output handles from the
  41. // parent.
  42. #ifdef _WIN32
  43. HANDLE read = GetStdHandle(STD_INPUT_HANDLE);
  44. HANDLE write = GetStdHandle(STD_OUTPUT_HANDLE);
  45. if (!SetStdHandle(STD_INPUT_HANDLE, INVALID_HANDLE_VALUE)) {
  46. nout << "unable to reset input handle\n";
  47. }
  48. if (!SetStdHandle(STD_OUTPUT_HANDLE, INVALID_HANDLE_VALUE)) {
  49. nout << "unable to reset input handle\n";
  50. }
  51. _pipe_read.open_read(read);
  52. _pipe_write.open_write(write);
  53. #else
  54. _pipe_read.open_read(STDIN_FILENO);
  55. _pipe_write.open_write(STDOUT_FILENO);
  56. #endif // _WIN32
  57. if (!_pipe_read) {
  58. nout << "unable to open read pipe\n";
  59. }
  60. if (!_pipe_write) {
  61. nout << "unable to open write pipe\n";
  62. }
  63. spawn_read_thread();
  64. }
  65. ////////////////////////////////////////////////////////////////////
  66. // Function: P3DPythonRun::Destructor
  67. // Access: Public
  68. // Description:
  69. ////////////////////////////////////////////////////////////////////
  70. P3DPythonRun::
  71. ~P3DPythonRun() {
  72. Py_Finalize();
  73. join_read_thread();
  74. DESTROY_LOCK(_commands_lock);
  75. }
  76. ////////////////////////////////////////////////////////////////////
  77. // Function: P3DPythonRun::run_python
  78. // Access: Public
  79. // Description: Runs the embedded Python process. This method does
  80. // not return until the plugin is ready to exit.
  81. ////////////////////////////////////////////////////////////////////
  82. bool P3DPythonRun::
  83. run_python() {
  84. // First, load runp3d_frozen.pyd. Since this is a magic frozen pyd,
  85. // importing it automatically makes all of its frozen contents
  86. // available to import as well.
  87. PyObject *runp3d_frozen = PyImport_ImportModule("runp3d_frozen");
  88. if (runp3d_frozen == NULL) {
  89. PyErr_Print();
  90. return false;
  91. }
  92. Py_DECREF(runp3d_frozen);
  93. // So now we can import the module itself.
  94. PyObject *runp3d = PyImport_ImportModule("direct.showutil.runp3d");
  95. if (runp3d == NULL) {
  96. PyErr_Print();
  97. return false;
  98. }
  99. // Get the pointers to the objects needed within the module.
  100. PyObject *AppRunner = PyObject_GetAttrString(runp3d, "AppRunner");
  101. if (AppRunner == NULL) {
  102. PyErr_Print();
  103. return false;
  104. }
  105. // Construct an instance of AppRunner.
  106. _runner = PyObject_CallFunction(AppRunner, (char*) "");
  107. if (_runner == NULL) {
  108. PyErr_Print();
  109. return false;
  110. }
  111. Py_DECREF(AppRunner);
  112. // Get the global TaskManager.
  113. _taskMgr = PyObject_GetAttrString(runp3d, "taskMgr");
  114. if (_taskMgr == NULL) {
  115. PyErr_Print();
  116. return false;
  117. }
  118. Py_DECREF(runp3d);
  119. // Construct a Python wrapper around our request_func() method.
  120. static PyMethodDef p3dpython_methods[] = {
  121. {"request_func", P3DPythonRun::st_request_func, METH_VARARGS,
  122. "Check for communications to and from the plugin host."},
  123. {NULL, NULL, 0, NULL} /* Sentinel */
  124. };
  125. PyObject *p3dpython = Py_InitModule("p3dpython", p3dpython_methods);
  126. if (p3dpython == NULL) {
  127. PyErr_Print();
  128. return false;
  129. }
  130. PyObject *request_func = PyObject_GetAttrString(p3dpython, "request_func");
  131. if (request_func == NULL) {
  132. PyErr_Print();
  133. return false;
  134. }
  135. // Now pass that func pointer back to our AppRunner instance, so it
  136. // can call up to us.
  137. PyObject *result = PyObject_CallMethod(_runner, (char*) "setRequestFunc", (char*) "O", request_func);
  138. if (result == NULL) {
  139. PyErr_Print();
  140. return false;
  141. }
  142. Py_DECREF(result);
  143. Py_DECREF(request_func);
  144. // Now add check_comm() as a task.
  145. _check_comm_task = new GenericAsyncTask("check_comm", st_check_comm, this);
  146. AsyncTaskManager *task_mgr = AsyncTaskManager::get_global_ptr();
  147. task_mgr->add(_check_comm_task);
  148. // Finally, get lost in taskMgr.run().
  149. nout << "calling run()\n";
  150. PyObject *done = PyObject_CallMethod(_taskMgr, (char*) "run", (char*) "");
  151. if (done == NULL) {
  152. PyErr_Print();
  153. return false;
  154. }
  155. Py_DECREF(done);
  156. nout << "done calling run()\n";
  157. return true;
  158. }
  159. ////////////////////////////////////////////////////////////////////
  160. // Function: P3DPythonRun::handle_command
  161. // Access: Private
  162. // Description: Handles a command received from the plugin host, via
  163. // an XML syntax on the wire.
  164. ////////////////////////////////////////////////////////////////////
  165. void P3DPythonRun::
  166. handle_command(TiXmlDocument *doc) {
  167. nout << "got command: " << *doc << "\n";
  168. TiXmlElement *xcommand = doc->FirstChildElement("command");
  169. if (xcommand != NULL) {
  170. const char *cmd = xcommand->Attribute("cmd");
  171. if (cmd != NULL) {
  172. if (strcmp(cmd, "start_instance") == 0) {
  173. TiXmlElement *xinstance = xcommand->FirstChildElement("instance");
  174. if (xinstance != (TiXmlElement *)NULL) {
  175. P3DCInstance *inst = new P3DCInstance(xinstance);
  176. start_instance(inst, xinstance);
  177. }
  178. } else if (strcmp(cmd, "terminate_instance") == 0) {
  179. int instance_id;
  180. if (xcommand->QueryIntAttribute("instance_id", &instance_id) == TIXML_SUCCESS) {
  181. terminate_instance(instance_id);
  182. }
  183. } else if (strcmp(cmd, "setup_window") == 0) {
  184. int instance_id;
  185. TiXmlElement *xwparams = xcommand->FirstChildElement("wparams");
  186. if (xwparams != (TiXmlElement *)NULL &&
  187. xcommand->QueryIntAttribute("instance_id", &instance_id) == TIXML_SUCCESS) {
  188. setup_window(instance_id, xwparams);
  189. }
  190. } else if (strcmp(cmd, "exit") == 0) {
  191. terminate_session();
  192. } else if (strcmp(cmd, "feed_value") == 0) {
  193. int instance_id, unique_id;
  194. TiXmlElement *xvalue = xcommand->FirstChildElement("value");
  195. if (xvalue != (TiXmlElement *)NULL &&
  196. xcommand->QueryIntAttribute("instance_id", &instance_id) == TIXML_SUCCESS &&
  197. xcommand->QueryIntAttribute("unique_id", &unique_id) == TIXML_SUCCESS) {
  198. // TODO: deal with instance_id.
  199. PyObject *value = from_xml_value(xvalue);
  200. PyObject *result = PyObject_CallMethod
  201. (_runner, (char*)"feedValue", (char*)"iO", unique_id, value);
  202. Py_DECREF(value);
  203. Py_XDECREF(result);
  204. }
  205. } else {
  206. nout << "Unhandled command " << cmd << "\n";
  207. }
  208. }
  209. }
  210. }
  211. ////////////////////////////////////////////////////////////////////
  212. // Function: P3DPythonRun::check_comm
  213. // Access: Private
  214. // Description: This method is added to the Python task manager (via
  215. // py_check_comm, below) so that it gets a call every
  216. // frame. Its job is to check for commands received
  217. // from the plugin host in the parent process.
  218. ////////////////////////////////////////////////////////////////////
  219. AsyncTask::DoneStatus P3DPythonRun::
  220. check_comm(GenericAsyncTask *task) {
  221. ACQUIRE_LOCK(_commands_lock);
  222. while (!_commands.empty()) {
  223. TiXmlDocument *doc = _commands.front();
  224. _commands.pop_front();
  225. assert(_commands.size() < 10);
  226. RELEASE_LOCK(_commands_lock);
  227. handle_command(doc);
  228. delete doc;
  229. ACQUIRE_LOCK(_commands_lock);
  230. }
  231. if (!_program_continue) {
  232. // The low-level thread detected an error, for instance pipe
  233. // closed. We should exit gracefully.
  234. terminate_session();
  235. }
  236. RELEASE_LOCK(_commands_lock);
  237. return AsyncTask::DS_cont;
  238. }
  239. ////////////////////////////////////////////////////////////////////
  240. // Function: P3DPythonRun::st_check_comm
  241. // Access: Private, Static
  242. // Description: This static function wrapper around check_comm is
  243. // necessary to add the method function to the
  244. // GenericAsyncTask object.
  245. ////////////////////////////////////////////////////////////////////
  246. AsyncTask::DoneStatus P3DPythonRun::
  247. st_check_comm(GenericAsyncTask *task, void *user_data) {
  248. P3DPythonRun *self = (P3DPythonRun *)user_data;
  249. return self->check_comm(task);
  250. }
  251. ////////////////////////////////////////////////////////////////////
  252. // Function: P3DPythonRun::py_request_func
  253. // Access: Private
  254. // Description: This method is a special Python function that is
  255. // added as a callback to the AppRunner class, to allow
  256. // Python to upcall into this object.
  257. ////////////////////////////////////////////////////////////////////
  258. PyObject *P3DPythonRun::
  259. py_request_func(PyObject *args) {
  260. int instance_id;
  261. const char *request_type;
  262. PyObject *extra_args;
  263. if (!PyArg_ParseTuple(args, "isO", &instance_id, &request_type, &extra_args)) {
  264. return NULL;
  265. }
  266. TiXmlDocument doc;
  267. TiXmlDeclaration *decl = new TiXmlDeclaration("1.0", "utf-8", "");
  268. TiXmlElement *xrequest = new TiXmlElement("request");
  269. xrequest->SetAttribute("instance_id", instance_id);
  270. xrequest->SetAttribute("rtype", request_type);
  271. doc.LinkEndChild(decl);
  272. doc.LinkEndChild(xrequest);
  273. if (strcmp(request_type, "notify") == 0) {
  274. // A general notification to be sent directly to the instance.
  275. const char *message;
  276. if (!PyArg_ParseTuple(extra_args, "s", &message)) {
  277. return NULL;
  278. }
  279. xrequest->SetAttribute("message", message);
  280. nout << "sending " << doc << "\n" << flush;
  281. _pipe_write << doc << flush;
  282. } else if (strcmp(request_type, "get_property") == 0) {
  283. // A get-property request.
  284. const char *property_name;
  285. int unique_id;
  286. if (!PyArg_ParseTuple(extra_args, "si", &property_name, &unique_id)) {
  287. return NULL;
  288. }
  289. xrequest->SetAttribute("property_name", property_name);
  290. xrequest->SetAttribute("unique_id", unique_id);
  291. nout << "sending " << doc << "\n" << flush;
  292. _pipe_write << doc << flush;
  293. } else if (strcmp(request_type, "set_property") == 0) {
  294. // A set-property request.
  295. const char *property_name;
  296. PyObject *value;
  297. if (!PyArg_ParseTuple(extra_args, "sO", &property_name, &value)) {
  298. return NULL;
  299. }
  300. xrequest->SetAttribute("property_name", property_name);
  301. xrequest->LinkEndChild(make_xml_value(value));
  302. nout << "sending " << doc << "\n" << flush;
  303. _pipe_write << doc << flush;
  304. } else if (strcmp(request_type, "call") == 0) {
  305. // A call-method request.
  306. const char *property_name;
  307. PyObject *params;
  308. int unique_id;
  309. if (!PyArg_ParseTuple(extra_args, "sOi", &property_name, &params, &unique_id)) {
  310. return NULL;
  311. }
  312. xrequest->SetAttribute("property_name", property_name);
  313. xrequest->SetAttribute("unique_id", unique_id);
  314. xrequest->LinkEndChild(make_xml_value(params));
  315. nout << "sending " << doc << "\n" << flush;
  316. _pipe_write << doc << flush;
  317. } else {
  318. string message = string("Unsupported request type: ") + string(request_type);
  319. PyErr_SetString(PyExc_ValueError, message.c_str());
  320. return NULL;
  321. }
  322. return Py_BuildValue("");
  323. }
  324. ////////////////////////////////////////////////////////////////////
  325. // Function: P3DPythonRun::st_request_func
  326. // Access: Private, Static
  327. // Description: This is the static wrapper around py_request_func.
  328. ////////////////////////////////////////////////////////////////////
  329. PyObject *P3DPythonRun::
  330. st_request_func(PyObject *, PyObject *args) {
  331. return P3DPythonRun::_global_ptr->py_request_func(args);
  332. }
  333. ////////////////////////////////////////////////////////////////////
  334. // Function: P3DPythonRun::spawn_read_thread
  335. // Access: Private
  336. // Description: Starts the read thread. This thread is responsible
  337. // for reading the standard input socket for XML
  338. // commands and storing them in the _commands queue.
  339. ////////////////////////////////////////////////////////////////////
  340. void P3DPythonRun::
  341. spawn_read_thread() {
  342. assert(!_read_thread_continue);
  343. // We have to use direct OS calls to create the thread instead of
  344. // Panda constructs, because it has to be an actual thread, not
  345. // necessarily a Panda thread (we can't use Panda's simple threads
  346. // implementation, because we can't get overlapped I/O on an
  347. // anonymous pipe in Windows).
  348. _read_thread_continue = true;
  349. #ifdef _WIN32
  350. _read_thread = CreateThread(NULL, 0, &win_rt_thread_run, this, 0, NULL);
  351. #else
  352. pthread_attr_t attr;
  353. pthread_attr_init(&attr);
  354. pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM);
  355. pthread_create(&_read_thread, &attr, &posix_rt_thread_run, (void *)this);
  356. pthread_attr_destroy(&attr);
  357. #endif
  358. }
  359. ////////////////////////////////////////////////////////////////////
  360. // Function: P3DPythonRun::join_read_thread
  361. // Access: Private
  362. // Description: Waits for the read thread to stop.
  363. ////////////////////////////////////////////////////////////////////
  364. void P3DPythonRun::
  365. join_read_thread() {
  366. nout << "waiting for thread\n";
  367. _read_thread_continue = false;
  368. _pipe_read.close();
  369. #ifdef _WIN32
  370. assert(_read_thread != NULL);
  371. WaitForSingleObject(_read_thread, INFINITE);
  372. CloseHandle(_read_thread);
  373. _read_thread = NULL;
  374. #else
  375. void *return_val;
  376. pthread_join(_read_thread, &return_val);
  377. #endif
  378. nout << "done waiting for thread\n";
  379. }
  380. ////////////////////////////////////////////////////////////////////
  381. // Function: P3DPythonRun::start_instance
  382. // Access: Private
  383. // Description: Starts the indicated instance running within the
  384. // Python process.
  385. ////////////////////////////////////////////////////////////////////
  386. void P3DPythonRun::
  387. start_instance(P3DCInstance *inst, TiXmlElement *xinstance) {
  388. nout << "starting instance " << inst << "\n";
  389. _instances[inst->get_instance_id()] = inst;
  390. TiXmlElement *xfparams = xinstance->FirstChildElement("fparams");
  391. if (xfparams != (TiXmlElement *)NULL) {
  392. set_p3d_filename(inst, xfparams);
  393. }
  394. TiXmlElement *xwparams = xinstance->FirstChildElement("wparams");
  395. if (xwparams != (TiXmlElement *)NULL) {
  396. setup_window(inst, xwparams);
  397. }
  398. }
  399. ////////////////////////////////////////////////////////////////////
  400. // Function: P3DPythonRun::terminate_instance
  401. // Access: Private
  402. // Description: Stops the instance with the indicated id.
  403. ////////////////////////////////////////////////////////////////////
  404. void P3DPythonRun::
  405. terminate_instance(int id) {
  406. Instances::iterator ii = _instances.find(id);
  407. if (ii == _instances.end()) {
  408. nout << "Can't stop instance " << id << ": not started.\n";
  409. return;
  410. }
  411. P3DCInstance *inst = (*ii).second;
  412. _instances.erase(ii);
  413. delete inst;
  414. // TODO: we don't currently have any way to stop just one instance
  415. // of a multi-instance session. This will require a different
  416. // Python interface than ShowBase.
  417. terminate_session();
  418. }
  419. ////////////////////////////////////////////////////////////////////
  420. // Function: P3DPythonRun::set_p3d_filename
  421. // Access: Private
  422. // Description: Sets the startup filename and tokens for the
  423. // indicated instance.
  424. ////////////////////////////////////////////////////////////////////
  425. void P3DPythonRun::
  426. set_p3d_filename(P3DCInstance *inst, TiXmlElement *xfparams) {
  427. string p3d_filename;
  428. const char *p3d_filename_c = xfparams->Attribute("p3d_filename");
  429. if (p3d_filename_c != NULL) {
  430. p3d_filename = p3d_filename_c;
  431. }
  432. PyObject *token_list = PyList_New(0);
  433. TiXmlElement *xtoken = xfparams->FirstChildElement("token");
  434. while (xtoken != NULL) {
  435. string keyword, value;
  436. const char *keyword_c = xtoken->Attribute("keyword");
  437. if (keyword_c != NULL) {
  438. keyword = keyword_c;
  439. }
  440. const char *value_c = xtoken->Attribute("value");
  441. if (value_c != NULL) {
  442. value = value_c;
  443. }
  444. PyObject *tuple = Py_BuildValue("(ss)", keyword.c_str(),
  445. value.c_str());
  446. PyList_Append(token_list, tuple);
  447. Py_DECREF(tuple);
  448. xtoken = xtoken->NextSiblingElement("token");
  449. }
  450. PyObject *result = PyObject_CallMethod
  451. (_runner, (char*) "setP3DFilename", (char*) "sOi", p3d_filename.c_str(),
  452. token_list, inst->get_instance_id());
  453. Py_DECREF(token_list);
  454. if (result == NULL) {
  455. PyErr_Print();
  456. }
  457. Py_XDECREF(result);
  458. }
  459. ////////////////////////////////////////////////////////////////////
  460. // Function: P3DPythonRun::setup_window
  461. // Access: Private
  462. // Description: Sets the window parameters for the indicated instance.
  463. ////////////////////////////////////////////////////////////////////
  464. void P3DPythonRun::
  465. setup_window(int id, TiXmlElement *xwparams) {
  466. Instances::iterator ii = _instances.find(id);
  467. if (ii == _instances.end()) {
  468. nout << "Can't setup window for " << id << ": not started.\n";
  469. return;
  470. }
  471. P3DCInstance *inst = (*ii).second;
  472. setup_window(inst, xwparams);
  473. }
  474. ////////////////////////////////////////////////////////////////////
  475. // Function: P3DPythonRun::setup_window
  476. // Access: Private
  477. // Description: Sets the window parameters for the indicated instance.
  478. ////////////////////////////////////////////////////////////////////
  479. void P3DPythonRun::
  480. setup_window(P3DCInstance *inst, TiXmlElement *xwparams) {
  481. string window_type;
  482. const char *window_type_c = xwparams->Attribute("window_type");
  483. if (window_type_c != NULL) {
  484. window_type = window_type_c;
  485. }
  486. int win_x, win_y, win_width, win_height;
  487. xwparams->Attribute("win_x", &win_x);
  488. xwparams->Attribute("win_y", &win_y);
  489. xwparams->Attribute("win_width", &win_width);
  490. xwparams->Attribute("win_height", &win_height);
  491. long parent_window_handle = 0;
  492. #ifdef _WIN32
  493. int hwnd;
  494. if (xwparams->Attribute("parent_hwnd", &hwnd)) {
  495. parent_window_handle = (long)hwnd;
  496. }
  497. #endif
  498. // TODO: direct this into the particular instance. This will
  499. // require a specialized ShowBase replacement.
  500. PyObject *result = PyObject_CallMethod
  501. (_runner, (char*)"setupWindow", (char*)"siiiii", window_type.c_str(),
  502. win_x, win_y, win_width, win_height,
  503. parent_window_handle);
  504. if (result == NULL) {
  505. PyErr_Print();
  506. }
  507. Py_XDECREF(result);
  508. }
  509. ////////////////////////////////////////////////////////////////////
  510. // Function: P3DPythonRun::terminate_session
  511. // Access: Private
  512. // Description: Stops all currently-running instances.
  513. ////////////////////////////////////////////////////////////////////
  514. void P3DPythonRun::
  515. terminate_session() {
  516. Instances::iterator ii;
  517. for (ii = _instances.begin(); ii != _instances.end(); ++ii) {
  518. P3DCInstance *inst = (*ii).second;
  519. delete inst;
  520. }
  521. _instances.clear();
  522. nout << "calling stop()\n";
  523. PyObject *result = PyObject_CallMethod(_taskMgr, (char*) "stop", (char*) "");
  524. if (result == NULL) {
  525. PyErr_Print();
  526. return;
  527. }
  528. Py_DECREF(result);
  529. nout << "done calling stop()\n";
  530. }
  531. ////////////////////////////////////////////////////////////////////
  532. // Function: P3DPythonRun::make_xml_value
  533. // Access: Private
  534. // Description: Converts the indicated PyObject to the appropriate
  535. // XML representation of a P3D_value type, and returns a
  536. // freshly-allocated TiXmlElement.
  537. ////////////////////////////////////////////////////////////////////
  538. TiXmlElement *P3DPythonRun::
  539. make_xml_value(PyObject *value) {
  540. TiXmlElement *xvalue = new TiXmlElement("value");
  541. if (value == Py_None) {
  542. // None.
  543. xvalue->SetAttribute("type", "none");
  544. } else if (PyBool_Check(value)) {
  545. // A bool value.
  546. xvalue->SetAttribute("type", "bool");
  547. xvalue->SetAttribute("value", PyObject_IsTrue(value));
  548. } else if (PyInt_Check(value)) {
  549. // A plain integer value.
  550. xvalue->SetAttribute("type", "int");
  551. xvalue->SetAttribute("value", PyInt_AsLong(value));
  552. } else if (PyLong_Check(value)) {
  553. // A long integer value. This gets converted either as an integer
  554. // or as a floating-point type, whichever fits.
  555. long lvalue = PyLong_AsLong(value);
  556. if (PyErr_Occurred()) {
  557. // It won't fit as an integer; make it a double.
  558. PyErr_Clear();
  559. xvalue->SetAttribute("type", "float");
  560. xvalue->SetDoubleAttribute("value", PyLong_AsDouble(value));
  561. } else {
  562. // It fits as an integer.
  563. xvalue->SetAttribute("type", "int");
  564. xvalue->SetAttribute("value", lvalue);
  565. }
  566. } else if (PyFloat_Check(value)) {
  567. // A floating-point value.
  568. xvalue->SetAttribute("type", "float");
  569. xvalue->SetDoubleAttribute("value", PyFloat_AsDouble(value));
  570. } else if (PyUnicode_Check(value)) {
  571. // A unicode value. Convert to utf-8 for the XML encoding.
  572. xvalue->SetAttribute("type", "string");
  573. PyObject *as_str = PyUnicode_AsUTF8String(value);
  574. if (as_str != NULL) {
  575. char *buffer;
  576. Py_ssize_t length;
  577. if (PyString_AsStringAndSize(as_str, &buffer, &length) != -1) {
  578. string str(buffer, length);
  579. xvalue->SetAttribute("value", str);
  580. }
  581. Py_DECREF(as_str);
  582. }
  583. } else if (PyString_Check(value)) {
  584. // A string value.
  585. xvalue->SetAttribute("type", "string");
  586. char *buffer;
  587. Py_ssize_t length;
  588. if (PyString_AsStringAndSize(value, &buffer, &length) != -1) {
  589. string str(buffer, length);
  590. xvalue->SetAttribute("value", str);
  591. }
  592. } else if (PySequence_Check(value)) {
  593. // A sequence or list value.
  594. xvalue->SetAttribute("type", "list");
  595. Py_ssize_t length = PySequence_Length(value);
  596. for (Py_ssize_t i = 0; i < length; ++i) {
  597. PyObject *obj = PySequence_GetItem(value, i);
  598. xvalue->LinkEndChild(make_xml_value(obj));
  599. }
  600. } else {
  601. // Some other kind of object. Don't know what else to do with it;
  602. // we'll make it a string.
  603. xvalue->SetAttribute("type", "string");
  604. PyObject *as_str = PyObject_Str(value);
  605. if (as_str != NULL) {
  606. char *buffer;
  607. Py_ssize_t length;
  608. if (PyString_AsStringAndSize(as_str, &buffer, &length) != -1) {
  609. string str(buffer, length);
  610. xvalue->SetAttribute("value", str);
  611. }
  612. Py_DECREF(as_str);
  613. }
  614. }
  615. return xvalue;
  616. }
  617. ////////////////////////////////////////////////////////////////////
  618. // Function: P3DPythonRun::from_xml_value
  619. // Access: Private
  620. // Description: Converts the XML representation of a P3D_value type
  621. // into the equivalent Python object and returns it.
  622. ////////////////////////////////////////////////////////////////////
  623. PyObject *P3DPythonRun::
  624. from_xml_value(TiXmlElement *xvalue) {
  625. const char *type = xvalue->Attribute("type");
  626. if (strcmp(type, "none") == 0) {
  627. return Py_BuildValue("");
  628. } else if (strcmp(type, "bool") == 0) {
  629. int value;
  630. if (xvalue->QueryIntAttribute("value", &value) == TIXML_SUCCESS) {
  631. return PyBool_FromLong(value);
  632. }
  633. } else if (strcmp(type, "int") == 0) {
  634. int value;
  635. if (xvalue->QueryIntAttribute("value", &value) == TIXML_SUCCESS) {
  636. return PyInt_FromLong(value);
  637. }
  638. } else if (strcmp(type, "float") == 0) {
  639. double value;
  640. if (xvalue->QueryDoubleAttribute("value", &value) == TIXML_SUCCESS) {
  641. return PyFloat_FromDouble(value);
  642. }
  643. } else if (strcmp(type, "string") == 0) {
  644. // Using the string form here instead of the char * form, so we
  645. // don't get tripped up on embedded null characters.
  646. const string *value = xvalue->Attribute(string("value"));
  647. if (value != NULL) {
  648. return PyString_FromStringAndSize(value->data(), value->length());
  649. }
  650. } else if (strcmp(type, "list") == 0) {
  651. PyObject *list = PyList_New(0);
  652. TiXmlElement *xchild = xvalue->FirstChildElement("value");
  653. while (xchild != NULL) {
  654. PyObject *child = from_xml_value(xchild);
  655. PyList_Append(list, child);
  656. Py_DECREF(child);
  657. xchild = xchild->NextSiblingElement("value");
  658. }
  659. return list;
  660. }
  661. // Something went wrong in decoding.
  662. return Py_BuildValue("");
  663. }
  664. ////////////////////////////////////////////////////////////////////
  665. // Function: P3DPythonRun::rt_thread_run
  666. // Access: Private
  667. // Description: The main function for the read thread.
  668. ////////////////////////////////////////////////////////////////////
  669. void P3DPythonRun::
  670. rt_thread_run() {
  671. nout << "thread reading.\n";
  672. while (_read_thread_continue) {
  673. TiXmlDocument *doc = new TiXmlDocument;
  674. _pipe_read >> *doc;
  675. if (!_pipe_read || _pipe_read.eof()) {
  676. // Some error on reading. Abort.
  677. nout << "Error on reading.\n";
  678. _program_continue = false;
  679. return;
  680. }
  681. // Successfully read an XML document.
  682. // Check for one special case: the "exit" command means we shut
  683. // down the read thread along with everything else.
  684. TiXmlElement *xcommand = doc->FirstChildElement("command");
  685. if (xcommand != NULL) {
  686. const char *cmd = xcommand->Attribute("cmd");
  687. if (cmd != NULL) {
  688. if (strcmp(cmd, "exit") == 0) {
  689. _read_thread_continue = false;
  690. }
  691. }
  692. }
  693. // Feed the command up to the parent.
  694. ACQUIRE_LOCK(_commands_lock);
  695. _commands.push_back(doc);
  696. RELEASE_LOCK(_commands_lock);
  697. }
  698. }
  699. #ifdef _WIN32
  700. ////////////////////////////////////////////////////////////////////
  701. // Function: P3DPythonRun::win_rt_thread_run
  702. // Access: Private, Static
  703. // Description: The Windows flavor of the thread callback function.
  704. ////////////////////////////////////////////////////////////////////
  705. DWORD P3DPythonRun::
  706. win_rt_thread_run(LPVOID data) {
  707. ((P3DPythonRun *)data)->rt_thread_run();
  708. return 0;
  709. }
  710. #endif
  711. #ifndef _WIN32
  712. ////////////////////////////////////////////////////////////////////
  713. // Function: P3DPythonRun::posix_rt_thread_run
  714. // Access: Private, Static
  715. // Description: The Posix flavor of the thread callback function.
  716. ////////////////////////////////////////////////////////////////////
  717. void *P3DPythonRun::
  718. posix_rt_thread_run(void *data) {
  719. ((P3DPythonRun *)data)->rt_thread_run();
  720. return NULL;
  721. }
  722. #endif
  723. ////////////////////////////////////////////////////////////////////
  724. // Function: main
  725. // Description: Starts the program running.
  726. ////////////////////////////////////////////////////////////////////
  727. int
  728. main(int argc, char *argv[]) {
  729. P3DPythonRun::_global_ptr = new P3DPythonRun(argc, argv);
  730. if (!P3DPythonRun::_global_ptr->run_python()) {
  731. nout << "Couldn't initialize Python.\n";
  732. return 1;
  733. }
  734. return 0;
  735. }