panda3d/direct/src/plugin/p3dInstance.cxx
2009-09-03 02:09:21 +00:00

1750 lines
59 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 "p3dOsxSplashWindow.h"
#include "p3dX11SplashWindow.h"
#include "p3dObject.h"
#include "p3dMainObject.h"
#include "p3dUndefinedObject.h"
#include "p3dMultifileReader.h"
#include "p3dTemporaryFile.h"
#include <sstream>
#include <algorithm>
#ifdef __APPLE__
#include <sys/mman.h>
#include <ApplicationServices/ApplicationServices.h>
#endif // __APPLE__
#ifdef _WIN32
typedef P3DWinSplashWindow SplashWindowType;
#elif defined(__APPLE__)
typedef P3DOsxSplashWindow SplashWindowType;
#elif defined(HAVE_X11)
typedef P3DX11SplashWindow SplashWindowType;
#else
typedef P3DSplashWindow SplashWindowType;
#endif
////////////////////////////////////////////////////////////////////
// Function: P3DInstance::Constructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
P3DInstance::
P3DInstance(P3D_request_ready_func *func,
const P3D_token tokens[], size_t num_tokens,
int argc, const char *argv[], void *user_data) :
_func(func)
{
_browser_script_object = NULL;
_panda_script_object = new P3DMainObject;
_panda_script_object->set_instance(this);
_user_data = user_data;
_request_pending = false;
_temp_p3d_filename = NULL;
_splash_package = NULL;
_temp_splash_image = NULL;
_got_fparams = false;
_got_wparams = false;
_fparams.set_tokens(tokens, num_tokens);
_fparams.set_args(argc, argv);
P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
_instance_id = inst_mgr->get_unique_id();
_hidden = false;
_allow_python_dev = false;
_session = NULL;
_panda3d = NULL;
_splash_window = NULL;
_instance_window_opened = false;
_stuff_to_download = false;
INIT_LOCK(_request_lock);
_requested_stop = false;
#ifdef __APPLE__
_shared_fd = -1;
_shared_mmap_size = 0;
_swbuffer = NULL;
_reversed_buffer = NULL;
_mouse_active = true;
_frame_timer = NULL;
#endif // __APPLE__
// Set some initial properties.
_panda_script_object->set_float_property("instanceDownloadProgress", 0.0);
_panda_script_object->set_float_property("downloadProgress", 0.0);
_panda_script_object->set_string_property("downloadPackageName", "");
_panda_script_object->set_string_property("downloadPackageDisplayName", "");
_panda_script_object->set_bool_property("downloadComplete", false);
_panda_script_object->set_string_property("status", "initial");
}
////////////////////////////////////////////////////////////////////
// Function: P3DInstance::Destructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
P3DInstance::
~P3DInstance() {
assert(_session == NULL);
P3D_OBJECT_XDECREF(_browser_script_object);
nout << "panda_script_object ref = "
<< _panda_script_object->_ref_count << "\n";
_panda_script_object->set_instance(NULL);
P3D_OBJECT_DECREF(_panda_script_object);
// 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)->remove_instance(this);
}
_packages.clear();
if (_splash_package != NULL) {
_splash_package->remove_instance(this);
_splash_package = NULL;
}
if (_splash_window != NULL) {
delete _splash_window;
_splash_window = NULL;
}
if (_temp_p3d_filename != NULL) {
delete _temp_p3d_filename;
_temp_p3d_filename = NULL;
}
if (_temp_splash_image != NULL) {
delete _temp_splash_image;
_temp_splash_image = NULL;
}
#ifdef __APPLE__
if (_frame_timer != NULL) {
CFRunLoopTimerInvalidate(_frame_timer);
CFRelease(_frame_timer);
}
if (_swbuffer != NULL) {
SubprocessWindowBuffer::destroy_buffer(_shared_fd, _shared_mmap_size,
_shared_filename, _swbuffer);
_swbuffer = NULL;
}
if (_reversed_buffer != NULL) {
delete[] _reversed_buffer;
_reversed_buffer = NULL;
}
#endif
DESTROY_LOCK(_request_lock);
// TODO: empty _raw_requests and _baked_requests queues, 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_p3d_url
// Access: Public
// Description: Specifies a URL that should be contacted to download
// the instance data. Normally this, or
// set_p3d_filename(), is only called once.
//
// The instance data at the other end of this URL is
// key. We can't start the instance until we have
// downloaded the instance file and examined the
// p3d_info.xml, and we know what Python version we need
// and so forth.
////////////////////////////////////////////////////////////////////
void P3DInstance::
set_p3d_url(const string &p3d_url) {
// Make a temporary file to receive the instance data.
assert(_temp_p3d_filename == NULL);
_temp_p3d_filename = new P3DTemporaryFile(".p3d");
_stuff_to_download = true;
// Maybe it's time to open a splash window now.
make_splash_window();
// Mark the time we started downloading, so we'll know when to set
// the install label.
#ifdef _WIN32
_start_dl_instance_tick = GetTickCount();
#else
gettimeofday(&_start_dl_instance_timeval, NULL);
#endif
_show_dl_instance_progress = false;
// Start downloading the data.
InstanceDownload *download = new InstanceDownload(this);
download->set_url(p3d_url);
download->set_filename(_temp_p3d_filename->get_filename());
_panda_script_object->set_string_property("status", "downloading_instance");
start_download(download);
}
////////////////////////////////////////////////////////////////////
// Function: P3DInstance::set_p3d_filename
// Access: Public
// Description: Specifies the file that contains the instance data.
// Normally this is only called once.
////////////////////////////////////////////////////////////////////
void P3DInstance::
set_p3d_filename(const string &p3d_filename) {
_got_fparams = true;
_fparams.set_p3d_filename(p3d_filename);
_panda_script_object->set_float_property("instanceDownloadProgress", 1.0);
// This also sets up some internal data based on the contents of the
// above file and the associated tokens.
// Extract the application desc file from the p3d file.
P3DMultifileReader reader;
stringstream sstream;
if (!reader.extract_one(p3d_filename, sstream, "p3d_info.xml")) {
nout << "No p3d_info.xml file found in " << p3d_filename << "\n";
} else {
sstream.seekg(0);
TiXmlDocument doc;
sstream >> doc;
scan_app_desc_file(&doc);
}
// For the moment, all sessions will be unique. TODO: support
// multiple instances per session.
P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
ostringstream strm;
strm << inst_mgr->get_unique_id();
_session_key = strm.str();
// Generate a special notification: onpluginload, indicating the
// plugin has read its parameters and is ready to be queried (even
// if Python has not yet started).
send_notify("onpluginload");
// Now we're ready to start.
inst_mgr->start_instance(this);
}
////////////////////////////////////////////////////////////////////
// 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;
if (_hidden || _wparams.get_win_width() == 0 || _wparams.get_win_height() == 0) {
// If we're a hidden app, or if the window has no size, then it is
// really a hidden window, regardless of what type it claims to
// be.
_wparams.set_window_type(P3D_WT_hidden);
}
if (_wparams.get_window_type() != P3D_WT_hidden) {
// Update or create the splash window.
if (_splash_window != NULL) {
_splash_window->set_wparams(_wparams);
} else {
make_splash_window();
}
#ifdef __APPLE__
// On Mac, we have to communicate the results of the rendering
// back via shared memory, instead of directly parenting windows
// to the browser. Set up this mechanism.
int x_size = _wparams.get_win_width();
int y_size = _wparams.get_win_height();
if (x_size != 0 && y_size != 0) {
if (_swbuffer == NULL || _swbuffer->get_x_size() != x_size ||
_swbuffer->get_y_size() != y_size) {
// We need to open a new shared buffer.
if (_swbuffer != NULL) {
SubprocessWindowBuffer::destroy_buffer(_shared_fd, _shared_mmap_size,
_shared_filename, _swbuffer);
_swbuffer = NULL;
}
if (_reversed_buffer != NULL) {
delete[] _reversed_buffer;
_reversed_buffer = NULL;
}
_swbuffer = SubprocessWindowBuffer::new_buffer
(_shared_fd, _shared_mmap_size, _shared_filename, x_size, y_size);
if (_swbuffer != NULL) {
_reversed_buffer = new char[_swbuffer->get_framebuffer_size()];
}
}
if (_swbuffer == NULL) {
nout << "Could not open swbuffer\n";
}
}
#endif // __APPLE__
}
// Update the instance in the sub-process.
if (_session != NULL) {
TiXmlDocument *doc = new TiXmlDocument;
TiXmlElement *xcommand = new TiXmlElement("command");
xcommand->SetAttribute("cmd", "setup_window");
xcommand->SetAttribute("instance_id", get_instance_id());
TiXmlElement *xwparams = _wparams.make_xml(this);
doc->LinkEndChild(xcommand);
xcommand->LinkEndChild(xwparams);
_session->send_command(doc);
}
}
////////////////////////////////////////////////////////////////////
// Function: P3DInstance::get_panda_script_object
// Access: Public
// Description: Returns a pointer to the top-level scriptable object
// of the instance, to be used by JavaScript code in the
// browser to control this program.
////////////////////////////////////////////////////////////////////
P3D_object *P3DInstance::
get_panda_script_object() const {
return _panda_script_object;
}
////////////////////////////////////////////////////////////////////
// Function: P3DInstance::set_browser_script_object
// Access: Public
// Description: Stores a pointer to the top-level window object
// of the browser, to be used by Panda code to control
// JavaScript. The new object's reference count is
// incremented, and the previous object's is
// decremented.
////////////////////////////////////////////////////////////////////
void P3DInstance::
set_browser_script_object(P3D_object *browser_script_object) {
if (browser_script_object != _browser_script_object) {
P3D_OBJECT_XDECREF(_browser_script_object);
_browser_script_object = browser_script_object;
if (_browser_script_object != NULL) {
P3D_OBJECT_INCREF(_browser_script_object);
}
if (_session != NULL) {
send_browser_script_object();
}
}
}
////////////////////////////////////////////////////////////////////
// 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() {
return _request_pending;
}
////////////////////////////////////////////////////////////////////
// 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() {
bake_requests();
if (_baked_requests.empty()) {
// No requests ready.
_request_pending = false;
return NULL;
}
P3D_request *request = _baked_requests.front();
_baked_requests.pop_front();
_request_pending = !_baked_requests.empty();
if (request != NULL) {
if (request->_request_type == P3D_RT_notify) {
// Also eval the associated HTML token, if any.
string message = request->_request._notify._message;
string expression = _fparams.lookup_token(message);
if (!expression.empty() && _browser_script_object != NULL) {
P3D_object *result = P3D_OBJECT_EVAL(_browser_script_object, expression.c_str());
P3D_OBJECT_XDECREF(result);
}
} else if (request->_request_type == P3D_RT_stop) {
// We also send an implicit message when Python requests itself
// to shutdown.
_panda_script_object->set_pyobj(NULL);
_panda_script_object->set_string_property("status", "stopped");
string expression = _fparams.lookup_token("onpythonstop");
if (!expression.empty() && _browser_script_object != NULL) {
P3D_object *result = P3D_OBJECT_EVAL(_browser_script_object, expression.c_str());
P3D_OBJECT_XDECREF(result);
}
}
}
return request;
}
////////////////////////////////////////////////////////////////////
// Function: P3DInstance::bake_requests
// Access: Public
// Description: Copies requests from the _raw_requests queue, which
// is built up in one or more sub-threads, into the
// _baked_requests queue, which is publicly presented
// to the browser. Along the way, some requests (like
// script requests) are handled immediately.
//
// At the end of this call, _baked_requests will contain
// the current set of requests pending for the browser.
//
// This method should only be called in the main thread.
////////////////////////////////////////////////////////////////////
void P3DInstance::
bake_requests() {
while (true) {
// Get the latest request from the read thread.
TiXmlDocument *doc = NULL;
ACQUIRE_LOCK(_request_lock);
if (!_raw_requests.empty()) {
doc = _raw_requests.front();
_raw_requests.pop_front();
}
RELEASE_LOCK(_request_lock);
if (doc == NULL) {
// No more requests to process right now.
return;
}
// Now we've got a request in XML form; convert it to P3D_request
// form.
TiXmlElement *xrequest = doc->FirstChildElement("request");
assert(xrequest != (TiXmlElement *)NULL);
P3D_request *request = make_p3d_request(xrequest);
delete doc;
if (request != NULL) {
_baked_requests.push_back(request);
}
}
}
////////////////////////////////////////////////////////////////////
// Function: P3DInstance::add_raw_request
// Access: Public
// Description: May be called in any thread to add a new XML request
// to the pending_request queue for this instance. The
// XML document will be deleted when the request is
// eventually handled.
////////////////////////////////////////////////////////////////////
void P3DInstance::
add_raw_request(TiXmlDocument *doc) {
ACQUIRE_LOCK(_request_lock);
_raw_requests.push_back(doc);
_request_pending = true;
RELEASE_LOCK(_request_lock);
// We don't decode the XML yet, since we might be running in any
// thread here. We'll decode it in the main thread, where it's
// safe.
// Tell the world we've got a new request.
P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
inst_mgr->signal_request_ready(this);
_session->signal_request_ready(this);
}
////////////////////////////////////////////////////////////////////
// Function: P3DInstance::add_baked_request
// Access: Public
// Description: May be called in the main thread only to add a new
// request to the baked_request queue. This request
// queue is directly passed on the browser without
// further processing at this level.
////////////////////////////////////////////////////////////////////
void P3DInstance::
add_baked_request(P3D_request *request) {
request->_instance = this;
_baked_requests.push_back(request);
_request_pending = true;
// Tell the world we've got a new request.
P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
inst_mgr->signal_request_ready(this);
}
////////////////////////////////////////////////////////////////////
// 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);
switch (request->_request_type) {
case P3D_RT_stop:
break;
case P3D_RT_get_url:
free((char *)request->_request._get_url._url);
break;
case P3D_RT_notify:
free((char *)request->_request._notify._message);
break;
}
delete request;
}
////////////////////////////////////////////////////////////////////
// Function: P3DInstance::feed_url_stream
// Access: Public
// Description: Called by the host in response to a get_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";
// 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.
_downloads.erase(di);
delete download;
}
return download_ok;
}
////////////////////////////////////////////////////////////////////
// Function: P3DInstance::handle_event
// Access: Public
// Description: Responds to the os-generated window event. Returns
// true if the event is handled, false if ignored.
////////////////////////////////////////////////////////////////////
bool P3DInstance::
handle_event(P3D_event_data event) {
bool retval = false;
if (_splash_window != NULL) {
if (_splash_window->handle_event(event)) {
retval = true;
}
}
#ifdef _WIN32
// This function is not used in Win32 and does nothing.
#elif defined(__APPLE__)
EventRecord *er = event._event;
// Need to ensure we have the correct port set, in order to
// convert the mouse coordinates successfully via
// GlobalToLocal().
GrafPtr out_port = _wparams.get_parent_window()._port;
GrafPtr portSave = NULL;
Boolean portChanged = QDSwapPort(out_port, &portSave);
Point pt = er->where;
GlobalToLocal(&pt);
if (portChanged) {
QDSwapPort(portSave, NULL);
}
SubprocessWindowBuffer::Event swb_event;
swb_event._source = SubprocessWindowBuffer::ES_none;
swb_event._type = SubprocessWindowBuffer::ET_none;
swb_event._code = 0;
swb_event._flags = 0;
add_modifier_flags(swb_event._flags, er->modifiers);
switch (er->what) {
case mouseDown:
case mouseUp:
{
P3D_window_handle window = _wparams.get_parent_window();
swb_event._source = SubprocessWindowBuffer::ES_mouse;
if (er->what == mouseUp) {
swb_event._type = SubprocessWindowBuffer::ET_button_up;
} else {
swb_event._type = SubprocessWindowBuffer::ET_button_down;
}
retval = true;
}
break;
case keyDown:
case keyUp:
case autoKey:
if (_swbuffer != NULL) {
swb_event._source = SubprocessWindowBuffer::ES_keyboard;
swb_event._code = er->message;
if (er->what == keyUp) {
swb_event._type = SubprocessWindowBuffer::ET_button_up;
} else if (er->what == keyDown) {
swb_event._type = SubprocessWindowBuffer::ET_button_down;
} else {
swb_event._type = SubprocessWindowBuffer::ET_button_again;
}
retval = true;
}
break;
case updateEvt:
paint_window();
retval = true;
break;
case activateEvt:
_mouse_active = ((er->modifiers & 1) != 0);
break;
default:
break;
}
if (_mouse_active) {
swb_event._x = pt.h;
swb_event._y = pt.v;
swb_event._flags |= SubprocessWindowBuffer::EF_mouse_position | SubprocessWindowBuffer::EF_has_mouse;
}
if (_swbuffer != NULL) {
_swbuffer->add_event(swb_event);
}
#endif
return retval;
}
////////////////////////////////////////////////////////////////////
// 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 with any of the other
// instances that use the same package.
////////////////////////////////////////////////////////////////////
void P3DInstance::
add_package(P3DPackage *package) {
if (find(_packages.begin(), _packages.end(), package) != _packages.end()) {
// Already have this package.
return;
}
_packages.push_back(package);
package->add_instance(this);
if (package->get_package_name() == "panda3d") {
_panda3d = package;
}
}
////////////////////////////////////////////////////////////////////
// Function: P3DInstance::get_packages_info_ready
// Access: Public
// Description: Returns true if all of the packages required by the
// instance have their information available and are
// ready to be downloaded, false if one or more of them
// is still waiting for information (or has failed).
////////////////////////////////////////////////////////////////////
bool P3DInstance::
get_packages_info_ready() const {
Packages::const_iterator pi;
for (pi = _packages.begin(); pi != _packages.end(); ++pi) {
if (!(*pi)->get_info_ready()) {
return false;
}
}
return true;
}
////////////////////////////////////////////////////////////////////
// Function: P3DInstance::get_packages_ready
// Access: Public
// Description: Returns true if all of the packages required by the
// instance (as specified in previous calls to
// add_package()) have been fully downloaded and are
// ready to run, or false if one or more of them still
// requires downloading.
////////////////////////////////////////////////////////////////////
bool P3DInstance::
get_packages_ready() const {
Packages::const_iterator pi;
for (pi = _packages.begin(); pi != _packages.end(); ++pi) {
if (!(*pi)->get_ready()) {
return false;
}
}
return true;
}
////////////////////////////////////////////////////////////////////
// Function: P3DInstance::get_packages_failed
// Access: Public
// Description: Returns true if any of the packages required by the
// instance have failed to download (and thus we will
// never be ready).
////////////////////////////////////////////////////////////////////
bool P3DInstance::
get_packages_failed() const {
Packages::const_iterator pi;
for (pi = _packages.begin(); pi != _packages.end(); ++pi) {
if ((*pi)->get_failed()) {
return true;
}
}
return false;
}
////////////////////////////////////////////////////////////////////
// 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);
P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
// Since we're downloading something, we might as well check all
// contents files from this point on.
inst_mgr->reset_verify_contents();
int download_id = inst_mgr->get_unique_id();
download->set_download_id(download_id);
bool inserted = _downloads.insert(Downloads::value_type(download_id, download)).second;
assert(inserted);
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_baked_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_baked_request(request);
}
}
////////////////////////////////////////////////////////////////////
// Function: P3DInstance::request_refresh
// Access: Public
// Description: Asks the host to refresh the plugin window. This is
// only relevant for windowless plugins, for instance,
// the way OSX plugins always run.
////////////////////////////////////////////////////////////////////
void P3DInstance::
request_refresh() {
P3D_request *request = new P3D_request;
request->_request_type = P3D_RT_refresh;
add_baked_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("instance_id", _instance_id);
P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
xinstance->SetAttribute("root_dir", inst_mgr->get_root_dir());
TiXmlElement *xfparams = _fparams.make_xml();
xinstance->LinkEndChild(xfparams);
if (_got_wparams) {
TiXmlElement *xwparams = _wparams.make_xml(this);
xinstance->LinkEndChild(xwparams);
}
Packages::const_iterator pi;
for (pi = _packages.begin(); pi != _packages.end(); ++pi) {
TiXmlElement *xpackage = (*pi)->make_xml();
xinstance->LinkEndChild(xpackage);
}
return xinstance;
}
////////////////////////////////////////////////////////////////////
// Function: P3DInstance::scan_app_desc_file
// Access: Private
// Description: Reads the p3d_info.xml file at instance startup, to
// determine the set of required packages and so forth.
////////////////////////////////////////////////////////////////////
void P3DInstance::
scan_app_desc_file(TiXmlDocument *doc) {
P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
TiXmlElement *xpackage = doc->FirstChildElement("package");
if (xpackage == NULL) {
return;
}
const char *log_basename = xpackage->Attribute("log_basename");
if (log_basename != NULL) {
_log_basename = log_basename;
}
TiXmlElement *xconfig = xpackage->FirstChildElement("config");
if (xconfig != NULL) {
int hidden = 0;
if (xconfig->QueryIntAttribute("hidden", &hidden) == TIXML_SUCCESS) {
_hidden = (hidden != 0);
}
int allow_python_dev = 0;
if (xconfig->QueryIntAttribute("allow_python_dev", &allow_python_dev) == TIXML_SUCCESS) {
_allow_python_dev = (allow_python_dev != 0);
}
}
if (_hidden && _got_wparams) {
_wparams.set_window_type(P3D_WT_hidden);
}
TiXmlElement *xrequires = xpackage->FirstChildElement("requires");
while (xrequires != NULL) {
const char *name = xrequires->Attribute("name");
const char *host_url = xrequires->Attribute("host");
if (name != NULL && host_url != NULL) {
const char *version = xrequires->Attribute("version");
if (version == NULL) {
version = "";
}
P3DHost *host = inst_mgr->get_host(host_url);
P3DPackage *package = host->get_package(name, version);
add_package(package);
}
xrequires = xrequires->NextSiblingElement("requires");
}
}
////////////////////////////////////////////////////////////////////
// Function: P3DInstance::send_browser_script_object
// Access: Private
// Description: Sends the XML sequence to inform the session of our
// browser's toplevel window object.
////////////////////////////////////////////////////////////////////
void P3DInstance::
send_browser_script_object() {
TiXmlDocument *doc = new TiXmlDocument;
TiXmlElement *xcommand = new TiXmlElement("command");
xcommand->SetAttribute("cmd", "pyobj");
xcommand->SetAttribute("op", "set_browser_script_object");
if (_browser_script_object != NULL) {
xcommand->LinkEndChild(_session->p3dobj_to_xml(_browser_script_object));
}
doc->LinkEndChild(xcommand);
_session->send_command(doc);
}
////////////////////////////////////////////////////////////////////
// Function: P3DInstance::make_p3d_request
// Access: Private
// Description: Creates a new P3D_request structure from the XML.
// Returns NULL if no request is needed.
////////////////////////////////////////////////////////////////////
P3D_request *P3DInstance::
make_p3d_request(TiXmlElement *xrequest) {
P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
P3D_request *request = NULL;
const char *rtype = xrequest->Attribute("rtype");
if (rtype != NULL) {
if (strcmp(rtype, "notify") == 0) {
const char *message = xrequest->Attribute("message");
if (message != NULL) {
// A notify message from Python code.
request = new P3D_request;
request->_request_type = P3D_RT_notify;
request->_request._notify._message = strdup(message);
handle_notify_request(message);
}
} else if (strcmp(rtype, "script") == 0) {
// We don't actually build a P3D_request for a script request;
// we always just handle it immediately.
const char *operation = xrequest->Attribute("operation");
TiXmlElement *xobject = xrequest->FirstChildElement("object");
const char *property_name = xrequest->Attribute("property_name");
int needs_response = 0;
xrequest->Attribute("needs_response", &needs_response);
int unique_id = 0;
xrequest->Attribute("unique_id", &unique_id);
P3D_object *value = NULL;
TiXmlElement *xvalue = xrequest->FirstChildElement("value");
if (xvalue != NULL) {
value = _session->xml_to_p3dobj(xvalue);
}
if (value == NULL) {
value = inst_mgr->new_none_object();
}
if (operation != NULL && xobject != NULL) {
P3D_object *object = _session->xml_to_p3dobj(xobject);
if (property_name == NULL) {
property_name = "";
}
handle_script_request(operation, object, property_name, value,
(needs_response != 0), unique_id);
}
P3D_OBJECT_DECREF(value);
} else if (strcmp(rtype, "drop_p3dobj") == 0) {
int object_id;
if (xrequest->QueryIntAttribute("object_id", &object_id) == TIXML_SUCCESS) {
// We no longer need to keep this reference.
_session->drop_p3dobj(object_id);
}
} else if (strcmp(rtype, "stop") == 0) {
// A stop request from Python code. This is kind of weird, but OK.
request = new P3D_request;
request->_request_type = P3D_RT_stop;
} else {
nout << "Ignoring request of type " << rtype << "\n";
}
}
if (request != NULL) {
request->_instance = this;
}
return request;
}
////////////////////////////////////////////////////////////////////
// Function: P3DInstance::handle_notify_request
// Access: Private
// Description: Called (in the main thread) when a notify request is
// received from the subprocess.
////////////////////////////////////////////////////////////////////
void P3DInstance::
handle_notify_request(const string &message) {
// We look for certain notify events that have particular meaning
// to this instance.
nout << "Got notify: " << message << "\n";
if (message == "onpythonload") {
// Once Python is up and running, we can get the actual main
// object from the Python side, and merge it with our own.
TiXmlDocument *doc = new TiXmlDocument;
TiXmlElement *xcommand = new TiXmlElement("command");
xcommand->SetAttribute("cmd", "pyobj");
xcommand->SetAttribute("op", "get_panda_script_object");
doc->LinkEndChild(xcommand);
TiXmlDocument *response = _session->command_and_response(doc);
P3D_object *result = NULL;
if (response != NULL) {
TiXmlElement *xresponse = response->FirstChildElement("response");
if (xresponse != NULL) {
TiXmlElement *xvalue = xresponse->FirstChildElement("value");
if (xvalue != NULL) {
result = _session->xml_to_p3dobj(xvalue);
}
}
delete response;
}
if (result != NULL) {
_panda_script_object->set_pyobj(result);
P3D_OBJECT_DECREF(result);
}
_panda_script_object->set_string_property("status", "running");
} else if (message == "onwindowopen") {
// The process told us that it just succesfully opened its
// window. Tear down the splash window.
_instance_window_opened = true;
if (_splash_window != NULL) {
delete _splash_window;
_splash_window = NULL;
}
if (_temp_splash_image != NULL) {
delete _temp_splash_image;
_temp_splash_image = NULL;
}
_panda_script_object->set_string_property("status", "open");
#ifdef __APPLE__
// Start a timer to update the frame repeatedly. This seems to be
// steadier than waiting for nullEvent.
CFRunLoopTimerContext timer_context;
memset(&timer_context, 0, sizeof(timer_context));
timer_context.info = this;
_frame_timer = CFRunLoopTimerCreate
(NULL, 0, 1.0 / 60.0, 0, 0, timer_callback, &timer_context);
CFRunLoopRef run_loop = CFRunLoopGetCurrent();
CFRunLoopAddTimer(run_loop, _frame_timer, kCFRunLoopCommonModes);
#endif // __APPLE__
}
}
////////////////////////////////////////////////////////////////////
// Function: P3DInstance::handle_script_request
// Access: Private
// Description: Called (in the main thread) when a script request is
// received from the subprocess.
////////////////////////////////////////////////////////////////////
void P3DInstance::
handle_script_request(const string &operation, P3D_object *object,
const string &property_name, P3D_object *value,
bool needs_response, int unique_id) {
TiXmlDocument *doc = new TiXmlDocument;
TiXmlElement *xcommand = new TiXmlElement("command");
xcommand->SetAttribute("cmd", "script_response");
xcommand->SetAttribute("unique_id", unique_id);
doc->LinkEndChild(xcommand);
if (operation == "get_property") {
P3D_object *result = P3D_OBJECT_GET_PROPERTY(object, property_name.c_str());
// We've got the property value; feed it back down to the
// subprocess.
if (result != NULL) {
xcommand->LinkEndChild(_session->p3dobj_to_xml(result));
P3D_OBJECT_DECREF(result);
}
} else if (operation == "set_property") {
bool result =
P3D_OBJECT_SET_PROPERTY(object, property_name.c_str(), value);
TiXmlElement *xvalue = new TiXmlElement("value");
xvalue->SetAttribute("type", "bool");
xvalue->SetAttribute("value", (int)result);
xcommand->LinkEndChild(xvalue);
} else if (operation == "del_property") {
bool result = P3D_OBJECT_SET_PROPERTY(object, property_name.c_str(), NULL);
TiXmlElement *xvalue = new TiXmlElement("value");
xvalue->SetAttribute("type", "bool");
xvalue->SetAttribute("value", (int)result);
xcommand->LinkEndChild(xvalue);
} else if (operation == "has_method") {
bool result = P3D_OBJECT_HAS_METHOD(object, property_name.c_str());
TiXmlElement *xvalue = new TiXmlElement("value");
xvalue->SetAttribute("type", "bool");
xvalue->SetAttribute("value", (int)result);
xcommand->LinkEndChild(xvalue);
} else if (operation == "call") {
// Convert the single value parameter into an array of parameters.
P3D_object **values = &value;
int num_values = 1;
if (value->_class == &P3DObject::_object_class) {
P3DObject *p3dobj = (P3DObject *)value;
if (p3dobj->get_object_array_size() != -1) {
values = p3dobj->get_object_array();
num_values = p3dobj->get_object_array_size();
}
}
P3D_object *result =
P3D_OBJECT_CALL(object, property_name.c_str(), needs_response,
values, num_values);
if (result != NULL) {
xcommand->LinkEndChild(_session->p3dobj_to_xml(result));
P3D_OBJECT_DECREF(result);
}
} else if (operation == "eval") {
P3D_object *result;
int size = P3D_OBJECT_GET_STRING(value, NULL, 0);
char *buffer = new char[size + 1];
P3D_OBJECT_GET_STRING(value, buffer, size + 1);
result = P3D_OBJECT_EVAL(object, buffer);
delete[] buffer;
if (result != NULL) {
xcommand->LinkEndChild(_session->p3dobj_to_xml(result));
P3D_OBJECT_DECREF(result);
}
}
if (needs_response) {
_session->send_command(doc);
} else {
delete doc;
}
}
////////////////////////////////////////////////////////////////////
// Function: P3DInstance::make_splash_window
// Access: Private
// Description: Creates the splash window to be displayed at startup,
// if it's time.
////////////////////////////////////////////////////////////////////
void P3DInstance::
make_splash_window() {
if (_splash_window != NULL || _instance_window_opened) {
// Already got one, or we're already showing the real instance.
return;
}
if (!_got_wparams) {
// Don't know where to put it yet.
return;
}
if (_wparams.get_window_type() == P3D_WT_hidden) {
// We're hidden, and so is the splash window.
return;
}
if (_wparams.get_window_type() != P3D_WT_embedded && !_stuff_to_download) {
// If it's a toplevel or fullscreen window, then we don't want a
// splash window until we have stuff to download.
return;
}
_splash_window = new SplashWindowType(this);
_splash_window->set_wparams(_wparams);
_splash_window->set_install_label(_install_label);
P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
if (!_fparams.has_token("splash_img")) {
// No specific splash image is specified; get the default splash
// image. We do this via the P3DPackage interface, so we can
// use the cached version on disk if it's good.
P3DHost *host = inst_mgr->get_host(PANDA_PACKAGE_HOST_URL);
_splash_package = host->get_package("splash", "");
_splash_package->add_instance(this);
} else {
// We have an explicit splash image specified, so just download it
// directly. This one won't be cached locally (though the browser
// might be free to cache it).
string splash_image_url = _fparams.lookup_token("splash_img");
if (splash_image_url.empty()) {
// No splash image. Never mind.
return;
}
// Make a temporary file to receive the splash image.
assert(_temp_splash_image == NULL);
_temp_splash_image = new P3DTemporaryFile(".jpg");
// Start downloading the requested splash image.
SplashDownload *download = new SplashDownload(this);
download->set_url(splash_image_url);
download->set_filename(_temp_splash_image->get_filename());
start_download(download);
}
}
////////////////////////////////////////////////////////////////////
// Function: P3DInstance::report_package_info_ready
// Access: Private
// Description: Notified when a package information has been
// successfully downloaded and the package is idle,
// waiting for activate_download() to be called.
////////////////////////////////////////////////////////////////////
void P3DInstance::
report_package_info_ready(P3DPackage *package) {
if (package == _splash_package) {
// A special case: we just downloaded the splash image, via the
// package interface.
if (_splash_window != NULL) {
string filename = package->get_desc_file_pathname();
_splash_window->set_image_filename(filename, false);
}
return;
}
if (get_packages_info_ready()) {
// All packages are ready to go. Let's start some download
// action.
_downloading_packages.clear();
_total_download_size = 0;
Packages::const_iterator pi;
for (pi = _packages.begin(); pi != _packages.end(); ++pi) {
P3DPackage *package = (*pi);
if (package->get_info_ready() && !package->get_ready()) {
_downloading_packages.push_back(package);
_total_download_size += package->get_download_size();
}
}
_download_package_index = 0;
_total_downloaded = 0;
nout << "Beginning download of " << _downloading_packages.size()
<< " packages, total " << _total_download_size
<< " bytes required.\n";
if (_downloading_packages.size() > 0) {
_stuff_to_download = true;
// Maybe it's time to open a splash window now.
make_splash_window();
}
if (_splash_window != NULL) {
_splash_window->set_install_progress(0.0);
}
_panda_script_object->set_string_property("status", "downloading");
_panda_script_object->set_int_property("numDownloadingPackages", _downloading_packages.size());
_panda_script_object->set_int_property("totalDownloadSize", _total_download_size);
send_notify("ondownloadbegin");
start_next_download();
}
}
////////////////////////////////////////////////////////////////////
// Function: P3DInstance::start_next_download
// Access: Private
// Description: Checks whether all packages are ready and waiting to
// be downloaded; if so, starts the next package in
// sequence downloading.
////////////////////////////////////////////////////////////////////
void P3DInstance::
start_next_download() {
while (_download_package_index < (int)_downloading_packages.size()) {
P3DPackage *package = _downloading_packages[_download_package_index];
if (package->get_failed()) {
// Too bad. TODO: fail.
return;
}
if (!package->get_ready()) {
// This package is ready to download. Begin.
string name = package->get_package_display_name();
if (name.empty()) {
name = package->get_package_name();
}
_panda_script_object->set_string_property("downloadPackageName", package->get_package_name());
_panda_script_object->set_string_property("downloadPackageDisplayName", name);
_panda_script_object->set_int_property("downloadPackageNumber", _download_package_index + 1);
_panda_script_object->set_int_property("downloadPackageSize", package->get_download_size());
set_install_label("Installing " + name);
nout << "Downloading " << package->get_package_name()
<< ", package " << _download_package_index + 1
<< " of " << _downloading_packages.size()
<< ", " << package->get_download_size()
<< " bytes.\n";
package->activate_download();
send_notify("ondownloadnext");
return;
}
// This package has been downloaded. Move to the next.
_total_downloaded += package->get_download_size();
++_download_package_index;
}
// Looks like we're all done downloading. Launch!
_downloading_packages.clear();
if (get_packages_ready()) {
if (!_panda_script_object->get_bool_property("downloadComplete")) {
_panda_script_object->set_bool_property("downloadComplete", true);
_panda_script_object->set_string_property("status", "starting");
send_notify("ondownloadcomplete");
}
// Notify the session also.
if (_session != NULL) {
_session->report_packages_done(this, true);
}
}
}
////////////////////////////////////////////////////////////////////
// Function: P3DInstance::report_instance_progress
// Access: Private
// Description: Notified as the instance file is downloaded.
////////////////////////////////////////////////////////////////////
void P3DInstance::
report_instance_progress(double progress) {
if (!_show_dl_instance_progress) {
// If we haven't yet set the download label, set it after a full
// second has elapsed. We don't want to set it too soon, because
// we're not really sure how long it will take to download (the
// instance file might be already in the browser cache).
#ifdef _WIN32
int now = GetTickCount();
double elapsed = (double)(now - _start_dl_instance_tick) * 0.001;
#else
struct timeval now;
gettimeofday(&now, NULL);
double elapsed = (double)(now.tv_sec - _start_dl_instance_timeval.tv_sec) +
(double)(now.tv_usec - _start_dl_instance_timeval.tv_usec) / 1000000.0;
#endif
// Put up the progress bar after 2 seconds have elapsed, if we've
// still got some distance to go; or after 5 seconds have elapsed
// regardless.
if ((elapsed > 2.0 && progress < 0.7) ||
(elapsed > 5.0)) {
_show_dl_instance_progress = true;
}
}
if (_splash_window != NULL && _show_dl_instance_progress) {
_splash_window->set_install_progress(progress);
}
_panda_script_object->set_float_property("instanceDownloadProgress", progress);
}
////////////////////////////////////////////////////////////////////
// Function: P3DInstance::report_package_progress
// Access: Private
// Description: Notified as the packages required by the instance
// file are downloaded.
////////////////////////////////////////////////////////////////////
void P3DInstance::
report_package_progress(P3DPackage *package, double progress) {
if (_download_package_index >= (int)_downloading_packages.size() ||
package != _downloading_packages[_download_package_index]) {
// Quietly ignore a download progress report from an unexpected
// package.
return;
}
// Scale the progress into the range appropriate to this package.
progress = (progress * package->get_download_size() + _total_downloaded) / _total_download_size;
progress = min(progress, 1.0);
if (_splash_window != NULL) {
_splash_window->set_install_progress(progress);
}
_panda_script_object->set_float_property("downloadProgress", progress);
}
////////////////////////////////////////////////////////////////////
// Function: P3DInstance::report_package_done
// Access: Private
// Description: Notified when a required package is fully downloaded,
// or failed.
////////////////////////////////////////////////////////////////////
void P3DInstance::
report_package_done(P3DPackage *package, bool success) {
nout << "Done downloading " << package->get_package_name()
<< ": success = " << success << "\n";
if (success) {
report_package_progress(package, 1.0);
start_next_download();
} else {
// TODO: fail.
}
}
////////////////////////////////////////////////////////////////////
// Function: P3DInstance::set_install_label
// Access: Private
// Description: Sets the install label that will be displayed on the
// splash window, if it is present.
////////////////////////////////////////////////////////////////////
void P3DInstance::
set_install_label(const string &install_label) {
_install_label = install_label;
if (_splash_window != NULL) {
_splash_window->set_install_label(_install_label);
}
}
////////////////////////////////////////////////////////////////////
// Function: P3DInstance::send_notify
// Access: Private
// Description: Generates a synthetic notify message here at the C++
// level.
//
// Most notify messages are generated from within the
// Python code, and don't use this method; but a few
// have to be sent before Python has started, and those
// come through this method.
////////////////////////////////////////////////////////////////////
void P3DInstance::
send_notify(const string &message) {
P3D_request *request = new P3D_request;
request->_request_type = P3D_RT_notify;
request->_request._notify._message = strdup(message.c_str());
add_baked_request(request);
}
////////////////////////////////////////////////////////////////////
// Function: P3DInstance::paint_window
// Access: Private
// Description: Actually paints the rendered image to the browser
// window. This is only implemented (and needed) for
// OSX, where the child process isn't allowed to do it
// directly.
////////////////////////////////////////////////////////////////////
void P3DInstance::
paint_window() {
#ifdef __APPLE__
if (_swbuffer == NULL || !_instance_window_opened) {
return;
}
QDErr err;
// blit rendered framebuffer into window backing store
int x_size = min(_wparams.get_win_width(), _swbuffer->get_x_size());
int y_size = min(_wparams.get_win_height(), _swbuffer->get_y_size());
size_t rowsize = _swbuffer->get_row_size();
if (_swbuffer->ready_for_read()) {
// Copy the new framebuffer image from the child process.
const void *framebuffer = _swbuffer->open_read_framebuffer();
// We have to reverse the image vertically first (different
// conventions between Panda and Mac).
for (int yi = 0; yi < y_size; ++yi) {
#ifndef __BIG_ENDIAN__
// On a little-endian machine, we only have to reverse the order of the rows.
memcpy(_reversed_buffer + (y_size - 1 - yi) * rowsize,
(char *)framebuffer + yi * rowsize,
rowsize);
#else // __BIG_ENDIAN__
// On a big-endian machine, we need to do more work.
// It appears that kBGRAPixelFormat, below, is ignored on
// big-endian machines, and it is treated as KARGBPixelFormat
// regardless of what we specify. Vexing. To compensate for
// this, we have to reverse the color channels ourselves on
// big-endian machines.
const char *source = (const char *)framebuffer + yi * rowsize;
const char *stop = source + x_size * 4;
char *dest = (_reversed_buffer + (y_size - 1 - yi) * rowsize);
while (source < stop) {
char b = source[0];
char g = source[1];
char r = source[2];
char a = source[3];
dest[0] = a;
dest[1] = r;
dest[2] = g;
dest[3] = b;
dest += 4;
source += 4;
}
#endif
}
_swbuffer->close_read_framebuffer();
} else {
// No frame ready. Just re-paint the frame we had saved last
// time.
}
/*
// This is an attempt to paint the frame using the less-deprecated
// Quartz interfaces. Sure does seem like a lot of layers to go
// through just to paint a bitmap.
CFDataRef data =
CFDataCreateWithBytesNoCopy(NULL, (const UInt8 *)_reversed_buffer,
y_size * rowsize, kCFAllocatorNull);
CGDataProviderRef provider = CGDataProviderCreateWithCFData(data);
CGColorSpaceRef color_space = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
CGImageRef image =
CGImageCreate(x_size, y_size, 8, 32, rowsize, color_space,
kCGImageAlphaFirst | kCGBitmapByteOrder32Little, provider,
NULL, false, kCGRenderingIntentDefault);
CGrafPtr port = _wparams.get_parent_window()._port;
CGContextRef context;
err = QDBeginCGContext(port, &context);
if (err != noErr) {
nout << "Error: QDBeginCGContext\n";
return;
}
// CGContextTranslateCTM(context, 0.0, win_height);
// CGContextScaleCTM(context, 1.0, -1.0);
// We have to rely on the clipping rectangle having been set up
// correctly in order to get the proper location to draw the image.
// This isn't completely right, because if the image is slightly
// offscreen, the top left of the clipping rectangle will no longer
// correspond to the top left of the original image.
CGRect rect = CGContextGetClipBoundingBox(context);
nout << "rect: " << rect.origin.x << " " << rect.origin.y
<< " " << rect.size.width << " " << rect.size.height << "\n";
rect.size.width = x_size;
rect.size.height = y_size;
CGContextDrawImage(context, rect, image);
//CGContextSynchronize(context);
CGContextFlush(context);
QDEndCGContext(port, &context);
CGImageRelease(image);
CGColorSpaceRelease(color_space);
CGDataProviderRelease(provider);
CFRelease(data);
*/
// Painting the frame using the deprecated QuickDraw interfaces.
Rect src_rect = {0, 0, y_size, x_size};
Rect ddrc_rect = {0, 0, y_size, x_size};
GWorldPtr pGWorld;
err = NewGWorldFromPtr(&pGWorld, k32BGRAPixelFormat, &src_rect, 0, 0, 0,
_reversed_buffer, rowsize);
if (err != noErr) {
nout << " error in NewGWorldFromPtr, called from paint_window()\n";
return;
}
GrafPtr out_port = _wparams.get_parent_window()._port;
GrafPtr portSave = NULL;
Boolean portChanged = QDSwapPort(out_port, &portSave);
// Make sure the clipping rectangle isn't in the way. Is there a
// better way to eliminate the cliprect from consideration?
Rect r = { 0, 0, 0x7fff, 0x7fff };
ClipRect(&r);
CopyBits(GetPortBitMapForCopyBits(pGWorld),
GetPortBitMapForCopyBits(out_port),
&src_rect, &ddrc_rect, srcCopy, 0);
if (portChanged) {
QDSwapPort(portSave, NULL);
}
DisposeGWorld(pGWorld);
#endif // __APPLE__
}
////////////////////////////////////////////////////////////////////
// Function: P3DInstance::add_modifier_flags
// Access: Private
// Description: OSX only: adds the appropriate bits to the Event flag
// bitmask to correspond to the modifier buttons held in
// the MacOS-style EventRecord::modifiers mask.
////////////////////////////////////////////////////////////////////
void P3DInstance::
add_modifier_flags(unsigned int &swb_flags, int modifiers) {
#ifdef __APPLE__
if (modifiers & cmdKey) {
swb_flags |= SubprocessWindowBuffer::EF_meta_held;
}
if (modifiers & shiftKey) {
swb_flags |= SubprocessWindowBuffer::EF_shift_held;
}
if (modifiers & optionKey) {
swb_flags |= SubprocessWindowBuffer::EF_alt_held;
}
if (modifiers & controlKey) {
swb_flags |= SubprocessWindowBuffer::EF_control_held;
}
#endif // __APPLE__
}
#ifdef __APPLE__
////////////////////////////////////////////////////////////////////
// Function: P3DInstance::timer_callback
// Access: Private
// Description: OSX only: this callback is associated with a
// CFRunLoopTimer, to be called periodically for
// updating the frame.
////////////////////////////////////////////////////////////////////
void P3DInstance::
timer_callback(CFRunLoopTimerRef timer, void *info) {
P3DInstance *self = (P3DInstance *)info;
self->request_refresh();
//self->paint_window();
}
#endif // __APPLE__
////////////////////////////////////////////////////////////////////
// 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 (directly, not
// via the package interface). Put it onscreen if our splash
// window still exists.
if (_inst->_splash_window != NULL) {
_inst->_splash_window->set_image_filename(get_filename(), true);
}
}
}
////////////////////////////////////////////////////////////////////
// Function: P3DInstance::InstanceDownload::Constructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
P3DInstance::InstanceDownload::
InstanceDownload(P3DInstance *inst) :
_inst(inst)
{
}
////////////////////////////////////////////////////////////////////
// Function: P3DInstance::InstanceDownload::download_progress
// Access: Protected, Virtual
// Description: Intended to be overloaded to generate an occasional
// callback as new data comes in.
////////////////////////////////////////////////////////////////////
void P3DInstance::InstanceDownload::
download_progress() {
P3DFileDownload::download_progress();
_inst->report_instance_progress(get_download_progress());
}
////////////////////////////////////////////////////////////////////
// Function: P3DInstance::InstanceDownload::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::InstanceDownload::
download_finished(bool success) {
P3DFileDownload::download_finished(success);
if (success) {
// We've successfully downloaded the instance data.
_inst->set_p3d_filename(get_filename());
}
}