507 lines
17 KiB
C++
507 lines
17 KiB
C++
// 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 "p3dDownload.h"
|
|
#include "p3dSession.h"
|
|
#include "p3dPackage.h"
|
|
#include "p3dSplashWindow.h"
|
|
#include "p3dWinSplashWindow.h"
|
|
|
|
#include <sstream>
|
|
#include <algorithm>
|
|
|
|
#ifdef _WIN32
|
|
typedef P3DWinSplashWindow SplashWindowType;
|
|
#else
|
|
typedef P3DSplashWindow SplashWindowType;
|
|
#endif
|
|
|
|
int P3DInstance::_next_instance_id = 0;
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: P3DInstance::Constructor
|
|
// Access: Public
|
|
// Description:
|
|
////////////////////////////////////////////////////////////////////
|
|
P3DInstance::
|
|
P3DInstance(P3D_request_ready_func *func, void *user_data) :
|
|
_func(func)
|
|
{
|
|
_user_data = user_data;
|
|
_request_pending = false;
|
|
_got_fparams = false;
|
|
_got_wparams = false;
|
|
|
|
_instance_id = _next_instance_id;
|
|
++_next_instance_id;
|
|
|
|
INIT_LOCK(_request_lock);
|
|
|
|
_session = NULL;
|
|
_splash_window = NULL;
|
|
_instance_window_opened = false;
|
|
_requested_stop = false;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: P3DInstance::Destructor
|
|
// Access: Public
|
|
// Description:
|
|
////////////////////////////////////////////////////////////////////
|
|
P3DInstance::
|
|
~P3DInstance() {
|
|
assert(_session == NULL);
|
|
|
|
DESTROY_LOCK(_request_lock);
|
|
|
|
// Tell all of the packages that we're no longer in business for
|
|
// them.
|
|
Packages::iterator pi;
|
|
for (pi = _packages.begin(); pi != _packages.end(); ++pi) {
|
|
(*pi)->cancel_instance(this);
|
|
}
|
|
_packages.clear();
|
|
|
|
if (_splash_window != NULL) {
|
|
nout << "Deleting splash window in destructor\n" << flush;
|
|
delete _splash_window;
|
|
_splash_window = NULL;
|
|
}
|
|
|
|
// TODO: empty _pending_requests queue and _downloads map.
|
|
|
|
// TODO: Is it possible for someone to delete an instance while a
|
|
// download is still running? Who will crash when this happens?
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: P3DInstance::set_fparams
|
|
// Access: Public
|
|
// Description: Sets up the initial file parameters for the instance.
|
|
// Normally this is only called once.
|
|
////////////////////////////////////////////////////////////////////
|
|
void P3DInstance::
|
|
set_fparams(const P3DFileParams &fparams) {
|
|
_got_fparams = true;
|
|
_fparams = fparams;
|
|
|
|
// This also sets up some internal data based on the contents of the
|
|
// above file and the associated tokens.
|
|
|
|
// For the moment, all sessions will be unique.
|
|
P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
|
|
ostringstream strm;
|
|
strm << inst_mgr->get_unique_session_index();
|
|
_session_key = strm.str();
|
|
|
|
_python_version = "python24";
|
|
|
|
|
|
// Maybe create the splash window.
|
|
if (!_instance_window_opened && _got_wparams) {
|
|
if (_splash_window == NULL) {
|
|
make_splash_window();
|
|
}
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: P3DInstance::set_wparams
|
|
// Access: Public
|
|
// Description: Changes the window parameters, e.g. to resize or
|
|
// reposition the window; or sets the parameters for the
|
|
// first time, creating the initial window.
|
|
////////////////////////////////////////////////////////////////////
|
|
void P3DInstance::
|
|
set_wparams(const P3DWindowParams &wparams) {
|
|
_got_wparams = true;
|
|
_wparams = wparams;
|
|
|
|
// Update or create the splash window.
|
|
if (!_instance_window_opened && _got_fparams) {
|
|
if (_splash_window == NULL) {
|
|
make_splash_window();
|
|
} else {
|
|
_splash_window->set_wparams(_wparams);
|
|
}
|
|
}
|
|
|
|
// Update the instance in the sub-process.
|
|
if (_session != NULL) {
|
|
TiXmlDocument *doc = new TiXmlDocument;
|
|
TiXmlDeclaration *decl = new TiXmlDeclaration("1.0", "", "");
|
|
TiXmlElement *xcommand = new TiXmlElement("command");
|
|
xcommand->SetAttribute("cmd", "setup_window");
|
|
xcommand->SetAttribute("id", get_instance_id());
|
|
TiXmlElement *xwparams = _wparams.make_xml();
|
|
|
|
doc->LinkEndChild(decl);
|
|
doc->LinkEndChild(xcommand);
|
|
xcommand->LinkEndChild(xwparams);
|
|
|
|
_session->send_command(doc);
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// 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();
|
|
_request_pending = !_pending_requests.empty();
|
|
}
|
|
RELEASE_LOCK(_request_lock);
|
|
|
|
if (result != NULL) {
|
|
if (result->_request_type == P3D_RT_notify) {
|
|
// If we received a notify request, process the notification
|
|
// immediately--it might be interesting to this instance.
|
|
const char *message = result->_request._notify._message;
|
|
if (strcmp(message, "window_opened") == 0) {
|
|
// The process told us that it just succesfully opened its
|
|
// window.
|
|
nout << "Instance " << this << " got window_opened\n" << flush;
|
|
_instance_window_opened = true;
|
|
if (_splash_window != NULL) {
|
|
nout << "Deleting splash window\n" << flush;
|
|
delete _splash_window;
|
|
_splash_window = NULL;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
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) {
|
|
nout << "Instance " << this << " add_request(" << request->_request_type
|
|
<< ")\n" << flush;
|
|
request->_instance = this;
|
|
|
|
ACQUIRE_LOCK(_request_lock);
|
|
_pending_requests.push_back(request);
|
|
_request_pending = true;
|
|
RELEASE_LOCK(_request_lock);
|
|
|
|
// Asynchronous notification for anyone who cares.
|
|
if (_func != NULL) {
|
|
_func(this);
|
|
}
|
|
|
|
// Synchronous notification for pollers.
|
|
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);
|
|
|
|
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.
|
|
////////////////////////////////////////////////////////////////////
|
|
bool 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) {
|
|
Downloads::iterator di = _downloads.find(unique_id);
|
|
if (di == _downloads.end()) {
|
|
nout << "Unexpected feed_url_stream for " << unique_id << "\n"
|
|
<< flush;
|
|
// Don't know this request.
|
|
return false;
|
|
}
|
|
|
|
P3DDownload *download = (*di).second;
|
|
bool download_ok = download->feed_url_stream
|
|
(result_code, http_status_code, total_expected_data,
|
|
this_data, this_data_size);
|
|
|
|
if (!download_ok || download->get_download_finished()) {
|
|
// All done.
|
|
nout << "completed download " << unique_id << "\n" << flush;
|
|
_downloads.erase(di);
|
|
delete download;
|
|
}
|
|
|
|
return download_ok;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: P3DInstance::add_package
|
|
// Access: Public
|
|
// Description: Adds the package to the list of packages used by this
|
|
// instance. The instance will share responsibility for
|
|
// downloading the package will any of the other
|
|
// instances that use the same package.
|
|
////////////////////////////////////////////////////////////////////
|
|
void P3DInstance::
|
|
add_package(P3DPackage *package) {
|
|
assert(find(_packages.begin(), _packages.end(), package) == _packages.end());
|
|
|
|
_packages.push_back(package);
|
|
package->set_instance(this);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: P3DInstance::start_download
|
|
// Access: Public
|
|
// Description: Adds a newly-allocated P3DDownload object to the
|
|
// download queue, and issues the request to start it
|
|
// downloading. As the download data comes in, it will
|
|
// be fed to the download object. After
|
|
// download_finished() has been called, the P3DDownload
|
|
// object will be deleted.
|
|
////////////////////////////////////////////////////////////////////
|
|
void P3DInstance::
|
|
start_download(P3DDownload *download) {
|
|
assert(download->get_download_id() == 0);
|
|
|
|
int download_id = _next_instance_id;
|
|
++_next_instance_id;
|
|
download->set_download_id(download_id);
|
|
|
|
bool inserted = _downloads.insert(Downloads::value_type(download_id, download)).second;
|
|
assert(inserted);
|
|
|
|
nout << "beginning download " << download_id << ": " << download->get_url()
|
|
<< "\n" << flush;
|
|
|
|
P3D_request *request = new P3D_request;
|
|
request->_request_type = P3D_RT_get_url;
|
|
request->_request._get_url._url = strdup(download->get_url().c_str());
|
|
request->_request._get_url._unique_id = download_id;
|
|
|
|
add_request(request);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: P3DInstance::request_stop
|
|
// Access: Public
|
|
// Description: Asks the host to shut down this particular instance,
|
|
// presumably because the user has indicated it should
|
|
// exit.
|
|
////////////////////////////////////////////////////////////////////
|
|
void P3DInstance::
|
|
request_stop() {
|
|
if (!_requested_stop) {
|
|
_requested_stop = true;
|
|
P3D_request *request = new P3D_request;
|
|
request->_request_type = P3D_RT_stop;
|
|
add_request(request);
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: P3DInstance::make_xml
|
|
// Access: Public
|
|
// Description: Returns a newly-allocated XML structure that
|
|
// corresponds to the data within this instance.
|
|
////////////////////////////////////////////////////////////////////
|
|
TiXmlElement *P3DInstance::
|
|
make_xml() {
|
|
assert(_got_fparams);
|
|
|
|
TiXmlElement *xinstance = new TiXmlElement("instance");
|
|
xinstance->SetAttribute("id", _instance_id);
|
|
|
|
TiXmlElement *xfparams = _fparams.make_xml();
|
|
xinstance->LinkEndChild(xfparams);
|
|
|
|
if (_got_wparams) {
|
|
TiXmlElement *xwparams = _wparams.make_xml();
|
|
xinstance->LinkEndChild(xwparams);
|
|
}
|
|
|
|
return xinstance;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: P3DInstance::make_splash_window
|
|
// Access: Private
|
|
// Description: Creates the splash window to be displayed at startup.
|
|
// This method is called as soon as we have received
|
|
// both _fparams and _wparams.
|
|
////////////////////////////////////////////////////////////////////
|
|
void P3DInstance::
|
|
make_splash_window() {
|
|
assert(_splash_window == NULL);
|
|
|
|
_splash_window = new SplashWindowType(this);
|
|
|
|
string splash_image_url = _fparams.lookup_token("splash_img");
|
|
if (!_fparams.has_token("splash_img")) {
|
|
// No specific splash image is specified; get the default splash
|
|
// image.
|
|
P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
|
|
splash_image_url = inst_mgr->get_download_url();
|
|
splash_image_url += "coreapi/splash.jpg";
|
|
}
|
|
|
|
if (splash_image_url.empty()) {
|
|
// No splash image. Never mind.
|
|
return;
|
|
}
|
|
|
|
// Make a temporary file to receive the splash image.
|
|
char *name = tempnam(NULL, "p3d_");
|
|
string filename = name;
|
|
free(name);
|
|
|
|
nout << "Downloading splash image into " << filename << "\n";
|
|
|
|
// Start downloading the requested splash image.
|
|
SplashDownload *download = new SplashDownload(this);
|
|
download->set_url(splash_image_url);
|
|
download->set_filename(filename);
|
|
|
|
start_download(download);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: P3DInstance::install_progress
|
|
// Access: Private
|
|
// Description: Notified as the _panda3d package is downloaded.
|
|
////////////////////////////////////////////////////////////////////
|
|
void P3DInstance::
|
|
install_progress(P3DPackage *package, double progress) {
|
|
if (_splash_window != NULL) {
|
|
_splash_window->set_install_label("Installing Panda3D");
|
|
_splash_window->set_install_progress(progress);
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: P3DInstance::SplashDownload::Constructor
|
|
// Access: Public
|
|
// Description:
|
|
////////////////////////////////////////////////////////////////////
|
|
P3DInstance::SplashDownload::
|
|
SplashDownload(P3DInstance *inst) :
|
|
_inst(inst)
|
|
{
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: P3DInstance::SplashDownload::download_finished
|
|
// Access: Protected, Virtual
|
|
// Description: Intended to be overloaded to generate a callback
|
|
// when the download finishes, either successfully or
|
|
// otherwise. The bool parameter is true if the
|
|
// download was successful.
|
|
////////////////////////////////////////////////////////////////////
|
|
void P3DInstance::SplashDownload::
|
|
download_finished(bool success) {
|
|
P3DFileDownload::download_finished(success);
|
|
if (success) {
|
|
// We've successfully downloaded the splash image. Put it
|
|
// onscreen if our splash window still exists.
|
|
if (_inst->_splash_window != NULL) {
|
|
_inst->_splash_window->set_image_filename(get_filename(), true);
|
|
}
|
|
}
|
|
}
|