From 6f711392aadd9c8c2cd4ac2fe032da8076d918fc Mon Sep 17 00:00:00 2001 From: David Rose Date: Fri, 5 Jun 2009 00:47:03 +0000 Subject: [PATCH] wip: web plugin --- direct/src/plugin/Sources.pp | 40 ++ direct/src/plugin/p3dInstance.I | 52 +++ direct/src/plugin/p3dInstance.cxx | 214 +++++++++ direct/src/plugin/p3dInstance.h | 92 ++++ direct/src/plugin/p3dInstanceManager.I | 36 ++ direct/src/plugin/p3dInstanceManager.cxx | 255 +++++++++++ direct/src/plugin/p3dInstanceManager.h | 90 ++++ direct/src/plugin/p3dPython.I | 49 +++ direct/src/plugin/p3dPython.cxx | 372 ++++++++++++++++ direct/src/plugin/p3dPython.h | 125 ++++++ direct/src/plugin/p3dSession.I | 48 ++ direct/src/plugin/p3dSession.cxx | 93 ++++ direct/src/plugin/p3dSession.h | 58 +++ direct/src/plugin/p3d_plugin.cxx | 166 +++++++ direct/src/plugin/p3d_plugin.h | 458 ++++++++++++++++++++ direct/src/plugin/p3d_plugin_common.h | 43 ++ direct/src/plugin/p3d_plugin_composite1.cxx | 5 + direct/src/plugin/panda3d.cxx | 366 ++++++++++++++++ direct/src/plugin/wingetopt.c | 75 ++++ direct/src/plugin/wingetopt.h | 32 ++ direct/src/showbase/RunAppMF.py | 98 ++++- direct/src/showutil/FreezeTool.py | 28 +- direct/src/showutil/pfreeze.py | 17 +- 23 files changed, 2780 insertions(+), 32 deletions(-) create mode 100644 direct/src/plugin/Sources.pp create mode 100644 direct/src/plugin/p3dInstance.I create mode 100644 direct/src/plugin/p3dInstance.cxx create mode 100644 direct/src/plugin/p3dInstance.h create mode 100644 direct/src/plugin/p3dInstanceManager.I create mode 100644 direct/src/plugin/p3dInstanceManager.cxx create mode 100644 direct/src/plugin/p3dInstanceManager.h create mode 100644 direct/src/plugin/p3dPython.I create mode 100644 direct/src/plugin/p3dPython.cxx create mode 100644 direct/src/plugin/p3dPython.h create mode 100644 direct/src/plugin/p3dSession.I create mode 100644 direct/src/plugin/p3dSession.cxx create mode 100644 direct/src/plugin/p3dSession.h create mode 100644 direct/src/plugin/p3d_plugin.cxx create mode 100644 direct/src/plugin/p3d_plugin.h create mode 100644 direct/src/plugin/p3d_plugin_common.h create mode 100644 direct/src/plugin/p3d_plugin_composite1.cxx create mode 100644 direct/src/plugin/panda3d.cxx create mode 100644 direct/src/plugin/wingetopt.c create mode 100644 direct/src/plugin/wingetopt.h diff --git a/direct/src/plugin/Sources.pp b/direct/src/plugin/Sources.pp new file mode 100644 index 0000000000..16f9b28ce4 --- /dev/null +++ b/direct/src/plugin/Sources.pp @@ -0,0 +1,40 @@ +// This directory is still experimental. Define HAVE_P3D_PLUGIN in +// your Config.pp to build it. +#define BUILD_DIRECTORY $[HAVE_P3D_PLUGIN] + +#begin lib_target + #define TARGET p3d_plugin + + #define COMBINED_SOURCES \ + $[TARGET]_composite1.cxx + + #define SOURCES \ + 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 \ + p3d_plugin.h + +#end lib_target + +#begin bin_target + #define TARGET panda3d + + #define SOURCES \ + panda3d.cxx \ + $[if $[WINDOWS_PLATFORM],wingetopt.h wingetopt.c] + + #define WIN_SYS_LIBS user32.lib gdi32.lib + +#end bin_target diff --git a/direct/src/plugin/p3dInstance.I b/direct/src/plugin/p3dInstance.I new file mode 100644 index 0000000000..3f37f32e4c --- /dev/null +++ b/direct/src/plugin/p3dInstance.I @@ -0,0 +1,52 @@ +// Filename: p3dInstance.I +// Created by: drose (29May09) +// +//////////////////////////////////////////////////////////////////// +// +// 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: P3DInstance::get_p3d_filename +// Access: Public +// Description: Returns the p3d filename that was passed to the +// constructor. +//////////////////////////////////////////////////////////////////// +INLINE const string &P3DInstance:: +get_p3d_filename() const { + return _p3d_filename; +} + +//////////////////////////////////////////////////////////////////// +// Function: P3DInstance::get_session_key +// Access: Public +// Description: Returns a string that uniquely identifies this +// session. This is a constructed string that includes +// the supplied session_name, the python and panda +// version, and the publisher, as well as any other +// relevant details; it is guaranteed to be unique for +// each unique session required for different +// P3DInstances. +//////////////////////////////////////////////////////////////////// +INLINE const string &P3DInstance:: +get_session_key() const { + return _session_key; +} + +//////////////////////////////////////////////////////////////////// +// Function: P3DInstance::get_python_version +// Access: Public +// Description: Returns a string that uniquely identifies this +// instance's required Python version. +//////////////////////////////////////////////////////////////////// +INLINE const string &P3DInstance:: +get_python_version() const { + return _python_version; +} diff --git a/direct/src/plugin/p3dInstance.cxx b/direct/src/plugin/p3dInstance.cxx new file mode 100644 index 0000000000..0a7a3d5d86 --- /dev/null +++ b/direct/src/plugin/p3dInstance.cxx @@ -0,0 +1,214 @@ +// Filename: p3dInstance.cxx +// Created by: drose (29May09) +// +//////////////////////////////////////////////////////////////////// +// +// 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 "p3dInstance.h" +#include "p3dInstanceManager.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Function: P3DInstance::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +P3DInstance:: +P3DInstance(P3D_request_ready_func *func, + const string &p3d_filename, + P3D_window_type window_type, + int win_x, int win_y, + int win_width, int win_height, + P3D_window_handle parent_window, + const P3D_token *tokens[], size_t tokens_size) : + _func(func), + _p3d_filename(p3d_filename), + _window_type(window_type), + _win_x(win_x), _win_y(win_y), + _win_width(win_width), _win_height(win_height), + _parent_window(parent_window) +{ + P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr(); + + INIT_LOCK(_request_lock); + + // For the moment, all sessions will be shared. + /* + ostringstream strm; + strm << inst_mgr->get_unique_session_index(); + _session_key = strm.str(); + */ + _session_key = "common"; + _python_version = "python24"; + + _session = NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: P3DInstance::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +P3DInstance:: +~P3DInstance() { + assert(_session == NULL); + + DESTROY_LOCK(_request_lock); + + // TODO: empty _pending_requests queue. +} + + +//////////////////////////////////////////////////////////////////// +// Function: P3DInstance::has_property +// Access: Public +// Description: Returns true if the instance has the named property, +// false otherwise. +//////////////////////////////////////////////////////////////////// +bool P3DInstance:: +has_property(const string &property_name) const { + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: P3DInstance::get_property +// Access: Public +// Description: Returns the value of the named property, or empty +// string if there is no such property. Properties are +// created by the script run within the instance; they +// are used for communicating between scripting +// languages (for instance, communication between the +// Python-based Panda application, and the Javascript on +// the containing web page). +//////////////////////////////////////////////////////////////////// +string P3DInstance:: +get_property(const string &property_name) const { + return string(); +} + +//////////////////////////////////////////////////////////////////// +// Function: P3DInstance::set_property +// Access: Public +// Description: Changes the value of the named property. It is an +// error to call this on a property that does not +// already exist. +//////////////////////////////////////////////////////////////////// +void P3DInstance:: +set_property(const string &property_name, const string &value) { +} + + +//////////////////////////////////////////////////////////////////// +// Function: P3DInstance::has_request +// Access: Public +// Description: Returns true if the instance has any pending requests +// at the time of this call, false otherwise. +//////////////////////////////////////////////////////////////////// +bool P3DInstance:: +has_request() { + ACQUIRE_LOCK(_request_lock); + bool any_requests = !_pending_requests.empty(); + RELEASE_LOCK(_request_lock); + return any_requests; +} + +//////////////////////////////////////////////////////////////////// +// Function: P3DInstance::get_request +// Access: Public +// Description: Returns a newly-allocated P3D_request corresponding +// to the pending request for the host, or NULL if there +// is no pending request. If the return value is +// non-NULL, it should eventually be passed back to +// finish_request() for cleanup. +//////////////////////////////////////////////////////////////////// +P3D_request *P3DInstance:: +get_request() { + P3D_request *result = NULL; + ACQUIRE_LOCK(_request_lock); + if (!_pending_requests.empty()) { + result = _pending_requests.front(); + _pending_requests.pop_front(); + } + RELEASE_LOCK(_request_lock); + + return result; +} + +//////////////////////////////////////////////////////////////////// +// Function: P3DInstance::add_request +// Access: Public +// Description: May be called in any thread to add a new P3D_request +// to the pending_request queue for this instance. +//////////////////////////////////////////////////////////////////// +void P3DInstance:: +add_request(P3D_request *request) { + assert(request->_instance == this); + + ACQUIRE_LOCK(_request_lock); + _pending_requests.push_back(request); + RELEASE_LOCK(_request_lock); + + P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr(); + inst_mgr->signal_request_ready(); +} + +//////////////////////////////////////////////////////////////////// +// Function: P3DInstance::finish_request +// Access: Public +// Description: Deallocates a previously-returned request from +// get_request(). If handled is true, the request has +// been handled by the host; otherwise, it has been +// ignored. +//////////////////////////////////////////////////////////////////// +void P3DInstance:: +finish_request(P3D_request *request, bool handled) { + assert(request != NULL); + + // TODO. Delete sub-pieces more aggressively. Deal with handled flag. + delete request; +} + +//////////////////////////////////////////////////////////////////// +// Function: P3DInstance::feed_url_stream +// Access: Public +// Description: Called by the host in response to a get_url or +// post_url request, this sends the data retrieved from +// the requested URL, a piece at a time. +//////////////////////////////////////////////////////////////////// +void P3DInstance:: +feed_url_stream(int unique_id, + P3D_result_code result_code, + int http_status_code, + size_t total_expected_data, + const unsigned char *this_data, + size_t this_data_size) { +} + +//////////////////////////////////////////////////////////////////// +// Function: P3DInstance::fill_tokens +// Access: Private +// Description: Copies the C-style tokens array into the internal +// C++-style _tokens vector. +//////////////////////////////////////////////////////////////////// +void P3DInstance:: +fill_tokens(const P3D_token *tokens[], size_t tokens_size) { + for (size_t i = 0; i < tokens_size; ++i) { + Token token; + if (tokens[i]->_keyword != NULL) { + token._keyword = tokens[i]->_keyword; + } + if (tokens[i]->_value != NULL) { + token._value = tokens[i]->_value; + } + _tokens.push_back(token); + } +} diff --git a/direct/src/plugin/p3dInstance.h b/direct/src/plugin/p3dInstance.h new file mode 100644 index 0000000000..f8ac023aac --- /dev/null +++ b/direct/src/plugin/p3dInstance.h @@ -0,0 +1,92 @@ +// Filename: p3dInstance.h +// Created by: drose (29May09) +// +//////////////////////////////////////////////////////////////////// +// +// 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 P3DINSTANCE_H +#define P3DINSTANCE_H + +#include "p3d_plugin_common.h" + +#include +#include + +class P3DSession; + +//////////////////////////////////////////////////////////////////// +// Class : P3DInstance +// Description : This is an instance of a Panda3D window. +//////////////////////////////////////////////////////////////////// +class P3DInstance : public P3D_instance { +public: + P3DInstance(P3D_request_ready_func *func, + const string &p3d_filename, + P3D_window_type window_type, + int win_x, int win_y, + int win_width, int win_height, + P3D_window_handle parent_window, + const P3D_token *tokens[], size_t tokens_size); + ~P3DInstance(); + + bool has_property(const string &property_name) const; + string get_property(const string &property_name) const; + void set_property(const string &property_name, const string &value); + + bool has_request(); + P3D_request *get_request(); + void add_request(P3D_request *request); + void finish_request(P3D_request *request, bool handled); + + void feed_url_stream(int unique_id, + P3D_result_code result_code, + int http_status_code, + size_t total_expected_data, + const unsigned char *this_data, + size_t this_data_size); + + INLINE const string &get_p3d_filename() const; + INLINE const string &get_session_key() const; + INLINE const string &get_python_version() const; + +private: + void fill_tokens(const P3D_token *tokens[], size_t tokens_size); + + class Token { + public: + string _keyword; + string _value; + }; + typedef vector Tokens; + + P3D_request_ready_func *_func; + string _p3d_filename; + P3D_window_type _window_type; + int _win_x, _win_y; + int _win_width, _win_height; + P3D_window_handle _parent_window; + Tokens _tokens; + + string _session_key; + string _python_version; + P3DSession *_session; + + LOCK _request_lock; + typedef deque Requests; + Requests _pending_requests; + + friend class P3DSession; + friend class P3DPython; +}; + +#include "p3dInstance.I" + +#endif diff --git a/direct/src/plugin/p3dInstanceManager.I b/direct/src/plugin/p3dInstanceManager.I new file mode 100644 index 0000000000..a4ee3838b4 --- /dev/null +++ b/direct/src/plugin/p3dInstanceManager.I @@ -0,0 +1,36 @@ +// Filename: p3dInstanceManager.I +// Created by: drose (29May09) +// +//////////////////////////////////////////////////////////////////// +// +// 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: P3DInstanceManager::get_num_instances +// Access: Public +// Description: Returns the number of instances currently running +// within the world. +//////////////////////////////////////////////////////////////////// +INLINE int P3DInstanceManager:: +get_num_instances() const { + return _instances.size(); +} + +//////////////////////////////////////////////////////////////////// +// Function: P3DInstanceManager::get_dll_filename +// Access: Public +// Description: Returns the DLL filename that was passed to +// initialize(). +//////////////////////////////////////////////////////////////////// +INLINE const string &P3DInstanceManager:: +get_dll_filename() const { + return _dll_filename; +} diff --git a/direct/src/plugin/p3dInstanceManager.cxx b/direct/src/plugin/p3dInstanceManager.cxx new file mode 100644 index 0000000000..208eb39c1b --- /dev/null +++ b/direct/src/plugin/p3dInstanceManager.cxx @@ -0,0 +1,255 @@ +// Filename: p3dInstanceManager.cxx +// Created by: drose (29May09) +// +//////////////////////////////////////////////////////////////////// +// +// 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 "p3dInstanceManager.h" +#include "p3dInstance.h" +#include "p3dSession.h" +#include "p3dPython.h" + +P3DInstanceManager *P3DInstanceManager::_global_ptr; + +//////////////////////////////////////////////////////////////////// +// Function: P3DInstanceManager::Constructor +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +P3DInstanceManager:: +P3DInstanceManager() { + _unique_session_index = 0; + _current_python = NULL; + + _request_seq = 0; +#ifdef _WIN32 + _request_ready = CreateEvent(NULL, false, false, NULL); +#endif +} + +//////////////////////////////////////////////////////////////////// +// Function: P3DInstanceManager::Destructor +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +P3DInstanceManager:: +~P3DInstanceManager() { +} + +//////////////////////////////////////////////////////////////////// +// Function: P3DInstanceManager::initialize +// Access: Public +// Description: Called by the host at application startup, this +// receives the name of the DLL that contains this code +// (for patching purposes). It returns true if the DLL +// is successfully initialized, false if it should be +// immediately shut down and redownloaded. +//////////////////////////////////////////////////////////////////// +bool P3DInstanceManager:: +initialize(const string &config_xml, const string &dll_filename) { + _dll_filename = dll_filename; + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: P3DInstanceManager::create_instance +// Access: Public +// Description: Returns a newly-allocated P3DInstance with the +// indicated startup information. +//////////////////////////////////////////////////////////////////// +P3DInstance *P3DInstanceManager:: +create_instance(P3D_request_ready_func *func, + const string &p3d_filename, + P3D_window_type window_type, + int win_x, int win_y, + int win_width, int win_height, + P3D_window_handle parent_window, + const P3D_token *tokens[], size_t tokens_size) { + P3DInstance *inst = new P3DInstance(func, p3d_filename, + window_type, win_x, win_y, + win_width, win_height, parent_window, + tokens, tokens_size); + _instances.insert(inst); + + P3DSession *session; + Sessions::iterator si = _sessions.find(inst->get_session_key()); + if (si == _sessions.end()) { + session = new P3DSession(inst); + bool inserted = _sessions.insert(Sessions::value_type(session->get_session_key(), session)).second; + assert(inserted); + } else { + session = (*si).second; + } + + session->start_instance(inst); + + return inst; +} + +//////////////////////////////////////////////////////////////////// +// Function: P3DInstanceManager::finish_instance +// Access: Public +// Description: Terminates and removes a previously-returned +// instance. +//////////////////////////////////////////////////////////////////// +void P3DInstanceManager:: +finish_instance(P3DInstance *inst) { + Instances::iterator ii; + ii = _instances.find(inst); + assert(ii != _instances.end()); + _instances.erase(ii); + + Sessions::iterator si = _sessions.find(inst->get_session_key()); + assert(si != _sessions.end()); + P3DSession *session = (*si).second; + session->terminate_instance(inst); + + // If that was the last instance in this session, terminate the + // session. + if (session->get_num_instances() == 0) { + _sessions.erase(session->get_session_key()); + delete session; + } + + delete inst; +} + +//////////////////////////////////////////////////////////////////// +// Function: P3DInstanceManager::check_request +// Access: Public +// Description: If a request is currently pending on any instance, +// returns its pointer. Otherwise, returns NULL. +//////////////////////////////////////////////////////////////////// +P3DInstance *P3DInstanceManager:: +check_request() { + Instances::iterator ii; + for (ii = _instances.begin(); ii != _instances.end(); ++ii) { + P3DInstance *inst = (*ii); + if (inst->has_request()) { + return inst; + } + } + + return NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: P3DInstanceManager::wait_request +// Access: Public +// Description: Does not return until a request is pending on some +// instance, or until no instances remain. Use +// check_request to retrieve the pending request. Due +// to the possibility of race conditions, it is possible +// for this function to return when there is in fact no +// request pending (another thread may have extracted +// the request first). +//////////////////////////////////////////////////////////////////// +void P3DInstanceManager:: +wait_request() { + int seq = _request_seq; + + while (true) { + if (_instances.empty()) { + return; + } + if (check_request() != (P3DInstance *)NULL) { + return; + } + + // No pending requests; go to sleep. + if (seq == _request_seq) { + WaitForSingleObject(_request_ready, INFINITE); + } + seq = _request_seq; + } +} + +//////////////////////////////////////////////////////////////////// +// 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 +// Description: Returns a number used to uniquify the session_key for +// different instances. This number is guaranteed to be +// different at each call. +//////////////////////////////////////////////////////////////////// +int P3DInstanceManager:: +get_unique_session_index() { + ++_unique_session_index; + return _unique_session_index; +} + +//////////////////////////////////////////////////////////////////// +// Function: P3DInstanceManager::signal_request_ready +// Access: Public +// Description: May be called in any thread to indicate that a new +// P3D_request is available in some instance. This will +// wake up a sleeping wait_request() call, if any. +//////////////////////////////////////////////////////////////////// +void P3DInstanceManager:: +signal_request_ready() { + ++_request_seq; +#ifdef _WIN32 + SetEvent(_request_ready); +#endif +} + +//////////////////////////////////////////////////////////////////// +// Function: P3DInstanceManager::get_global_ptr +// Access: Public, Static +// Description: +//////////////////////////////////////////////////////////////////// +P3DInstanceManager *P3DInstanceManager:: +get_global_ptr() { + if (_global_ptr == NULL) { + _global_ptr = new P3DInstanceManager; + } + return _global_ptr; +} diff --git a/direct/src/plugin/p3dInstanceManager.h b/direct/src/plugin/p3dInstanceManager.h new file mode 100644 index 0000000000..cfec88606b --- /dev/null +++ b/direct/src/plugin/p3dInstanceManager.h @@ -0,0 +1,90 @@ +// Filename: p3dInstanceManager.h +// Created by: drose (29May09) +// +//////////////////////////////////////////////////////////////////// +// +// 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 P3DINSTANCEMANAGER_H +#define P3DINSTANCEMANAGER_H + +#include "p3d_plugin_common.h" + +#include +#include + +class P3DInstance; +class P3DSession; +class P3DPython; + +//////////////////////////////////////////////////////////////////// +// Class : P3DInstanceManager +// Description : This global class manages the set of instances in the +// universe. +//////////////////////////////////////////////////////////////////// +class P3DInstanceManager { +private: + P3DInstanceManager(); + ~P3DInstanceManager(); + +public: + bool initialize(const string &config_xml, const string &dll_filename); + + P3DInstance * + create_instance(P3D_request_ready_func *func, + const string &p3d_filename, + P3D_window_type window_type, + int win_x, int win_y, + int win_width, int win_height, + P3D_window_handle parent_window, + const P3D_token *tokens[], size_t tokens_size); + + void + finish_instance(P3DInstance *inst); + + 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; + + int get_unique_session_index(); + void signal_request_ready(); + + static P3DInstanceManager *get_global_ptr(); + +private: + string _dll_filename; + string _p3d_root_directory; + + typedef set Instances; + Instances _instances; + + typedef map Sessions; + Sessions _sessions; + + P3DPython *_current_python; + int _unique_session_index; + + // Implements a condition-var like behavior. + volatile int _request_seq; +#ifdef _WIN32 + HANDLE _request_ready; +#endif + + static P3DInstanceManager *_global_ptr; +}; + +#include "p3dInstanceManager.I" + +#endif diff --git a/direct/src/plugin/p3dPython.I b/direct/src/plugin/p3dPython.I new file mode 100644 index 0000000000..900bb81445 --- /dev/null +++ b/direct/src/plugin/p3dPython.I @@ -0,0 +1,49 @@ +// 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 new file mode 100644 index 0000000000..e69aa6356f --- /dev/null +++ b/direct/src/plugin/p3dPython.cxx @@ -0,0 +1,372 @@ +// 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 new file mode 100644 index 0000000000..834ab9cc5b --- /dev/null +++ b/direct/src/plugin/p3dPython.h @@ -0,0 +1,125 @@ +// 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/p3dSession.I b/direct/src/plugin/p3dSession.I new file mode 100644 index 0000000000..d823825c97 --- /dev/null +++ b/direct/src/plugin/p3dSession.I @@ -0,0 +1,48 @@ +// Filename: p3dSession.I +// Created by: drose (03Jun09) +// +//////////////////////////////////////////////////////////////////// +// +// 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: P3DSession::get_session_key +// Access: Public +// Description: Returns a string that uniquely identifies this +// session. See P3dInstance::get_session_key(). +//////////////////////////////////////////////////////////////////// +INLINE const string &P3DSession:: +get_session_key() const { + return _session_key; +} + +//////////////////////////////////////////////////////////////////// +// Function: P3DSession::get_python_version +// Access: Public +// Description: Returns a string that uniquely identifies this +// session's required Python version. +//////////////////////////////////////////////////////////////////// +INLINE const string &P3DSession:: +get_python_version() const { + return _python_version; +} + +//////////////////////////////////////////////////////////////////// +// Function: P3DSession::get_num_instances +// Access: Public +// Description: Returns the number of instances currently running +// within the session. When this is zero, the session +// may be safely deleted. +//////////////////////////////////////////////////////////////////// +INLINE int P3DSession:: +get_num_instances() const { + return _instances.size(); +} diff --git a/direct/src/plugin/p3dSession.cxx b/direct/src/plugin/p3dSession.cxx new file mode 100644 index 0000000000..4f8e082072 --- /dev/null +++ b/direct/src/plugin/p3dSession.cxx @@ -0,0 +1,93 @@ +// Filename: p3dSession.cxx +// Created by: drose (03Jun09) +// +//////////////////////////////////////////////////////////////////// +// +// 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 "p3dSession.h" +#include "p3dInstance.h" +#include "p3dInstanceManager.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Function: P3DSession::Constructor +// Access: Public +// Description: Creates a new session, corresponding to a new +// subprocess with its own copy of Python. The initial +// parameters for the session are taken from the +// indicated instance object (but the instance itself is +// not automatically started within the session). +//////////////////////////////////////////////////////////////////// +P3DSession:: +P3DSession(P3DInstance *inst) { + _session_key = inst->get_session_key(); + _python_version = inst->get_python_version(); + _python = NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: P3DSession::Destructor +// Access: Public +// Description: Terminates the session by shutting down Python and +// stopping the subprocess. +//////////////////////////////////////////////////////////////////// +P3DSession:: +~P3DSession() { +} + +//////////////////////////////////////////////////////////////////// +// Function: P3DSession::start_instance +// Access: Public +// Description: Adds the indicated instance to the session, and +// starts it running. It is an error if the instance +// has been started anywhere else. +// +// The instance must have the same session_key as the +// one that was passed to the P3DSession constructor. +//////////////////////////////////////////////////////////////////// +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); + + inst->_session = this; + bool inserted = _instances.insert(inst).second; + assert(inserted); + + P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr(); + P3DPython *python = inst_mgr->start_python(_python_version); + if (python != NULL) { + python->start_session(this, inst); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: P3DSession::terminate_instance +// Access: Public +// Description: Removes the indicated instance from the session, and +// stops it. It is an error if the instance is not +// already running on this session. +//////////////////////////////////////////////////////////////////// +void P3DSession:: +terminate_instance(P3DInstance *inst) { + if (_python != NULL) { + _python->terminate_session(this); + assert(_python == NULL); + } + + if (inst->_session == this) { + inst->_session = NULL; + _instances.erase(inst); + } +} diff --git a/direct/src/plugin/p3dSession.h b/direct/src/plugin/p3dSession.h new file mode 100644 index 0000000000..3dbbc6d532 --- /dev/null +++ b/direct/src/plugin/p3dSession.h @@ -0,0 +1,58 @@ +// Filename: p3dSession.h +// Created by: drose (03Jun09) +// +//////////////////////////////////////////////////////////////////// +// +// 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 P3DSESSION_H +#define P3DSESSION_H + +#include "p3d_plugin_common.h" + +#include + +class P3DInstance; + +//////////////////////////////////////////////////////////////////// +// Class : P3DSession +// Description : Corresponds to a single session: a subprocess with a +// unique instance of Python running within it, which +// might include one or more P3DInstance objects running +// in the same memory space with each other. +//////////////////////////////////////////////////////////////////// +class P3DSession { +public: + P3DSession(P3DInstance *inst); + ~P3DSession(); + + INLINE const string &get_session_key() const; + INLINE const string &get_python_version() const; + + void start_instance(P3DInstance *inst); + void terminate_instance(P3DInstance *inst); + + INLINE int get_num_instances() const; + +private: + string _session_key; + string _python_version; + + P3DPython *_python; + + typedef set Instances; + Instances _instances; + + friend class P3DPython; +}; + +#include "p3dSession.I" + +#endif diff --git a/direct/src/plugin/p3d_plugin.cxx b/direct/src/plugin/p3d_plugin.cxx new file mode 100644 index 0000000000..f3d58e809a --- /dev/null +++ b/direct/src/plugin/p3d_plugin.cxx @@ -0,0 +1,166 @@ +// Filename: p3d_plugin.cxx +// Created by: drose (29May09) +// +//////////////////////////////////////////////////////////////////// +// +// 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 "p3d_plugin_common.h" +#include "p3dInstanceManager.h" +#include "p3dInstance.h" + +// Use a simple lock to protect the C-style API functions in this +// module from parallel access by multiple threads in the host. + +bool initialized_lock = false; +LOCK _lock; + +bool +P3D_initialize(const char *config_xml, const char *dll_filename) { + string config_xml_str; + if (config_xml != NULL) { + config_xml_str = config_xml; + } + + string dll_filename_str; + if (dll_filename != NULL) { + dll_filename_str = dll_filename; + } + + if (!initialized_lock) { + INIT_LOCK(_lock); + initialized_lock = true; + } + ACQUIRE_LOCK(_lock); + P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr(); + bool result = inst_mgr->initialize(config_xml_str, dll_filename_str); + RELEASE_LOCK(_lock); + return result; +} + +void +P3D_free_string(char *string) { + ACQUIRE_LOCK(_lock); + delete [] string; + RELEASE_LOCK(_lock); +} + +P3D_instance * +P3D_create_instance(P3D_request_ready_func *func, + const char *p3d_filename, + P3D_window_type window_type, + int win_x, int win_y, + int win_width, int win_height, + P3D_window_handle parent_window, + const P3D_token *tokens[], size_t tokens_size) { + ACQUIRE_LOCK(_lock); + P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr(); + P3DInstance *result = + inst_mgr->create_instance(func, p3d_filename, window_type, + win_x, win_y, win_width, win_height, + parent_window, tokens, tokens_size); + RELEASE_LOCK(_lock); + return result; +} + +void +P3D_instance_finish(P3D_instance *instance) { + ACQUIRE_LOCK(_lock); + P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr(); + inst_mgr->finish_instance((P3DInstance *)instance); + RELEASE_LOCK(_lock); +} + +bool +P3D_instance_has_property(P3D_instance *instance, + const char *property_name) { + ACQUIRE_LOCK(_lock); + bool result = ((P3DInstance *)instance)->has_property(property_name); + RELEASE_LOCK(_lock); + return result; +} + +char * +P3D_instance_get_property(P3D_instance *instance, + const char *property_name) { + ACQUIRE_LOCK(_lock); + string value = ((P3DInstance *)instance)->get_property(property_name); + + char *result = new char[value.length() + 1]; + RELEASE_LOCK(_lock); + + memcpy(result, value.data(), value.length()); + result[value.length()] = '\0'; + return result; +} + +void +P3D_instance_set_property(P3D_instance *instance, + const char *property_name, + const char *value) { + ACQUIRE_LOCK(_lock); + ((P3DInstance *)instance)->set_property(property_name, value); + RELEASE_LOCK(_lock); +} + +P3D_request * +P3D_instance_get_request(P3D_instance *instance) { + ACQUIRE_LOCK(_lock); + P3D_request *result = ((P3DInstance *)instance)->get_request(); + RELEASE_LOCK(_lock); + return result; +} + +P3D_instance * +P3D_check_request(bool wait) { + ACQUIRE_LOCK(_lock); + P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr(); + P3D_instance *inst = inst_mgr->check_request(); + + if (inst != NULL || !wait) { + RELEASE_LOCK(_lock); + return inst; + } + + // Now we have to block until a request is available. + while (inst == NULL && inst_mgr->get_num_instances() != 0) { + RELEASE_LOCK(_lock); + inst_mgr->wait_request(); + ACQUIRE_LOCK(_lock); + inst = inst_mgr->check_request(); + } + + RELEASE_LOCK(_lock); + return inst; +} + +void +P3D_request_finish(P3D_request *request, bool handled) { + ACQUIRE_LOCK(_lock); + if (request != (P3D_request *)NULL) { + ((P3DInstance *)request->_instance)->finish_request(request, handled); + } + RELEASE_LOCK(_lock); +} + +void +P3D_instance_feed_url_stream(P3D_instance *instance, int unique_id, + P3D_result_code result_code, + int http_status_code, + size_t total_expected_data, + const unsigned char *this_data, + size_t this_data_size) { + ACQUIRE_LOCK(_lock); + ((P3DInstance *)instance)-> + feed_url_stream(unique_id, result_code, http_status_code, + total_expected_data, this_data, this_data_size); + RELEASE_LOCK(_lock); +} + diff --git a/direct/src/plugin/p3d_plugin.h b/direct/src/plugin/p3d_plugin.h new file mode 100644 index 0000000000..d7f6375a8e --- /dev/null +++ b/direct/src/plugin/p3d_plugin.h @@ -0,0 +1,458 @@ +/* Filename: p3d_plugin.h + * Created by: drose (28May09) + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * + * 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_PLUGIN_H +#define P3D_PLUGIN_H + +/* This file defines the C-level API to Panda's plugin system. This + API is intended to provide basic functionality for loading and + running Panda's .p3d files, particularly within a browser. + + This plugin code is intended to be loaded and run as a standalone + DLL. It will in turn be responsible for fetching and installing + the appropriate version of Panda and Python, as well as any + required supporting libraries. + + Note that this code defines only the interface between the actual + browser plugin and the Panda code. The actual plugin itself will + be a separate piece of code, written in ActiveX or NPIP or whatever + API is required for a given browser, which is designed to download + and link with this layer. + + The browser or launching application will be referred to as the + "host" in this documentation. The host should load this plugin dll + only once, but may then use it to create multiple simultaneous + different instances of Panda windows. + + Filenames passed through this interface are in native OS-specific + form, e.g. with a leading drive letter and backslashes, not in + Panda's Unix-like form (except on Unix-based OSes, of course). +*/ + +#include + +#ifdef _WIN32 +#include + +#ifdef BUILDING_P3D_PLUGIN +#define EXPCL_P3D_PLUGIN __declspec(dllexport) +#else +#define EXPCL_P3D_PLUGIN __declspec(dllimport) +#endif + +#else /* _WIN32 */ +#define EXPCL_P3D_PLUGIN + +#endif /* _WIN32 */ + +#ifdef __cplusplus +extern "C" { +#endif + +/* In the following function prototypes, all functions are declared + initially as typedefs only, and then the actual function references + are finally declared at the end of this file, but only if + P3D_PLUGIN_PROTOTYPES is defined. This is intended to allow + including this file without building an implicit reference to the + functions themselves, allowing the plugin library to be loaded via + an explicit LoadLibrary() or equivalent call. */ + +/************************ GLOBAL FUNCTIONS **************************/ + +/* The following interfaces are global to the plugin space, as opposed + to being specific to a particular instance. */ + +/* This function should be called immediately after the plugin is + loaded. The config_xml parameter is the plugin configuration data, + formatted as an XML stream (it contains the XML data itself; it is + not a filename). The exact contents of this XML stream are + documented elsewhere. This config_xml parameter may be the empty + string or NULL; if so, the plugin will use its own internal default + configuration. + + The dll_filename parameter is the filename of the plugin's dll + itself, which is needed for self-patching. + + This function returns true if the plugin is valid, false otherwise. + If it returns false, the host should not call any more functions in + this API, and should immediately unload the DLL and (if possible) + download a new one. */ +typedef bool +P3D_initialize_func(const char *config_xml, const char *dll_filename); + +/* This function frees a pointer returned by + P3D_instance_get_property(), or another similar function that + returns a dynamically-allocated string. */ +typedef void +P3D_free_string_func(char *string); + +/********************** INSTANCE MANAGEMENT **************************/ + +/* The following interfaces define the API to manage individual + Panda3D instances. Each instance can display a separate 3-D + graphics window simultaneously on the host or on the desktop. The + instances operate generally independently of each other. */ + +/* This structure defines the handle to a single instance. The host + may access the _request_pending member, which will be true if the + host should call P3D_instance_get_request(). */ +typedef struct { + bool _request_pending; + + /* Additional opaque data may be stored here. */ +} P3D_instance; + +/* This structure abstracts out the various window handle types for + the different platforms. */ +typedef struct { +#ifdef _WIN32 + HWND _hwnd; +#endif +} P3D_window_handle; + +/* This enum lists the different kinds of window types that may be + requested for the instance. These define the way that the instance + will create its main Panda3D window. The instance will treat this + as a request only; it is always free to create whatever kind of + window it likes. */ +typedef enum { + /* Embedded: the plugin window is embedded within the host window. + This is the normal kind of window for an object embedded within a + browser page. Pass a valid window handle in for parent_window, + and valid coordinates on the parent window for win_x, win_y, + win_width, win_height. */ + P3D_WT_embedded, + + /* Toplevel: the plugin window is a toplevel window on the user's + desktop. Pass valid desktop coordinates in for win_x, win_y, + win_width, and win_height. If all of these are zero, the plugin + will create a window wherever it sees fit. */ + P3D_WT_toplevel, + + /* Fullscreen: the plugin window is a fullscreen window, completely + overlaying the entire screen and changing the desktop resolution. + Pass a valid desktop size in for win_width and win_height (win_x + and win_y are ignored). If win_width and win_height are zero, + the plugin will create a fullscreen window of its own preferred + size. */ + P3D_WT_fullscreen, + + /* Hidden: there is no window at all for the plugin. */ + P3D_WT_hidden, + +} P3D_window_type; + + +/* This function pointer must be passed to P3D_create_instance(), + below. The host must pass in a pointer to a valid function in the + host's address space, or NULL. If not NULL, this function will be + called asynchronously by the plugin when the plugin needs to make a + request from the host. After this notification has been received, + the host should call P3D_instance_get_request() (at its + convenience) to retrieve the actual plugin request. If the host + passes NULL for this function pointer, asynchronous notifications + will not be provided, and the host must be responsible for calling + P3D_instance_get_request() from time to time. */ + +/* Note that, unlike the other func typedefs in this header file, this + declaration is not naming a function within the plugin itself. + Instead, it is a typedef for a function pointer that must be + supplied by the host. */ + +typedef void +P3D_request_ready_func(P3D_instance *instance); + +/* This structure is used to represent a single keyword/value pair + that appears within the embed syntax on the HTML page. An array of + these values is passed to the P3D instance to represent all of the + additional keywords that may appear within this syntax; it is up to + the plugin to interpret these additional keywords correctly. */ +typedef struct { + char *_keyword; + char *_value; +} P3D_token; + +/* This function creates a new Panda3D instance. For p3d_filename + pass the name of a file on disk that contains the contents of the + p3d file that should be launched within the instance. For tokens, + pass an array of P3D_token elements (above), which correspond to + the user-supplied keyword/value pairs that may appear in the embed + token within the HTML syntax; the host is responsible for + allocating this array, and for deallocating it after this call (the + plugin will make its own copy of the array). */ + +typedef P3D_instance * +P3D_create_instance_func(P3D_request_ready_func *func, + const char *p3d_filename, + P3D_window_type window_type, + int win_x, int win_y, + int win_width, int win_height, + P3D_window_handle parent_window, + const P3D_token *tokens[], size_t tokens_size); + + +/* Call this function to interrupt a particular instance and stop it + from rendering, for instance when the user navigates away from the + page containing it. After calling this function, you should not + reference the P3D_instance pointer again. */ +typedef void +P3D_instance_finish_func(P3D_instance *instance); + + +/********************** SCRIPTING SUPPORT **************************/ + +/* The following interfaces are provided to support controlling the + plugin via JavaScript or related interfaces on the browser. */ + +/* Call this function to query whether the instance has a property of + the indicated name. If this returns true, you may then query + P3D_instance_get_property() or P3D_instance_set_property(). */ +typedef bool +P3D_instance_has_property_func(P3D_instance *instance, + const char *property_name); + +/* Call this function to query the value of the indicated property. + It is an error to call this if the property does not exist; call + P3D_instance_has_property() first to ensure this is so. The return + value has been dynamically allocated and should be passed to + P3D_free_string() when it is no longer needed. */ +typedef char * +P3D_instance_get_property_func(P3D_instance *instance, + const char *property_name); + +/* Call this function to set the value of the indicated property. */ +typedef void +P3D_instance_set_property_func(P3D_instance *instance, + const char *property_name, + const char *value); + +/********************** REQUEST HANDLING **************************/ + +/* The plugin may occasionally have an asynchronous request to pass up + to the host. The following structures implement this interface. + The design is intended to support single-threaded as well as + multi-threaded implementations in the host; there is only the one + callback function, P3D_request_ready (above), which may be called + asynchronously by the plugin. The host should be careful that this + callback function is protected from mutual access. The callback + function implementation may be as simple as setting a flag that the + host will later check within its main processing loop. + + Once P3D_request_ready() has been received, the host should call + P3D_instance_get_request() to query the nature of the request. + This call may be made synchronously, i.e. within the host's main + processing loop. After each request is serviced, the host should + release the request via P3D_request_finish() and then call + P3D_instance_get_request() again until that function returns NULL. + + The requests themselves are implemented via a hierarchy of structs. + Each request is stored in a different kind of struct, allowing the + different requests to store a variety of data. An enumerated value + indicates the particular request type retrieved. */ + +/* This represents the type of a request returned by + P3D_instance_get_request. More types may be added later. */ +typedef enum { + P3D_RT_stop, + P3D_RT_new_config_xml, + P3D_RT_patch, + P3D_RT_get_url, + P3D_RT_post_url, +} P3D_request_type; + +/* Structures corresponding to the request types in the above enum. */ + +/* A stop request. The instance would like to stop itself. No + additional data is required. The host should respond by calling + P3D_instance_finish(). */ +typedef struct { +} P3D_request_stop; + +/* A new config_xml request. The plugin has determined that its + operating environment has changed, and that a new config.xml file + should be stored for future sessions. The plugin passes a new + config_xml data stream to the host, which should save it + permanently within its own cache. The original config_xml should + be replaced with this new stream; for all future sessions, when the + host loads and starts the plugin DLL, it should pass this new + config_xml stream to the P3D_initialize() function. */ +typedef struct { + const char *_config_xml; +} P3D_request_new_config_xml; + +/* A patch request. The plugin has determined that it is out of date + and needs to be patched. It has already applied the patch to + itself, and the resulting patched dll is referenced in the request + data. The host should respond by finishing all active instances, + unloading the DLL, moving the patched dll onto the original DLL, + and reloading the DLL and (optionally) restarting the instances. */ +typedef struct { + const char *_patched_filename; +} P3D_request_patch; + +/* A get_url request. The plugin would like to retrieve data for a + particular URL. The plugin is responsible for supplying a valid + URL string, and a unique integer ID. The unique ID is needed to + feed the results of the URL back to the plugin. If possible, the + host should be prepared to handle multiple get_url requests in + parallel, but it is allowed to handle them all one at a time if + necessary. As data comes in from the url, the host should call + P3D_instance_feed_url_stream(). +*/ +typedef struct { + const char *_url; + int _unique_id; +} P3D_request_get_url; + +/* A post_url request. Similar to get_url, but additional data is to + be sent via POST to the indicated URL. The result of the POST is + returned in a mechanism similar to get_url. +*/ +typedef struct { + const char *_url; + const char *_post_data; + size_t _post_data_size; + int _unique_id; +} P3D_request_post_url; + +/* This is the overall structure that represents a single request. It + is returned by P3D_instance_get_request(). */ +typedef struct { + P3D_instance *_instance; + P3D_request_type _request_type; + union { + P3D_request_stop _stop; + P3D_request_new_config_xml _new_config_xml; + P3D_request_patch _patch; + P3D_request_get_url _get_url; + P3D_request_post_url _post_url; + } _request; +} P3D_request; + +/* After a call to P3D_request_ready(), or from time to time in + general, the host should call this function to see if there are any + pending requests from the plugin. The function will return a + freshly-allocated request if there is a request ready, or NULL if + there are no requests. After a receipt of P3D_request_ready(), + the host should call this function repeatedly until it returns NULL + (there might be multiple requests for a single receipt of + P3D_request_ready()). Each request should be processed, then + released via P3D_request_finish(). */ +typedef P3D_request * +P3D_instance_get_request_func(P3D_instance *instance); + +/* This method may also be used to test whether a request to be ready. + If any open instance has a pending request, this function will + return a pointer to one of them (which you may then pass to + P3D_instance_get_request_func). If no instances have a pending + request, this function will return NULL. If wait is true, this + function will never return NULL unless there are no instances open; + instead, it will wait indefinitely until there is a request + available. + + Note that, due to race conditions, it is possible for this function + to return a P3D_instance that does not in fact have any requests + pending (another thread may have checked the request first). You + should always verify that the return value of + P3D_instance_get_request() is not NULL. */ +typedef P3D_instance * +P3D_check_request_func(bool wait); + +/* A request retrieved by P3D_instance_get_request() should eventually + be passed here, after it has been handled, to deallocate its + resources and prevent a memory leak. The 'handled' flag should be + passed true if the host has handled the request, or false if it has + ignored it (e.g. because it does not implement support for this + particular request type). After calling this function, you should + not reference the P3D_request pointer again. */ +typedef void +P3D_request_finish_func(P3D_request *request, bool handled); + +/* This code is passed to P3D_instance_feed_url_stream, below, as data + is retrieved from the URL. */ +typedef enum { + /* in progress: the query is still in progress, and another call + will be made in the future. */ + P3D_RC_in_progress, + + /* done: the query is done, and all data has been retrieved without + error. This call represents the last of the data. */ + P3D_RC_done, + + /* generic_error: some error other than an HTTP error has occurred, + for instance, lack of connection to the server, or malformed URL. + No more data will be forthcoming. */ + P3D_RC_generic_error, + + /* An HTTP error has occurred, for instance 404. The particular + status code will be supplied in the http_status_code parameter. + There may or may not be data associated with this error as well. + However, no more data will be delivered after this call. */ + P3D_RC_http_error, +} P3D_result_code; + +/* This function is used by the host to handle a get_url request, + above. As it retrieves data from the URL, it should call this + function from time to time to feed that data to the plugin. + + instance and unique_id are from the original get_url() request. + + result_code and http_status_code indicates the current status of + the request, as described above; the call will be made again in the + future if its result_code is P3D_RC_in_progress. + + total_expected_data represents the host's best guess at the total + amount of data that will be retrieved. It is acceptable if this + guess doesn't match the actual data received at all. Set it to 0 + if the host has no idea. This value may change from one call to + the next. + + this_data and this_data_size describe the most recent block of data + retrieved from the URL. Each chunk of data passed to this function + is appended together by the plugin to define the total set of data + retrieved from the URL. For a particular call to feed_url_stream, + this may contain no data at all (e.g. this_data_size may be 0). + */ +typedef void +P3D_instance_feed_url_stream_func(P3D_instance *instance, int unique_id, + P3D_result_code result_code, + int http_status_code, + size_t total_expected_data, + const unsigned char *this_data, + size_t this_data_size); + + +#ifdef P3D_FUNCTION_PROTOTYPES + +/* Define all of the actual prototypes for the above functions. */ +EXPCL_P3D_PLUGIN P3D_initialize_func P3D_initialize; +EXPCL_P3D_PLUGIN P3D_free_string_func P3D_free_string; +EXPCL_P3D_PLUGIN P3D_create_instance_func P3D_create_instance; +EXPCL_P3D_PLUGIN P3D_instance_finish_func P3D_instance_finish; +EXPCL_P3D_PLUGIN P3D_instance_has_property_func P3D_instance_has_property; +EXPCL_P3D_PLUGIN P3D_instance_get_property_func P3D_instance_get_property; +EXPCL_P3D_PLUGIN P3D_instance_set_property_func P3D_instance_set_property; +EXPCL_P3D_PLUGIN P3D_instance_get_request_func P3D_instance_get_request; +EXPCL_P3D_PLUGIN P3D_check_request_func P3D_check_request; +EXPCL_P3D_PLUGIN P3D_request_finish_func P3D_request_finish; +EXPCL_P3D_PLUGIN P3D_instance_feed_url_stream_func P3D_instance_feed_url_stream; +#endif /* P3D_FUNCTION_PROTOTYPES */ + +#ifdef __cplusplus +}; /* end of extern "C" */ +#endif + +#endif /* P3D_PLUGIN_H */ + + diff --git a/direct/src/plugin/p3d_plugin_common.h b/direct/src/plugin/p3d_plugin_common.h new file mode 100644 index 0000000000..00c27c2e37 --- /dev/null +++ b/direct/src/plugin/p3d_plugin_common.h @@ -0,0 +1,43 @@ +// Filename: p3d_plugin_common.h +// Created by: drose (29May09) +// +//////////////////////////////////////////////////////////////////// +// +// 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_PLUGIN_COMMON +#define P3D_PLUGIN_COMMON + +// This header file is included by all C++ files in this directory; it +// provides some common symbol declarations. + +#define P3D_FUNCTION_PROTOTYPES +#define BUILDING_P3D_PLUGIN + +#include "p3d_plugin.h" + +#include +#include +#include + +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 new file mode 100644 index 0000000000..069fc76349 --- /dev/null +++ b/direct/src/plugin/p3d_plugin_composite1.cxx @@ -0,0 +1,5 @@ +#include "p3d_plugin.cxx" +#include "p3dInstance.cxx" +#include "p3dInstanceManager.cxx" +#include "p3dPython.cxx" +#include "p3dSession.cxx" diff --git a/direct/src/plugin/panda3d.cxx b/direct/src/plugin/panda3d.cxx new file mode 100644 index 0000000000..dfbdc42c0c --- /dev/null +++ b/direct/src/plugin/panda3d.cxx @@ -0,0 +1,366 @@ +// Filename: panda3d.cxx +// Created by: drose (03Jun09) +// +//////////////////////////////////////////////////////////////////// +// +// 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 "p3d_plugin.h" + +#include +#include + +#ifdef _WIN32 +#include "wingetopt.h" +#include +#else +#include +#endif + +using namespace std; + +static const string default_plugin_filename = "libp3d_plugin"; + +P3D_initialize_func *P3D_initialize; +P3D_free_string_func *P3D_free_string; +P3D_create_instance_func *P3D_create_instance; +P3D_instance_finish_func *P3D_instance_finish; +P3D_instance_has_property_func *P3D_instance_has_property; +P3D_instance_get_property_func *P3D_instance_get_property; +P3D_instance_set_property_func *P3D_instance_set_property; +P3D_instance_get_request_func *P3D_instance_get_request; +P3D_check_request_func *P3D_check_request; +P3D_request_finish_func *P3D_request_finish; +P3D_instance_feed_url_stream_func *P3D_instance_feed_url_stream; + +bool +load_plugin(const string &config_xml_filename, + const string &p3d_plugin_filename) { + string filename = p3d_plugin_filename; + if (filename.empty()) { + // Look for the plugin along the path. + filename = default_plugin_filename; +#ifdef _WIN32 + filename += ".dll"; +#else + filename += ".so"; +#endif + } + +#ifdef _WIN32 + HMODULE module = LoadLibrary(filename.c_str()); + if (module == NULL) { + // Couldn't load the DLL. + return false; + } + + // Get the full path to the DLL in case it was found along the path. + static const buffer_size = 4096; + static char buffer[buffer_size]; + if (GetModuleFileName(module, buffer, buffer_size) != 0) { + if (GetLastError() != 0) { + filename = buffer; + } + } + cerr << filename << "\n"; + + // Now get all of the function pointers. + P3D_initialize = (P3D_initialize_func *)GetProcAddress(module, "P3D_initialize"); + P3D_free_string = (P3D_free_string_func *)GetProcAddress(module, "P3D_free_string"); + P3D_create_instance = (P3D_create_instance_func *)GetProcAddress(module, "P3D_create_instance"); + P3D_instance_finish = (P3D_instance_finish_func *)GetProcAddress(module, "P3D_instance_finish"); + P3D_instance_has_property = (P3D_instance_has_property_func *)GetProcAddress(module, "P3D_instance_has_property"); + P3D_instance_get_property = (P3D_instance_get_property_func *)GetProcAddress(module, "P3D_instance_get_property"); + P3D_instance_set_property = (P3D_instance_set_property_func *)GetProcAddress(module, "P3D_instance_set_property"); + P3D_instance_get_request = (P3D_instance_get_request_func *)GetProcAddress(module, "P3D_instance_get_request"); + P3D_check_request = (P3D_check_request_func *)GetProcAddress(module, "P3D_check_request"); + P3D_request_finish = (P3D_request_finish_func *)GetProcAddress(module, "P3D_request_finish"); + P3D_instance_feed_url_stream = (P3D_instance_feed_url_stream_func *)GetProcAddress(module, "P3D_instance_feed_url_stream"); +#endif // _WIN32 + + // Ensure that all of the function pointers have been found. + if (P3D_initialize == NULL || + P3D_free_string == NULL || + P3D_create_instance == NULL || + P3D_instance_finish == NULL || + P3D_instance_has_property == NULL || + P3D_instance_get_property == NULL || + P3D_instance_set_property == NULL || + P3D_instance_get_request == NULL || + P3D_check_request == NULL || + P3D_request_finish == NULL || + P3D_instance_feed_url_stream == NULL) { + return false; + } + + // Successfully loaded. + if (!P3D_initialize(NULL, filename.c_str())) { + // Oops, failure to initialize. + return false; + } + + return true; +} + +void +handle_request(P3D_request *request) { + bool handled = false; + + switch (request->_request_type) { + case P3D_RT_stop: + P3D_instance_finish(request->_instance); + handled = true; + break; + + default: + // Some request types are not handled. + break; + }; + + P3D_request_finish(request, handled); +} + +#ifdef _WIN32 +LONG WINAPI +window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { + switch (msg) { + case WM_DESTROY: + PostQuitMessage(0); + break; + }; + + return DefWindowProc(hwnd, msg, wparam, lparam); +} + +void +make_parent_window(P3D_window_handle &parent_window, + int win_width, int win_height) { + WNDCLASS wc; + + HINSTANCE application = GetModuleHandle(NULL); + ZeroMemory(&wc, sizeof(WNDCLASS)); + wc.lpfnWndProc = window_proc; + wc.hInstance = application; + wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); + wc.lpszClassName = "panda3d"; + + if (!RegisterClass(&wc)) { + cerr << "Could not register window class!\n"; + exit(1); + } + + DWORD window_style = + WS_POPUP | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | + WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | + WS_SIZEBOX | WS_MAXIMIZEBOX; + + HWND toplevel_window = + CreateWindow("panda3d", "Panda3D", window_style, + CW_USEDEFAULT, CW_USEDEFAULT, win_width, win_height, + NULL, NULL, application, 0); + if (!toplevel_window) { + cerr << "Could not create toplevel window!\n"; + exit(1); + } + + ShowWindow(toplevel_window, SW_SHOWNORMAL); + + parent_window._hwnd = toplevel_window; +} +#endif // _WIN32 + +void +usage() { + cerr + << "\nUsage:\n" + << " panda3d [opts] file.p3d [file_b.p3d file_c.p3d ...]\n\n" + + << "This program is used to execute a Panda3D application bundle stored\n" + << "in a .p3d file. Normally you only run one p3d bundle at a time,\n" + << "but it is possible to run multiple bundles simultaneously.\n\n" + + << "Options:\n\n" + + << " -c config.xml\n" + << " Specify the name of the config.xml file that informs the Panda\n" + << " plugin where to download patches and such. This is normally\n" + << " not necessary to specify, since it is already stored within\n" + << " the Panda plugin itself.\n\n" + + << " -p p3d_plugin.dll\n" + << " Specify the full path to the particular Panda plugin DLL to\n" + << " run. Normally, this will be found by searching in the usual\n" + << " places.\n\n" + + << " -t [toplevel|embedded|fullscreen|hidden]\n" + << " Specify the type of graphic window to create. If you specify " + << " \"embedded\", a new window is created to be the parent.\n\n" + + << " -s width,height\n" + << " Specify the size of the graphic window.\n\n" + + << " -o x,y\n" + << " Specify the position (origin) of the graphic window on the\n" + << " screen, or on the parent window.\n\n"; +} + +bool +parse_int_pair(char *arg, int &x, int &y) { + char *endptr; + x = strtol(arg, &endptr, 10); + if (*endptr == ',') { + y = strtol(endptr + 1, &endptr, 10); + if (*endptr == '\0') { + return true; + } + } + + // Some parse error on the string. + return false; +} + +int +main(int argc, char *argv[]) { + extern char *optarg; + extern int optind; + const char *optstr = "c:p:t:s:o:h"; + + string config_xml_filename; + string p3d_plugin_filename; + P3D_window_type window_type = P3D_WT_toplevel; + int win_x = 0, win_y = 0; + int win_width = 0, win_height = 0; + + int flag = getopt(argc, argv, optstr); + + while (flag != EOF) { + switch (flag) { + case 'c': + config_xml_filename = optarg; + break; + + case 'p': + p3d_plugin_filename = optarg; + break; + + case 't': + if (strcmp(optarg, "toplevel") == 0) { + window_type = P3D_WT_toplevel; + } else if (strcmp(optarg, "embedded") == 0) { + window_type = P3D_WT_embedded; + } else if (strcmp(optarg, "fullscreen") == 0) { + window_type = P3D_WT_fullscreen; + } else if (strcmp(optarg, "hidden") == 0) { + window_type = P3D_WT_hidden; + } else { + cerr << "Invalid value for -t: " << optarg << "\n"; + return 1; + } + break; + + case 's': + if (!parse_int_pair(optarg, win_width, win_height)) { + cerr << "Invalid value for -s: " << optarg << "\n"; + return 1; + } + break; + + case 'o': + if (!parse_int_pair(optarg, win_x, win_y)) { + cerr << "Invalid value for -o: " << optarg << "\n"; + return 1; + } + break; + + case 'h': + case '?': + default: + usage(); + return 1; + } + flag = getopt(argc, argv, optstr); + } + + argc -= (optind-1); + argv += (optind-1); + + if (argc < 2) { + usage(); + return 1; + } + + if (!load_plugin(config_xml_filename, p3d_plugin_filename)) { + cerr << "Unable to load Panda3D plugin.\n"; + return 1; + } + + P3D_window_handle parent_window; + if (window_type == P3D_WT_embedded) { + // The user asked for an embedded window. Create a toplevel + // window to be its parent, of the requested size. + make_parent_window(parent_window, win_width, win_height); + + // And center the graphics window within that parent window. + win_x = (int)(win_width * 0.1); + win_y = (int)(win_height * 0.1); + win_width = (int)(win_width * 0.8); + win_height = (int)(win_height * 0.8); + } + + // For now, only one instance at a time is supported. Ignore the + // remaining command-line parameters. + P3D_create_instance + (NULL, argv[1], + window_type, win_x, win_y, win_width, win_height, parent_window, + NULL, 0); + +#ifdef _WIN32 + // Wait for new messages from Windows, and new requests from the + // plugin. + MSG msg; + int retval; + retval = GetMessage(&msg, NULL, 0, 0); + while (retval != 0) { + if (retval == -1) { + cerr << "Error processing message queue.\n"; + exit(1); + } + TranslateMessage(&msg); + DispatchMessage(&msg); + + // Check for new requests from the Panda3D plugin. + P3D_instance *inst = P3D_check_request(false); + while (inst != (P3D_instance *)NULL) { + P3D_request *request = P3D_instance_get_request(inst); + if (request != (P3D_request *)NULL) { + handle_request(request); + } + inst = P3D_check_request(false); + } + retval = GetMessage(&msg, NULL, 0, 0); + } + +#else + + // Now wait while we process pending requests. + P3D_instance *inst = P3D_check_request(true); + while (inst != (P3D_instance *)NULL) { + P3D_request *request = P3D_instance_get_request(inst); + if (request != (P3D_request *)NULL) { + handle_request(request); + } + inst = P3D_check_request(true); + } +#endif + + // All instances have finished; we can exit. + + return 0; +} diff --git a/direct/src/plugin/wingetopt.c b/direct/src/plugin/wingetopt.c new file mode 100644 index 0000000000..30b24f0601 --- /dev/null +++ b/direct/src/plugin/wingetopt.c @@ -0,0 +1,75 @@ +/* +POSIX getopt for Windows + +AT&T Public License + +Code given out at the 1985 UNIFORUM conference in Dallas. +*/ + +#ifndef __GNUC__ + +#include "wingetopt.h" +#include + +#define NULL 0 +#define EOF (-1) +#define ERR(s, c) if(opterr){\ + char errbuf[2];\ + errbuf[0] = c; errbuf[1] = '\n';\ + fputs(argv[0], stderr);\ + fputs(s, stderr);\ + fputc(c, stderr);} + //(void) write(2, argv[0], (unsigned)strlen(argv[0]));\ + //(void) write(2, s, (unsigned)strlen(s));\ + //(void) write(2, errbuf, 2);} + +int opterr = 1; +int optind = 1; +int optopt; +char *optarg; + +int +getopt(int argc, char **argv, const char *opts) +{ + static int sp = 1; + register int c; + register char *cp; + + if(sp == 1) + if(optind >= argc || + argv[optind][0] != '-' || argv[optind][1] == '\0') + return(EOF); + else if(strcmp(argv[optind], "--") == NULL) { + optind++; + return(EOF); + } + optopt = c = argv[optind][sp]; + if(c == ':' || (cp=strchr(opts, c)) == NULL) { + ERR(": illegal option -- ", c); + if(argv[optind][++sp] == '\0') { + optind++; + sp = 1; + } + return('?'); + } + if(*++cp == ':') { + if(argv[optind][sp+1] != '\0') + optarg = &argv[optind++][sp+1]; + else if(++optind >= argc) { + ERR(": option requires an argument -- ", c); + sp = 1; + return('?'); + } else + optarg = argv[optind++]; + sp = 1; + } else { + if(argv[optind][++sp] == '\0') { + sp = 1; + optind++; + } + optarg = NULL; + } + return(c); +} + +#endif /* __GNUC__ */ diff --git a/direct/src/plugin/wingetopt.h b/direct/src/plugin/wingetopt.h new file mode 100644 index 0000000000..f48590f30a --- /dev/null +++ b/direct/src/plugin/wingetopt.h @@ -0,0 +1,32 @@ +/* +POSIX getopt for Windows + +AT&T Public License + +Code given out at the 1985 UNIFORUM conference in Dallas. +*/ + +#ifdef __GNUC__ +#include +#endif +#ifndef __GNUC__ + +#ifndef _WINGETOPT_H_ +#define _WINGETOPT_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +extern int opterr; +extern int optind; +extern int optopt; +extern char *optarg; +extern int getopt(int argc, char **argv, const char *opts); + +#ifdef __cplusplus +} +#endif + +#endif /* _GETOPT_H_ */ +#endif /* __GNUC__ */ diff --git a/direct/src/showbase/RunAppMF.py b/direct/src/showbase/RunAppMF.py index e3b70d7ef8..b791d3d379 100644 --- a/direct/src/showbase/RunAppMF.py +++ b/direct/src/showbase/RunAppMF.py @@ -30,29 +30,25 @@ default-model-extension .bam class ArgumentError(AttributeError): pass -def runPackedApp(args): - if not args: - raise ArgumentError, "No Panda app specified. Use:\npython RunAppMF.py app.mf" +__packedAppEnvironmentInitialized = False + +def initPackedAppEnvironment(): + """ This function sets up the Python environment suitably for + running a packed app. It should only run once in any given + session (and it includes logic to ensure this). """ + + global __packedAppEnvironmentInitialized + if __packedAppEnvironmentInitialized: + return + + __packedAppEnvironmentInitialized = True vfs = VirtualFileSystem.getGlobalPtr() - fname = Filename.fromOsSpecific(args[0]) - if not vfs.exists(fname): - raise ArgumentError, "No such file: %s" % (args[0]) - - mf = Multifile() - if not mf.openRead(fname): - raise ArgumentError, "Not a Panda Multifile: %s" % (args[0]) - # Clear *all* the mount points, including "/", so that we no # longer access the disk directly. vfs.unmountAll() - # Mount the Multifile under /mf, by convention, and make that our - # "current directory". - vfs.mount(mf, MultifileRoot, vfs.MFReadOnly) - vfs.chdir(MultifileRoot) - # Make sure the directories on our standard Python path are mounted # read-only, so we can still load Python. for dirname in sys.path: @@ -77,14 +73,6 @@ def runPackedApp(args): # Load the implicit App.prc file. loadPrcFileData(AppPrcFilename, AppPrc) - # Load any prc files in the root. We have to load them - # explicitly, since the ConfigPageManager can't directly look - # inside the vfs. - for f in vfs.scanDirectory(MultifileRoot): - if f.getFilename().getExtension() == 'prc': - data = f.readFile(True) - loadPrcFileData(f.getFilename().cStr(), data) - # Replace the builtin open and file symbols so user code will get # our versions by default, which can open and read files out of # the multifile. @@ -93,10 +81,72 @@ def runPackedApp(args): os.listdir = file.listdir os.walk = file.walk + # Make "/mf" our "current directory", for running the multifiles + # we plan to mount there. + vfs.chdir(MultifileRoot) + +def runPackedApp(args): + if not args: + raise ArgumentError, "No Panda app specified. Use:\npython RunAppMF.py app.mf" + + vfs = VirtualFileSystem.getGlobalPtr() + + fname = Filename.fromOsSpecific(args[0]) + if not vfs.exists(fname): + raise ArgumentError, "No such file: %s" % (args[0]) + + fname.makeAbsolute() + initPackedAppEnvironment() + + mf = Multifile() + if not mf.openRead(fname): + raise ArgumentError, "Not a Panda Multifile: %s" % (args[0]) + + # Mount the Multifile under /mf, by convention. + vfs.mount(mf, MultifileRoot, vfs.MFReadOnly) + + # Load any prc files in the root. We have to load them + # explicitly, since the ConfigPageManager can't directly look + # inside the vfs. Use the Multifile interface to find the prc + # files, rather than vfs.scanDirectory(), so we only pick up the + # files in this particular multifile. + for f in mf.getSubfileNames(): + fn = Filename(f) + if fn.getDirname() == '' and fn.getExtension() == 'prc': + pathname = '%s/%s' % (MultifileRoot, f) + data = open(pathname, 'r').read() + loadPrcFileData(pathname, data) + import main if hasattr(main, 'main') and callable(main.main): main.main() +def setupWindow(windowType, x, y, width, height, parent): + if windowType == 'hidden': + data = 'window-type none\n' + else: + data = 'window-type onscreen\n' + + if windowType == 'fullscreen': + data += 'fullscreen 1\n' + else: + data += 'fullscreen 0\n' + + if windowType == 'embedded': + data += 'parent-window-handle %s\n' % (parent) + else: + data += 'parent-window-handle 0\n' + + if x or y: + data += 'win-origin %s %s\n' % (x, y) + if width or height: + data += 'win-size %s %s\n' % (width, height) + + loadPrcFileData("setupWindow", data) + +def run(): + taskMgr.run() + if __name__ == '__main__': try: runPackedApp(sys.argv[1:]) diff --git a/direct/src/showutil/FreezeTool.py b/direct/src/showutil/FreezeTool.py index 9c230367cc..c2d00b4b5a 100644 --- a/direct/src/showutil/FreezeTool.py +++ b/direct/src/showutil/FreezeTool.py @@ -36,15 +36,25 @@ linkDll = 'error' Python = None # The directory that includes Python.h. -PythonIPath = '/Developer/SDKs/MacOSX10.5.sdk/System/Library/Frameworks/Python.framework/Versions/2.5/include/python2.5' +PythonIPath = None + +# The root directory of Microsoft Visual Studio (if relevant) +MSVS = None if sys.platform == 'win32': - compileObj = "cl /wd4996 /Fo%(basename)s.obj /nologo /c /MD /Zi /O2 /Ob2 /EHsc /Zm300 /W3 %(filename)s" - linkExe = 'link /nologo /MAP:NUL /FIXED:NO /OPT:REF /STACK:4194304 /INCREMENTAL:NO /out:%(basename)s.exe; mt -manifest %(basename)s.manifest -outputresource:%(basename)s.exe;2' - linkDll = 'link /nologo /MAP:NUL /FIXED:NO /OPT:REF /INCREMENTAL:NO /out:%(basename)s.dll; mt -manifest %(basename)s.manifest -outputresource:%(basename)s.dll;1' + wtpython = '$WINTOOLS/sdk/python/Python-2.4.1' + Python = Filename(ExecutionEnvironment.expandString(wtpython)).toOsSpecific() + + MSVS = Filename('/c/Program Files/Microsoft Visual Studio .NET 2003').toOsSpecific() + compileObj = 'cl /wd4996 /Fo%(basename)s.obj /nologo /c /MD /Zi /O2 /Ob2 /EHsc /Zm300 /W3 /I"%(python)s\Include" /I"%(python)s\PC" /I"%(msvs)s\Vc7\PlatformSDK\include" /I"%(msvs)s\Vc7\include" %(filename)s' + linkExe = 'link /nologo /MAP:NUL /FIXED:NO /OPT:REF /STACK:4194304 /INCREMENTAL:NO /out:%(basename)s.exe %(basename)s.obj' + linkDll = 'link /nologo /DLL /MAP:NUL /FIXED:NO /OPT:REF /INCREMENTAL:NO /LIBPATH:"%(msvs)s\Vc7\PlatformSDK\lib" /LIBPATH:"%(msvs)s\Vc7\lib" /LIBPATH:"%(python)s\PCbuild" /out:%(basename)s.pyd %(basename)s.obj' elif sys.platform == 'darwin': # OSX + + PythonIPath = '/Developer/SDKs/MacOSX10.5.sdk/System/Library/Frameworks/Python.framework/Versions/2.5/include/python2.5' + compileObj = "gcc -fPIC -c -o %(basename)s.o -O2 -arch i386 -arch ppc -I %(pythonIPath)s %(filename)s" linkExe = "gcc -o %(basename)s %(basename)s.o -framework Python" linkDll = "gcc -shared -o %(basename)s.so %(basename)s.o -framework Python" @@ -733,7 +743,7 @@ class Freezer: initCode = dllInitCode % { 'dllexport' : dllexport, - 'moduleName' : basename, + 'moduleName' : basename.split('.')[0], 'newcount' : len(moduleList), } doCompile = self.compileDll @@ -757,6 +767,8 @@ class Freezer: def compileExe(self, filename, basename): compile = self.compileObj % { + 'python' : Python, + 'msvs' : MSVS, 'pythonIPath' : PythonIPath, 'filename' : filename, 'basename' : basename, @@ -766,6 +778,8 @@ class Freezer: raise StandardError link = self.linkExe % { + 'python' : Python, + 'msvs' : MSVS, 'filename' : filename, 'basename' : basename, } @@ -775,6 +789,8 @@ class Freezer: def compileDll(self, filename, basename): compile = self.compileObj % { + 'python' : Python, + 'msvs' : MSVS, 'pythonIPath' : PythonIPath, 'filename' : filename, 'basename' : basename, @@ -784,6 +800,8 @@ class Freezer: raise StandardError link = self.linkDll % { + 'python' : Python, + 'msvs' : MSVS, 'filename' : filename, 'basename' : basename, } diff --git a/direct/src/showutil/pfreeze.py b/direct/src/showutil/pfreeze.py index ae97d625ef..4502e4650b 100755 --- a/direct/src/showutil/pfreeze.py +++ b/direct/src/showutil/pfreeze.py @@ -22,7 +22,8 @@ Options: -o output Specifies the name of the resulting executable file to produce. If this ends in ".mf", a multifile is written instead of a frozen - binary. + binary. If it ends in ".dll", ".pyd", or ".so", a shared library + is written. -x module[,module...] Specifies a comma-separated list of Python modules to exclude from @@ -85,10 +86,20 @@ if __name__ == '__main__': if len(args) != 1: usage(1, 'Only one main file may be specified.') - freezer.setMain(args[0]) + outputType = 'exe' + bl = basename.lower() + if bl.endswith('.mf'): + outputType = 'mf' + elif bl.endswith('.dll') or bl.endswith('.pyd') or bl.endswith('.so'): + basename = os.path.splitext(basename)[0] + outputType = 'dll' + + freezer.addModule(args[0]) + if outputType != 'dll': + freezer.setMain(args[0]) freezer.done() - if basename.lower().endswith('.mf'): + if outputType == 'mf': freezer.writeMultifile(basename) else: freezer.generateCode(basename)