wip: web plugin

This commit is contained in:
David Rose 2009-06-05 00:47:03 +00:00
parent c61f5f2b64
commit 6f711392aa
23 changed files with 2780 additions and 32 deletions

View File

@ -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

View File

@ -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;
}

View File

@ -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 <sstream>
////////////////////////////////////////////////////////////////////
// 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);
}
}

View File

@ -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 <vector>
#include <deque>
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<Token> 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<P3D_request *> Requests;
Requests _pending_requests;
friend class P3DSession;
friend class P3DPython;
};
#include "p3dInstance.I"
#endif

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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 <set>
#include <map>
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<P3DInstance *> Instances;
Instances _instances;
typedef map<string, P3DSession *> 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

View File

@ -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;
}

View File

@ -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 <malloc.h>
////////////////////////////////////////////////////////////////////
// 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

View File

@ -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<P3DSession *> 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

View File

@ -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();
}

View File

@ -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 <malloc.h>
////////////////////////////////////////////////////////////////////
// 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);
}
}

View File

@ -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 <set>
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<P3DInstance *> Instances;
Instances _instances;
friend class P3DPython;
};
#include "p3dSession.I"
#endif

View File

@ -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);
}

View File

@ -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 <sys/types.h>
#ifdef _WIN32
#include <windows.h>
#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 */

View File

@ -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 <iostream>
#include <string>
#include <assert.h>
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

View File

@ -0,0 +1,5 @@
#include "p3d_plugin.cxx"
#include "p3dInstance.cxx"
#include "p3dInstanceManager.cxx"
#include "p3dPython.cxx"
#include "p3dSession.cxx"

View File

@ -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 <iostream>
#include <string>
#ifdef _WIN32
#include "wingetopt.h"
#include <windows.h>
#else
#include <getopt.h>
#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;
}

View File

@ -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 <stdio.h>
#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__ */

View File

@ -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 <getopt.h>
#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__ */

View File

@ -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:])

View File

@ -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,
}

View File

@ -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)