浏览代码

wip: web plugin

David Rose 16 年之前
父节点
当前提交
6f711392aa

+ 40 - 0
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

+ 52 - 0
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;
+}

+ 214 - 0
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 <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);
+  }
+}

+ 92 - 0
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 <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

+ 36 - 0
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;
+}

+ 255 - 0
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;
+}

+ 90 - 0
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 <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

+ 49 - 0
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;
+}

+ 372 - 0
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 <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

+ 125 - 0
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<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

+ 48 - 0
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();
+}

+ 93 - 0
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 <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);
+  }
+}

+ 58 - 0
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 <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

+ 166 - 0
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);
+}
+

+ 458 - 0
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 <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 */
+
+

+ 43 - 0
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 <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
+

+ 5 - 0
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"

+ 366 - 0
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 <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;
+}

+ 75 - 0
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 <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__ */

+ 32 - 0
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 <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__ */

+ 73 - 23
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
 
-    vfs = VirtualFileSystem.getGlobalPtr()
+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). """
 
-    fname = Filename.fromOsSpecific(args[0])
-    if not vfs.exists(fname):
-        raise ArgumentError, "No such file: %s" % (args[0])
+    global __packedAppEnvironmentInitialized
+    if __packedAppEnvironmentInitialized:
+        return
 
-    mf = Multifile()
-    if not mf.openRead(fname):
-        raise ArgumentError, "Not a Panda Multifile: %s" % (args[0])
+    __packedAppEnvironmentInitialized = True
+
+    vfs = VirtualFileSystem.getGlobalPtr()
 
     # 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:])

+ 23 - 5
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,
             }

+ 14 - 3
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)