auto_start (wip)

This commit is contained in:
David Rose 2009-09-08 19:46:06 +00:00
parent 6d1facae8c
commit 76679a765f
21 changed files with 1221 additions and 375 deletions

View File

@ -19,14 +19,39 @@ class coreapi(solo):
# single .dll (or dylib, or whatever).
file('p3d_plugin.dll')
class splash(solo):
# We also store the default splash image, as "solo". Well, it
# has to go somewhere.
splashFilename = Filename('maps/panda_splash.jpg')
if splashFilename.resolveFilename(getModelPath().getValue()):
file(splashFilename, newName = 'splash.jpg')
else:
print "Could not locate %s" % (splashFilename)
class images(package):
# The default startup images are stored as their own package.
names = ['download', 'play_click', 'play_ready', 'play_rollover',
'auth_click', 'auth_ready', 'auth_rollover']
configDict = {}
for name in names:
# Look for a png image first.
basename = '%s.png' % (name)
filename = Filename('plugin_images/%s' % (basename))
found = filename.resolveFilename(getModelPath().getValue())
if not found:
# Then try a jpeg image.
basename = '%s.jpg' % (name)
filename = Filename('plugin_images/%s' % (basename))
found = filename.resolveFilename(getModelPath().getValue())
if found:
# Add the image file to the package
file(filename, newName = basename, extract = True)
# And set the config variable to reference it.
token = '%s_img' % (name)
configDict[token] = basename
else:
print "Could not locate %s" % (filename)
# Also make a few special cases. We use the same default image
# for both download and launch.
download = configDict.get('download_img', None)
if download:
configDict['launch_img'] = download
config(**configDict)
class panda3d(package):
# The core Panda3D package. Contains Python and most of the graphics

View File

@ -3,8 +3,8 @@
#define BUILD_DIRECTORY $[and $[HAVE_P3D_PLUGIN],$[HAVE_TINYXML],$[HAVE_OPENSSL],$[HAVE_ZLIB]]
#begin lib_target
#define BUILD_TARGET $[HAVE_JPEG]
#define USE_PACKAGES tinyxml openssl zlib jpeg x11
#define BUILD_TARGET $[and $[HAVE_JPEG],$[HAVE_PNG]]
#define USE_PACKAGES tinyxml openssl zlib jpeg png x11
#define TARGET p3d_plugin
#define LIB_PREFIX
@ -111,7 +111,7 @@
interrogatedb:c dconfig:c dtoolconfig:m \
express:c pandaexpress:m \
prc:c pstatclient:c pandabase:c linmath:c putil:c \
pipeline:c event:c nativenet:c net:c panda:m
pipeline:c event:c nativenet:c net:c display:c panda:m
#define SOURCES \
binaryXml.cxx binaryXml.h \
@ -138,7 +138,7 @@
interrogatedb:c dconfig:c dtoolconfig:m \
express:c pandaexpress:m \
prc:c pstatclient:c pandabase:c linmath:c putil:c \
pipeline:c event:c nativenet:c net:c panda:m
pipeline:c event:c nativenet:c net:c display:c panda:m
#define SOURCES \
binaryXml.cxx binaryXml.h \

View File

@ -94,3 +94,38 @@ inline bool P3DInstance::
is_started() const {
return (_session != NULL);
}
////////////////////////////////////////////////////////////////////
// Function: P3DInstance::ImageFile::Constructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
inline P3DInstance::ImageFile::
ImageFile() {
_use_standard_image = true;
_temp_filename = NULL;
_image_placement = P3DSplashWindow::IP_none;
}
////////////////////////////////////////////////////////////////////
// Function: P3DInstance::ImageFile::Destructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
inline P3DInstance::ImageFile::
~ImageFile() {
cleanup();
}
////////////////////////////////////////////////////////////////////
// Function: P3DInstance::ImageFile::cleanup
// Access: Public
// Description: Removes the temporary file, if any.
////////////////////////////////////////////////////////////////////
inline void P3DInstance::ImageFile::
cleanup() {
if (_temp_filename != NULL) {
delete _temp_filename;
_temp_filename = NULL;
}
}

View File

@ -45,6 +45,19 @@ typedef P3DX11SplashWindow SplashWindowType;
typedef P3DSplashWindow SplashWindowType;
#endif
// These are the various image files we might download for use in the
// splash window. This list must match the ImageType enum.
const char *P3DInstance::_image_type_names[P3DInstance::IT_num_image_types] = {
"download",
"ready",
"failed",
"launch",
"play_ready",
"play_rollover",
"play_click",
"none", // Not really used.
};
////////////////////////////////////////////////////////////////////
// Function: P3DInstance::Constructor
// Access: Public
@ -62,8 +75,9 @@ P3DInstance(P3D_request_ready_func *func,
_user_data = user_data;
_request_pending = false;
_temp_p3d_filename = NULL;
_splash_package = NULL;
_temp_splash_image = NULL;
_image_package = NULL;
_current_background_image = IT_none;
_current_button_image = IT_none;
_got_fparams = false;
_got_wparams = false;
@ -74,12 +88,13 @@ P3DInstance(P3D_request_ready_func *func,
_instance_id = inst_mgr->get_unique_id();
_hidden = false;
_allow_python_dev = false;
_auto_start = false;
_session = NULL;
_panda3d = NULL;
_splash_window = NULL;
_instance_window_opened = false;
_stuff_to_download = false;
INIT_LOCK(_request_lock);
_requested_stop = false;
@ -124,9 +139,9 @@ P3DInstance::
(*pi)->remove_instance(this);
}
_packages.clear();
if (_splash_package != NULL) {
_splash_package->remove_instance(this);
_splash_package = NULL;
if (_image_package != NULL) {
_image_package->remove_instance(this);
_image_package = NULL;
}
if (_splash_window != NULL) {
@ -139,11 +154,6 @@ P3DInstance::
_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);
@ -221,7 +231,6 @@ set_p3d_url(const string &p3d_url) {
////////////////////////////////////////////////////////////////////
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);
@ -238,6 +247,9 @@ set_p3d_filename(const string &p3d_filename) {
sstream.seekg(0);
TiXmlDocument doc;
sstream >> doc;
// This also starts required packages downloading. When all
// packages have been installed, we will start the instance.
scan_app_desc_file(&doc);
}
@ -248,13 +260,23 @@ set_p3d_filename(const string &p3d_filename) {
strm << inst_mgr->get_unique_id();
_session_key = strm.str();
// Until we've done all of the above processing, we haven't fully
// committed to having fparams. (Setting this flag down here
// instead of up there avoids starting the instance in
// scan_app_desc_file(), before we've had a chance to finish
// processing this method.)
_got_fparams = true;
// 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);
// Now that we're all set up, start the instance if we're fully
// downloaded.
if (get_packages_ready() && _got_wparams) {
ready_to_start();
}
}
////////////////////////////////////////////////////////////////////
@ -331,6 +353,10 @@ set_wparams(const P3DWindowParams &wparams) {
_session->send_command(doc);
}
if (get_packages_ready() && _got_fparams) {
ready_to_start();
}
}
////////////////////////////////////////////////////////////////////
@ -607,14 +633,14 @@ handle_event(P3D_event_data event) {
// convert the mouse coordinates successfully via
// GlobalToLocal().
GrafPtr out_port = _wparams.get_parent_window()._port;
GrafPtr portSave = NULL;
Boolean portChanged = QDSwapPort(out_port, &portSave);
GrafPtr port_save = NULL;
Boolean port_changed = QDSwapPort(out_port, &port_save);
Point pt = er->where;
GlobalToLocal(&pt);
if (portChanged) {
QDSwapPort(portSave, NULL);
if (port_changed) {
QDSwapPort(port_save, NULL);
}
SubprocessWindowBuffer::Event swb_event;
@ -698,12 +724,16 @@ add_package(P3DPackage *package) {
return;
}
_packages.push_back(package);
package->add_instance(this);
if (package->get_package_name() == "panda3d") {
_panda3d = package;
}
_packages.push_back(package);
// This call must be at the end of this method, because it might
// ultimately start the application before it returns (if this was
// the last required package).
package->add_instance(this);
}
////////////////////////////////////////////////////////////////////
@ -864,6 +894,22 @@ make_xml() {
return xinstance;
}
////////////////////////////////////////////////////////////////////
// Function: P3DInstance::splash_button_clicked
// Access: Public
// Description: Called by the P3DSplashWindow code when the user
// clicks the play button visible on the splash window.
////////////////////////////////////////////////////////////////////
void P3DInstance::
splash_button_clicked() {
// If we haven't launched yet, launch now.
if (_session == NULL) {
set_background_image(IT_launch);
P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
inst_mgr->start_instance(this);
}
}
////////////////////////////////////////////////////////////////////
// Function: P3DInstance::scan_app_desc_file
// Access: Private
@ -872,6 +918,7 @@ make_xml() {
////////////////////////////////////////////////////////////////////
void P3DInstance::
scan_app_desc_file(TiXmlDocument *doc) {
cerr << "scan_app_desc_file\n";
P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
TiXmlElement *xpackage = doc->FirstChildElement("package");
@ -895,8 +942,23 @@ scan_app_desc_file(TiXmlDocument *doc) {
if (xconfig->QueryIntAttribute("allow_python_dev", &allow_python_dev) == TIXML_SUCCESS) {
_allow_python_dev = (allow_python_dev != 0);
}
int auto_start = 0;
if (xconfig->QueryIntAttribute("auto_start", &auto_start) == TIXML_SUCCESS) {
_auto_start = (auto_start != 0);
}
}
// auto_start is true if it is set in the application itself, or in
// the web tokens.
if (_fparams.lookup_token_int("auto_start") != 0) {
_auto_start = true;
}
nout << "_auto_start = " << _auto_start << "\n";
// But auto_start will be set false if the p3d file has not been
// signed by an approved signature. TODO.
if (_hidden && _got_wparams) {
_wparams.set_window_type(P3D_WT_hidden);
}
@ -1069,9 +1131,8 @@ handle_notify_request(const string &message) {
_splash_window = NULL;
}
if (_temp_splash_image != NULL) {
delete _temp_splash_image;
_temp_splash_image = NULL;
for (int i = 0; i < (int)IT_num_image_types; ++i) {
_image_files[i].cleanup();
}
_panda_script_object->set_string_property("status", "open");
@ -1206,11 +1267,13 @@ make_splash_window() {
// We're hidden, and so is the splash window.
return;
}
/* temp removing: hack for debugging.
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);
@ -1218,34 +1281,117 @@ make_splash_window() {
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);
// Direct the "download" image to the background slot on the splash
// window for now, while we perform the download.
set_background_image(IT_download);
} 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;
// Go get the required images.
for (int i = 0; i < (int)IT_none; ++i) {
string token_keyword = string(_image_type_names[i]) + "_img";
if (!_fparams.has_token(token_keyword)) {
// No specific image for this type is specified; get the default
// image. We do this via the P3DPackage interface, so we can
// use the cached version on disk if it's good.
_image_files[i]._use_standard_image = true;
if (_image_package == NULL) {
P3DHost *host = inst_mgr->get_host(PANDA_PACKAGE_HOST_URL);
_image_package = host->get_package("images", "");
_image_package->add_instance(this);
}
} else {
// We have an explicit image specified for this slot, so just
// download it directly. This one won't be cached locally
// (though the browser might be free to cache it).
_image_files[i]._use_standard_image = false;
string image_url = _fparams.lookup_token(token_keyword);
if (image_url.empty()) {
// No splash image. Never mind.
return;
}
// Make a temporary file to receive the splash image.
assert(_image_files[i]._temp_filename == NULL);
_image_files[i]._temp_filename = new P3DTemporaryFile(".jpg");
// Start downloading the requested image.
ImageDownload *download = new ImageDownload(this, i);
download->set_url(image_url);
download->set_filename(_image_files[i]._temp_filename->get_filename());
start_download(download);
}
}
}
////////////////////////////////////////////////////////////////////
// Function: P3DInstance::set_background_image
// Access: Private
// Description: Specifies the particular image that should be
// displayed as the background image in the splash
// window. Specify IT_none to take the background image
// away.
////////////////////////////////////////////////////////////////////
void P3DInstance::
set_background_image(ImageType image_type) {
if (image_type != _current_background_image) {
// Remove the previous image.
_image_files[_current_background_image]._image_placement = P3DSplashWindow::IP_none;
// Install the new image.
_current_background_image = image_type;
if (_current_background_image != IT_none) {
_image_files[_current_background_image]._image_placement = P3DSplashWindow::IP_background;
}
// Update the splash window.
if (_splash_window != NULL) {
_splash_window->set_image_filename(_image_files[_current_background_image]._filename, P3DSplashWindow::IP_background);
}
}
}
////////////////////////////////////////////////////////////////////
// Function: P3DInstance::set_button_image
// Access: Private
// Description: Specifies the particular image that should be
// displayed as the button image in the splash
// window. Specify IT_none to take the button image
// away.
//
// This actually defines a trilogy of button images:
// ready, rollover, click.
////////////////////////////////////////////////////////////////////
void P3DInstance::
set_button_image(ImageType image_type) {
if (image_type != _current_button_image) {
// Remove the previous image.
_image_files[_current_button_image]._image_placement = P3DSplashWindow::IP_none;
if (_current_button_image != IT_none) {
_image_files[_current_button_image + 1]._image_placement = P3DSplashWindow::IP_none;
_image_files[_current_button_image + 2]._image_placement = P3DSplashWindow::IP_none;
}
// Install the new image.
_current_button_image = image_type;
if (_current_button_image != IT_none) {
_image_files[_current_button_image]._image_placement = P3DSplashWindow::IP_button_ready;
_image_files[_current_button_image + 1]._image_placement = P3DSplashWindow::IP_button_rollover;
_image_files[_current_button_image + 2]._image_placement = P3DSplashWindow::IP_button_click;
}
// Update the splash window.
if (_splash_window != NULL) {
if (_current_button_image != IT_none) {
_splash_window->set_image_filename(_image_files[_current_button_image]._filename, P3DSplashWindow::IP_button_ready);
_splash_window->set_image_filename(_image_files[_current_button_image + 1]._filename, P3DSplashWindow::IP_button_rollover);
_splash_window->set_image_filename(_image_files[_current_button_image + 2]._filename, P3DSplashWindow::IP_button_click);
} else {
_splash_window->set_image_filename(string(), P3DSplashWindow::IP_button_ready);
_splash_window->set_image_filename(string(), P3DSplashWindow::IP_button_rollover);
_splash_window->set_image_filename(string(), P3DSplashWindow::IP_button_click);
}
}
// 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);
}
}
@ -1258,13 +1404,10 @@ make_splash_window() {
////////////////////////////////////////////////////////////////////
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);
}
if (package == _image_package) {
// A special case: the image package gets immediately downloaded,
// without waiting for anything else.
package->activate_download();
return;
}
@ -1360,11 +1503,37 @@ start_next_download() {
_panda_script_object->set_string_property("status", "starting");
send_notify("ondownloadcomplete");
}
// Notify the session also.
if (_session != NULL) {
_session->report_packages_done(this, true);
// Take down the download progress bar.
if (_splash_window != NULL) {
_splash_window->set_install_progress(0.0);
_splash_window->set_install_label("");
}
if (_got_wparams && _got_fparams) {
ready_to_start();
}
}
}
////////////////////////////////////////////////////////////////////
// Function: P3DInstance::ready_to_start
// Access: Private
// Description: Called internally when we have got the wparams and
// fparams and we have downloaded all required packages.
////////////////////////////////////////////////////////////////////
void P3DInstance::
ready_to_start() {
if (_auto_start) {
set_background_image(IT_launch);
P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
inst_mgr->start_instance(this);
} else if (_splash_window != NULL) {
// We're fully downloaded, and waiting for the user to click play.
set_background_image(IT_ready);
set_button_image(IT_play_ready);
_splash_window->set_button_active(true);
}
}
@ -1440,6 +1609,41 @@ void P3DInstance::
report_package_done(P3DPackage *package, bool success) {
nout << "Done downloading " << package->get_package_name()
<< ": success = " << success << "\n";
if (package == _image_package) {
// A special case: we just downloaded the image package, so get
// the image files out of it and point them to the splash window.
string package_dir = package->get_package_dir();
const TiXmlElement *xconfig = package->get_xconfig();
if (xconfig == NULL) {
nout << "No <config> entry in image package\n";
return;
}
for (int i = 0; i < (int)IT_none; ++i) {
if (_image_files[i]._use_standard_image) {
// This image indexes into the package. Go get the standard
// image filename.
string token = string(_image_type_names[i]) + "_img";
const string *basename = xconfig->Attribute(token);
if (basename == NULL) {
nout << "No entry in image package for " << token << "\n";
} else {
string image_filename = package_dir + "/" + *basename;
_image_files[i]._filename = image_filename;
// If the image should be on the window now, and the window
// still exists, put it up.
if (_splash_window != NULL &&
_image_files[i]._image_placement != P3DSplashWindow::IP_none) {
P3DSplashWindow::ImagePlacement image_placement = _image_files[i]._image_placement;
_splash_window->set_image_filename(image_filename, image_placement);
}
}
}
}
return;
}
if (success) {
report_package_progress(package, 1.0);
start_next_download();
@ -1615,8 +1819,8 @@ paint_window() {
}
GrafPtr out_port = _wparams.get_parent_window()._port;
GrafPtr portSave = NULL;
Boolean portChanged = QDSwapPort(out_port, &portSave);
GrafPtr port_save = NULL;
Boolean port_changed = QDSwapPort(out_port, &port_save);
// Make sure the clipping rectangle isn't in the way. Is there a
// better way to eliminate the cliprect from consideration?
@ -1627,8 +1831,8 @@ paint_window() {
GetPortBitMapForCopyBits(out_port),
&src_rect, &ddrc_rect, srcCopy, 0);
if (portChanged) {
QDSwapPort(portSave, NULL);
if (port_changed) {
QDSwapPort(port_save, NULL);
}
DisposeGWorld(pGWorld);
@ -1677,33 +1881,39 @@ timer_callback(CFRunLoopTimerRef timer, void *info) {
#endif // __APPLE__
////////////////////////////////////////////////////////////////////
// Function: P3DInstance::SplashDownload::Constructor
// Function: P3DInstance::ImageDownload::Constructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
P3DInstance::SplashDownload::
SplashDownload(P3DInstance *inst) :
_inst(inst)
P3DInstance::ImageDownload::
ImageDownload(P3DInstance *inst, int index) :
_inst(inst),
_index(index)
{
}
////////////////////////////////////////////////////////////////////
// Function: P3DInstance::SplashDownload::download_finished
// Function: P3DInstance::ImageDownload::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::
void P3DInstance::ImageDownload::
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);
// We've successfully downloaded the image (directly, not via the
// package interface).
_inst->_image_files[_index]._filename = get_filename();
// Put it onscreen if it's supposed to be onscreen now, and our
// splash window still exists.
if (_inst->_splash_window != NULL &&
_inst->_image_files[_index]._image_placement != P3DSplashWindow::IP_none) {
P3DSplashWindow::ImagePlacement image_placement = _inst->_image_files[_index]._image_placement;
_inst->_splash_window->set_image_filename(get_filename(), image_placement);
}
}
}

View File

@ -20,6 +20,8 @@
#include "p3dFileParams.h"
#include "p3dWindowParams.h"
#include "p3dReferenceCount.h"
#include "p3dSplashWindow.h"
#include "p3dTemporaryFile.h"
#include "get_tinyxml.h"
#ifdef __APPLE__
@ -98,15 +100,17 @@ public:
void request_refresh();
TiXmlElement *make_xml();
void splash_button_clicked();
private:
class SplashDownload : public P3DFileDownload {
class ImageDownload : public P3DFileDownload {
public:
SplashDownload(P3DInstance *inst);
ImageDownload(P3DInstance *inst, int index);
protected:
virtual void download_finished(bool success);
private:
P3DInstance *_inst;
int _index;
};
class InstanceDownload : public P3DFileDownload {
public:
@ -118,6 +122,20 @@ private:
P3DInstance *_inst;
};
// The different kinds of image files we download for the splash
// window.
enum ImageType {
IT_download,
IT_ready,
IT_failed,
IT_launch,
IT_play_ready,
IT_play_rollover,
IT_play_click,
IT_none, // Must be the last value
IT_num_image_types, // Not a real value
};
void scan_app_desc_file(TiXmlDocument *doc);
void send_browser_script_object();
@ -127,8 +145,11 @@ private:
const string &property_name, P3D_object *value,
bool needs_response, int unique_id);
void make_splash_window();
void set_background_image(ImageType image_type);
void set_button_image(ImageType image_type);
void report_package_info_ready(P3DPackage *package);
void start_next_download();
void ready_to_start();
void report_instance_progress(double progress);
void report_package_progress(P3DPackage *package, double progress);
void report_package_done(P3DPackage *package, bool progress);
@ -148,8 +169,25 @@ private:
P3DMainObject *_panda_script_object;
P3DTemporaryFile *_temp_p3d_filename;
P3DPackage *_splash_package;
P3DTemporaryFile *_temp_splash_image;
// For downloading the various images used by the splash window.
P3DPackage *_image_package;
static const char *_image_type_names[IT_num_image_types];
class ImageFile {
public:
inline ImageFile();
inline ~ImageFile();
inline void cleanup();
bool _use_standard_image;
P3DTemporaryFile *_temp_filename;
string _filename;
P3DSplashWindow::ImagePlacement _image_placement;
};
ImageFile _image_files[IT_num_image_types];
ImageType _current_background_image;
ImageType _current_button_image;
bool _got_fparams;
P3DFileParams _fparams;
@ -162,6 +200,7 @@ private:
string _log_basename;
bool _hidden;
bool _allow_python_dev;
bool _auto_start;
P3DSession *_session;
@ -220,7 +259,7 @@ private:
BakedRequests _baked_requests;
friend class P3DSession;
friend class SplashDownload;
friend class ImageDownload;
friend class P3DWindowParams;
friend class P3DPackage;
};

View File

@ -12,3 +12,28 @@
//
////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////
// Function: P3DOsxSplashWindow::OsxImageData::Constructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
inline P3DOsxSplashWindow::OsxImageData::
OsxImageData() {
_raw_data = NULL;
_image = NULL;
_color_space = NULL;
_provider = NULL;
_data = NULL;
}
////////////////////////////////////////////////////////////////////
// Function: P3DOsxSplashWindow::OsxImageData::Destructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
inline P3DOsxSplashWindow::OsxImageData::
~OsxImageData() {
dump_image();
}

View File

@ -27,10 +27,9 @@ P3DOsxSplashWindow::
P3DOsxSplashWindow(P3DInstance *inst) :
P3DSplashWindow(inst)
{
_image = NULL;
_image_data = NULL;
_install_progress = 0;
_got_wparams = false;
_mouse_active = false;
_toplevel_window = NULL;
}
@ -47,15 +46,6 @@ P3DOsxSplashWindow::
DisposeWindow(_toplevel_window);
_toplevel_window = NULL;
}
if (_image != NULL) {
DisposeGWorld(_image);
}
if (_image_data != NULL) {
delete[] _image_data;
_image_data = NULL;
}
}
////////////////////////////////////////////////////////////////////
@ -83,8 +73,8 @@ set_wparams(const P3DWindowParams &wparams) {
r.left = 10;
}
r.right = r.left + _wparams.get_win_width();
r.bottom = r.top + _wparams.get_win_height();
r.right = r.left + _win_width;
r.bottom = r.top + _win_height;
WindowAttributes attrib =
kWindowStandardDocumentAttributes | kWindowStandardHandlerAttribute;
CreateNewWindow(kDocumentWindowClass, attrib, &r, &_toplevel_window);
@ -93,6 +83,10 @@ set_wparams(const P3DWindowParams &wparams) {
EventTypeSpec list1[] = {
{ kEventClassWindow, kEventWindowDrawContent },
//{ kEventClassWindow, kEventWindowUpdate },
{ kEventClassMouse, kEventMouseUp },
{ kEventClassMouse, kEventMouseDown },
{ kEventClassMouse, kEventMouseMoved },
{ kEventClassMouse, kEventMouseDragged },
};
EventHandlerUPP gEvtHandler = NewEventHandlerUPP(st_event_callback);
@ -113,67 +107,27 @@ set_wparams(const P3DWindowParams &wparams) {
// Function: P3DOsxSplashWindow::set_image_filename
// Access: Public, Virtual
// Description: Specifies the name of a JPEG image file that is
// displayed in the center of the splash window. If
// image_filename_temp is true, the file is immediately
// deleted after it has been read.
// displayed in the center of the splash window.
////////////////////////////////////////////////////////////////////
void P3DOsxSplashWindow::
set_image_filename(const string &image_filename,
bool image_filename_temp) {
int num_channels;
string data;
if (!read_image(image_filename, image_filename_temp,
_image_height, _image_width, num_channels, data)) {
return;
}
set_image_filename(const string &image_filename, ImagePlacement image_placement) {
switch (image_placement) {
case IP_background:
load_image(_background_image, image_filename);
break;
QDErr err;
Rect src_rect = { 0, 0, _image_height, _image_width };
case IP_button_ready:
load_image(_button_ready_image, image_filename);
set_button_range(_button_ready_image);
break;
if (_image != NULL) {
DisposeGWorld(_image);
_image = NULL;
}
if (_image_data != NULL) {
delete[] _image_data;
_image_data = NULL;
}
// Now we need to copy from the RGB source image into the BGRA target image.
int row_stride = _image_width * num_channels;
int new_row_stride = _image_width * 4;
_image_data = new char[new_row_stride * _image_height];
for (int yi = 0; yi < _image_height; ++yi) {
char *dest = _image_data + yi * new_row_stride;
const char *source = data.data() + yi * row_stride;
for (int xi = 0; xi < _image_width; ++xi) {
char r = source[0];
char g = source[1];
char b = source[2];
#ifndef __BIG_ENDIAN__
// Little-endian.
dest[0] = b;
dest[1] = g;
dest[2] = r;
dest[3] = 0xff;
#else // __BIG_ENDIAN__
// Big-endian.
dest[0] = 0xff;
dest[1] = r;
dest[2] = g;
dest[3] = b;
#endif // __BIG_ENDIAN__
source += 3;
dest += 4;
}
}
err = NewGWorldFromPtr(&_image, k32BGRAPixelFormat, &src_rect, 0, 0, 0,
_image_data, new_row_stride);
if (err != noErr) {
nout << " error in NewGWorldFromPtr, called from set_image_filename()\n";
return;
case IP_button_rollover:
load_image(_button_rollover_image, image_filename);
break;
case IP_button_click:
load_image(_button_click_image, image_filename);
break;
}
refresh();
@ -215,24 +169,58 @@ set_install_progress(double install_progress) {
bool P3DOsxSplashWindow::
handle_event(P3D_event_data event) {
EventRecord *er = event._event;
if (er->what == updateEvt) {
paint_window();
// 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 port_save = NULL;
Boolean port_changed = QDSwapPort(out_port, &port_save);
Point pt = er->where;
GlobalToLocal(&pt);
if (port_changed) {
QDSwapPort(port_save, NULL);
}
switch (er->what) {
case updateEvt:
paint_window();
break;
case mouseDown:
set_mouse_data(_mouse_x, _mouse_y, true);
break;
case mouseUp:
set_mouse_data(_mouse_x, _mouse_y, false);
break;
case activateEvt:
_mouse_active = ((er->modifiers & 1) != 0);
break;
default:
break;
}
if (_mouse_active) {
set_mouse_data(pt.h, pt.v, _mouse_down);
}
return false;
}
////////////////////////////////////////////////////////////////////
// Function: P3DOsxSplashWindow::refresh
// Access: Private
// Access: Protected, Virtual
// Description: Requests that the window will be repainted.
////////////////////////////////////////////////////////////////////
void P3DOsxSplashWindow::
refresh() {
if (_toplevel_window != NULL) {
int win_width = _wparams.get_win_width();
int win_height = _wparams.get_win_height();
Rect r = { 0, 0, win_height, win_width };
Rect r = { 0, 0, _win_height, _win_width };
InvalWindowRect(_toplevel_window, &r);
} else {
@ -263,60 +251,34 @@ paint_window() {
portChanged = QDSwapPort(out_port, &portSave);
}
int win_width = _wparams.get_win_width();
int win_height = _wparams.get_win_height();
Rect r = { 0, 0, win_height, win_width };
Rect r = { 0, 0, _win_height, _win_width };
ClipRect(&r);
EraseRect(&r);
if (_image != NULL) {
Rect src_rect = { 0, 0, _image_height, _image_width };
Rect dest_rect;
// Determine the relative size of image and window.
int win_cx = win_width / 2;
int win_cy = win_height / 2;
if (_image_width <= win_width && _image_height <= win_height) {
// The bitmap fits within the window; center it.
// This is the top-left corner of the bitmap in window coordinates.
int p_x = win_cx - _image_width / 2;
int p_y = win_cy - _image_height / 2;
paint_image(out_port, _background_image);
dest_rect.left = p_x;
dest_rect.top = p_y;
dest_rect.right = p_x + _image_width;
dest_rect.bottom = p_y + _image_height;
} else {
// The bitmap is larger than the window; scale it down.
double x_scale = (double)win_width / (double)_image_width;
double y_scale = (double)win_height / (double)_image_height;
double scale = min(x_scale, y_scale);
int sc_width = (int)(_image_width * scale);
int sc_height = (int)(_image_height * scale);
int p_x = win_cx - sc_width / 2;
int p_y = win_cy - sc_height / 2;
dest_rect.left = p_x;
dest_rect.top = p_y;
dest_rect.right = p_x + sc_width;
dest_rect.bottom = p_y + sc_height;
switch (_bstate) {
case BS_hidden:
break;
case BS_ready:
paint_image(out_port, _button_ready_image);
break;
case BS_rollover:
if (!paint_image(out_port, _button_rollover_image)) {
paint_image(out_port, _button_ready_image);
}
CopyBits(GetPortBitMapForCopyBits(_image),
GetPortBitMapForCopyBits(out_port),
&src_rect, &dest_rect, srcCopy, 0);
break;
case BS_click:
if (!paint_image(out_port, _button_click_image)) {
paint_image(out_port, _button_ready_image);
}
break;
}
// Draw the progress bar. We don't draw this bar at all unless we
// have nonzero progress.
int bar_x, bar_y, bar_width, bar_height;
get_bar_placement(win_width, win_height,
bar_x, bar_y, bar_width, bar_height);
get_bar_placement(bar_x, bar_y, bar_width, bar_height);
if (_install_progress != 0.0) {
Rect rbar = { bar_y, bar_x, bar_y + bar_height, bar_x + bar_width };
@ -351,7 +313,7 @@ paint_window() {
int descent = font_info.descent * numer.v / denom.v;
int text_width = TextWidth(_install_label.data(), 0, _install_label.size());
int text_x = (win_width - text_width) / 2;
int text_x = (_win_width - text_width) / 2;
int text_y = bar_y - descent - 8;
Rect rtext = { text_y - ascent - 2, text_x - 2,
@ -370,6 +332,155 @@ paint_window() {
}
}
////////////////////////////////////////////////////////////////////
// Function: P3DOsxSplashWindow::load_image
// Access: Private
// Description: Loads the named image file into an OsxImageData object.
////////////////////////////////////////////////////////////////////
void P3DOsxSplashWindow::
load_image(OsxImageData &image, const string &image_filename) {
image.dump_image();
string string_data;
if (!read_image_data(image, string_data, image_filename)) {
return;
}
// Now we need to copy from the RGB (or RGBA) source image into the
// BGRA target image.
int row_stride = image._width * image._num_channels;
int new_row_stride = image._width * 4;
image._raw_data = new char[new_row_stride * image._height];
for (int yi = 0; yi < image._height; ++yi) {
char *dest = image._raw_data + yi * new_row_stride;
const char *source = string_data.data() + yi * row_stride;
if (image._num_channels == 3) {
// Source is RGB.
for (int xi = 0; xi < image._width; ++xi) {
char r = source[0];
char g = source[1];
char b = source[2];
#ifndef __BIG_ENDIAN__
// Little-endian.
dest[0] = b;
dest[1] = g;
dest[2] = r;
dest[3] = 0xff;
#else // __BIG_ENDIAN__
// Big-endian.
dest[0] = 0xff;
dest[1] = r;
dest[2] = g;
dest[3] = b;
#endif // __BIG_ENDIAN__
source += 3;
dest += 4;
}
} else if (image._num_channels == 4) {
// Source is RGBA.
for (int xi = 0; xi < image._width; ++xi) {
char r = source[0];
char g = source[1];
char b = source[2];
char a = source[3];
#ifndef __BIG_ENDIAN__
// Little-endian.
dest[0] = b;
dest[1] = g;
dest[2] = r;
dest[3] = a;
#else // __BIG_ENDIAN__
// Big-endian.
dest[0] = a;
dest[1] = r;
dest[2] = g;
dest[3] = b;
#endif // __BIG_ENDIAN__
source += 4;
dest += 4;
}
}
}
image._data =
CFDataCreateWithBytesNoCopy(NULL, (const UInt8 *)image._raw_data,
image._height * new_row_stride, kCFAllocatorNull);
image._provider = CGDataProviderCreateWithCFData(image._data);
image._color_space = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
image._image =
CGImageCreate(image._width, image._height, 8, 32,
new_row_stride, image._color_space,
kCGImageAlphaFirst | kCGBitmapByteOrder32Little,
image._provider, NULL, false, kCGRenderingIntentDefault);
}
////////////////////////////////////////////////////////////////////
// Function: P3DOsxSplashWindow::paint_image
// Access: Private
// Description: Draws the indicated image, centered within the
// window. Returns true on success, false if the image
// is not defined.
////////////////////////////////////////////////////////////////////
bool P3DOsxSplashWindow::
paint_image(GrafPtr out_port, const OsxImageData &image) {
if (image._image == NULL) {
return false;
}
CGContextRef context;
QDErr err = QDBeginCGContext(out_port, &context);
if (err != noErr) {
nout << "Error: QDBeginCGContext\n";
return false;
}
// 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);
// Determine the relative size of image and window.
int win_cx = _win_width / 2;
int win_cy = _win_height / 2;
if (image._width <= _win_width && image._height <= _win_height) {
// The bitmap fits within the window; center it.
// This is the top-left corner of the bitmap in window coordinates.
int p_x = win_cx - image._width / 2;
int p_y = win_cy - image._height / 2;
rect.origin.x += p_x;
rect.origin.y += p_y;
rect.size.width = image._width;
rect.size.height = image._height;
} else {
// The bitmap is larger than the window; scale it down.
double x_scale = (double)_win_width / (double)image._width;
double y_scale = (double)_win_height / (double)image._height;
double scale = min(x_scale, y_scale);
int sc_width = (int)(image._width * scale);
int sc_height = (int)(image._height * scale);
int p_x = win_cx - sc_width / 2;
int p_y = win_cy - sc_height / 2;
rect.origin.x += p_x;
rect.origin.y += p_y;
rect.size.width = sc_width;
rect.size.height = sc_height;
}
CGContextDrawImage(context, rect, image._image);
QDEndCGContext(out_port, &context);
return true;
}
////////////////////////////////////////////////////////////////////
// Function: P3DOsxSplashWindow::st_event_callback
// Access: Private, Static
@ -402,11 +513,75 @@ event_callback(EventHandlerCallRef my_handler, EventRef event) {
case kEventWindowDrawContent:
paint_window();
result = noErr;
};
break;
case kEventClassMouse:
switch (kind) {
case kEventMouseUp:
set_mouse_data(_mouse_x, _mouse_y, false);
break;
case kEventMouseDown:
set_mouse_data(_mouse_x, _mouse_y, true);
break;
case kEventMouseMoved:
case kEventMouseDragged:
{
Point point;
GetEventParameter(event, kEventParamMouseLocation, typeQDPoint, NULL,
sizeof(Point), NULL, (void *)&point);
GrafPtr port = _wparams.get_parent_window()._port;
if (_toplevel_window != NULL) {
port = GetWindowPort(_toplevel_window);
}
GrafPtr port_save = NULL;
Boolean port_changed = QDSwapPort(port, &port_save);
GlobalToLocal(&point);
if (port_changed) {
QDSwapPort(port_save, NULL);
}
set_mouse_data(point.h, point.v, _mouse_down);
}
break;
break;
}
}
return result;
}
////////////////////////////////////////////////////////////////////
// Function: P3DOsxSplashWindow::OsxImageData::dump_image
// Access: Public
// Description: Frees the previous image data.
////////////////////////////////////////////////////////////////////
void P3DOsxSplashWindow::OsxImageData::
dump_image() {
if (_image != NULL) {
CGImageRelease(_image);
_image = NULL;
}
if (_color_space != NULL) {
CGColorSpaceRelease(_color_space);
_color_space = NULL;
}
if (_provider != NULL) {
CGDataProviderRelease(_provider);
_provider = NULL;
}
if (_data != NULL) {
CFRelease(_data);
_data = NULL;
}
if (_raw_data != NULL) {
delete[] _raw_data;
_raw_data = NULL;
}
}
#endif // __APPLE__

View File

@ -35,15 +35,21 @@ public:
virtual void set_wparams(const P3DWindowParams &wparams);
virtual void set_image_filename(const string &image_filename,
bool image_filename_temp);
ImagePlacement image_placement);
virtual void set_install_label(const string &install_label);
virtual void set_install_progress(double install_progress);
virtual bool handle_event(P3D_event_data event);
protected:
virtual void refresh();
private:
void refresh();
void paint_window();
class OsxImageData;
void load_image(OsxImageData &image, const string &image_filename);
bool paint_image(GrafPtr out_port, const OsxImageData &image);
static pascal OSStatus
st_event_callback(EventHandlerCallRef my_handler, EventRef event,
@ -52,13 +58,32 @@ private:
private:
bool _got_wparams;
GWorldPtr _image;
char *_image_data;
int _image_height, _image_width;
class OsxImageData : public ImageData {
public:
inline OsxImageData();
inline ~OsxImageData();
void dump_image();
char *_raw_data;
CFDataRef _data;
CGDataProviderRef _provider;
CGColorSpaceRef _color_space;
CGImageRef _image;
};
OsxImageData _background_image;
OsxImageData _button_ready_image;
OsxImageData _button_rollover_image;
OsxImageData _button_click_image;
string _install_label;
double _install_progress;
// Used to track the mouse within the window in the embedded case.
bool _mouse_active;
// Filled only in the non-embedded case.
WindowRef _toplevel_window;
};

View File

@ -41,23 +41,6 @@ get_download_size() const {
return _download_size;
}
////////////////////////////////////////////////////////////////////
// Function: P3DPackage::activate_download
// Access: Public
// Description: Authorizes the package to begin downloading and
// unpacking the meat of its data. Until this is
// called, the package will download its file
// information only, and then wait.
////////////////////////////////////////////////////////////////////
inline void P3DPackage::
activate_download() {
_allow_data_download = true;
if (_info_ready) {
begin_data_download();
}
}
////////////////////////////////////////////////////////////////////
// Function: P3DPackage::get_ready
// Access: Public
@ -140,6 +123,17 @@ get_package_display_name() const {
return _package_display_name;
}
////////////////////////////////////////////////////////////////////
// Function: P3DPackage::get_xconfig
// Access: Public
// Description: Returns the <config> entry of the package desc file,
// if any, or NULL if it was not present.
////////////////////////////////////////////////////////////////////
inline const TiXmlElement *P3DPackage::
get_xconfig() const {
return _xconfig;
}
////////////////////////////////////////////////////////////////////
// Function: P3DPackage::get_desc_file_pathname
// Access: Public

View File

@ -54,6 +54,7 @@ P3DPackage(P3DHost *host, const string &package_name,
// file, instead of an xml file and a multifile to unpack.
_package_solo = false;
_xconfig = NULL;
_temp_contents_file = NULL;
_info_ready = false;
@ -78,6 +79,11 @@ P3DPackage::
// Tell any pending callbacks that we're no good any more.
report_done(false);
if (_xconfig != NULL) {
delete _xconfig;
_xconfig = NULL;
}
// Cancel any pending download.
if (_active_download != NULL) {
_active_download->cancel();
@ -93,6 +99,38 @@ P3DPackage::
assert(_instances.empty());
}
////////////////////////////////////////////////////////////////////
// Function: P3DPackage::activate_download
// Access: Public
// Description: Authorizes the package to begin downloading and
// unpacking the meat of its data. Until this is
// called, the package will download its file
// information only, and then wait.
////////////////////////////////////////////////////////////////////
void P3DPackage::
activate_download() {
if (_allow_data_download) {
// Already activated.
}
_allow_data_download = true;
if (_ready) {
// If we've already been downloaded, we can report that now.
Instances::iterator ii;
for (ii = _instances.begin(); ii != _instances.end(); ++ii) {
(*ii)->report_package_done(this, true);
}
} else {
// Otherwise, if we've already got the desc file, then start the
// download.
if (_info_ready) {
begin_data_download();
}
}
}
////////////////////////////////////////////////////////////////////
// Function: P3DPackage::add_instance
// Access: Public
@ -358,6 +396,9 @@ got_desc_file(TiXmlDocument *doc, bool freshly_downloaded) {
if (display_name_cstr != NULL) {
_package_display_name = display_name_cstr;
}
// Save the config entry within this class for others to query.
_xconfig = (TiXmlElement *)xconfig->Clone();
}
}

View File

@ -48,7 +48,7 @@ public:
inline bool get_info_ready() const;
inline size_t get_download_size() const;
inline void activate_download();
void activate_download();
inline bool get_ready() const;
inline bool get_failed() const;
inline P3DHost *get_host() const;
@ -56,6 +56,7 @@ public:
inline const string &get_package_name() const;
inline const string &get_package_version() const;
inline const string &get_package_display_name() const;
inline const TiXmlElement *get_xconfig() const;
inline const string &get_desc_file_pathname() const;
inline string get_archive_file_pathname() const;
@ -119,6 +120,7 @@ private:
string _package_display_name;
string _package_fullname;
string _package_dir;
TiXmlElement *_xconfig;
P3DTemporaryFile *_temp_contents_file;

View File

@ -109,6 +109,7 @@ P3DPythonRun::
// Close the write pipe, so the parent process will terminate us.
_pipe_write.close();
// Shut down Python and Panda.
Py_Finalize();
join_read_thread();

View File

@ -229,15 +229,11 @@ start_instance(P3DInstance *inst) {
send_command(doc);
inst->send_browser_script_object();
if (inst->get_packages_ready()) {
// If it's ready immediately, go ahead and start.
start_p3dpython(inst);
// We shouldn't have gotten here unless the instance is fully
// downloaded and ready to start.
assert(inst->get_packages_ready());
} else {
// Otherwise, wait for the instance to download itself. We'll
// automatically get a callback to report_packages_done() when
// it's done.
}
start_p3dpython(inst);
}
////////////////////////////////////////////////////////////////////
@ -641,18 +637,6 @@ drop_p3dobj(int object_id) {
}
}
////////////////////////////////////////////////////////////////////
// Function: P3DSession::report_packages_done
// Access: Private
// Description: Notified when a child instance is fully downloaded.
////////////////////////////////////////////////////////////////////
void P3DSession::
report_packages_done(P3DInstance *inst, bool success) {
if (success) {
start_p3dpython(inst);
}
}
////////////////////////////////////////////////////////////////////
// Function: P3DSession::start_p3dpython
// Access: Private

View File

@ -61,7 +61,6 @@ public:
void drop_p3dobj(int object_id);
private:
void report_packages_done(P3DInstance *inst, bool success);
void start_p3dpython(P3DInstance *inst);
void spawn_read_thread();

View File

@ -32,3 +32,15 @@ inline const P3DWindowParams &P3DSplashWindow::
get_wparams() const {
return _wparams;
}
////////////////////////////////////////////////////////////////////
// Function: P3DSplashWindow::ImageData::Constructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
inline P3DSplashWindow::ImageData::
ImageData() {
_width = 0;
_height = 0;
_num_channels = 0;
}

View File

@ -14,6 +14,8 @@
#include "p3dSplashWindow.h"
// Stuff to use libpng.
#include <png.h>
// Stuff to use libjpeg.
extern "C" {
@ -52,6 +54,16 @@ P3DSplashWindow(P3DInstance *inst) :
_fparams(inst->get_fparams()),
_wparams(inst->get_wparams())
{
_button_width = 0;
_button_height = 0;
_button_x = 0;
_button_y = 0;
_button_active = false;
_mouse_x = -1;
_mouse_y = -1;
_mouse_down = false;
_button_depressed = false;
_bstate = BS_hidden;
}
////////////////////////////////////////////////////////////////////
@ -73,22 +85,23 @@ P3DSplashWindow::
void P3DSplashWindow::
set_wparams(const P3DWindowParams &wparams) {
_wparams = wparams;
_win_width = _wparams.get_win_width();
_win_height = _wparams.get_win_height();
}
////////////////////////////////////////////////////////////////////
// Function: P3DSplashWindow::set_image_filename
// Access: Public, Virtual
// Description: Specifies the name of a JPEG image file that is
// displayed in the center of the splash window. If
// image_filename_temp is true, the file is immediately
// deleted after it has been read.
// Description: Specifies the name of a JPEG or PNG image file that
// is displayed in the center of the splash window.
//
// image_placement defines the specific context in which
// this particular image is displayed. It is similar to
// the P3DInstance's image_type, but it is a more
// specific, lower-level usage.
////////////////////////////////////////////////////////////////////
void P3DSplashWindow::
set_image_filename(const string &image_filename,
bool image_filename_temp) {
if (image_filename_temp) {
unlink(image_filename.c_str());
}
set_image_filename(const string &image_filename, ImagePlacement image_placement) {
}
////////////////////////////////////////////////////////////////////
@ -122,35 +135,84 @@ handle_event(P3D_event_data event) {
return false;
}
////////////////////////////////////////////////////////////////////
// Function: P3DSplashWindow::set_button_active
// Access: Public
// Description: Sets whether the button should be visible and active
// (true) or invisible and inactive (false). If active,
// the button image will be displayed in the window, and
// a click event will be generated when the user clicks
// the button.
////////////////////////////////////////////////////////////////////
void P3DSplashWindow::
set_button_active(bool flag) {
_button_active = flag;
// Now light up the button according to the current mouse position.
set_mouse_data(_mouse_x, _mouse_y, _mouse_down);
}
////////////////////////////////////////////////////////////////////
// Function: P3DSplashWindow::read_image
// Function: P3DSplashWindow::read_image_data
// Access: Protected
// Description: Reads the image filename and sets image parameters
// height, width, num_channels, and data. Returns true
// on success, false on failure. If image_filename_temp
// is true, the file will be deleted after reading.
// width, height, num_channels, and data. Returns true
// on success, false on failure.
////////////////////////////////////////////////////////////////////
bool P3DSplashWindow::
read_image(const string &image_filename, bool image_filename_temp,
int &height, int &width, int &num_channels,
string &data) {
height = 0;
width = 0;
num_channels = 0;
read_image_data(ImageData &image, string &data,
const string &image_filename) {
image._width = 0;
image._height = 0;
image._num_channels = 0;
data.clear();
// We currently only support JPEG images. Maybe that's all we'll
// ever support.
// We only support JPEG or PNG images.
FILE *fp = fopen(image_filename.c_str(), "rb");
if (fp == NULL) {
nout << "Couldn't open splash file image: " << image_filename << "\n";
if (image_filename_temp) {
unlink(image_filename.c_str());
}
return false;
}
// Check the magic number to determine which image type we have.
static const size_t magic_number_len = 2;
char magic_number[magic_number_len];
if (fread(magic_number, 1, magic_number_len, fp) != magic_number_len) {
nout << "Empty file: " << image_filename << "\n";
return false;
}
// Rewind to re-read the magic number below.
fseek(fp, 0, SEEK_SET);
bool result = false;
if ((char)magic_number[0] == (char)0xff &&
(char)magic_number[1] == (char)0xd8) {
// It's a jpeg image.
result = read_image_data_jpeg(image, data, fp, image_filename);
} else if (png_sig_cmp((png_bytep)magic_number, 0, magic_number_len) == 0) {
// It's a PNG image.
result = read_image_data_png(image, data, fp, image_filename);
} else {
nout << "Neither a JPEG nor a PNG image: " << image_filename << "\n";
result = false;
}
fclose(fp);
return result;
}
////////////////////////////////////////////////////////////////////
// Function: P3DSplashWindow::read_image_data_jpeg
// Access: Protected
// Description: Reads the image filename and sets image parameters
// width, height, num_channels, and data. Returns true
// on success, false on failure.
////////////////////////////////////////////////////////////////////
bool P3DSplashWindow::
read_image_data_jpeg(ImageData &image, string &data,
FILE *fp, const string &image_filename) {
// We set up the normal JPEG error routines, then override error_exit.
struct jpeg_decompress_struct cinfo;
@ -167,15 +229,10 @@ read_image(const string &image_filename, bool image_filename_temp,
// We need to clean up the JPEG object, close the input file, and return.
jpeg_destroy_decompress(&cinfo);
fclose(fp);
if (buffer != NULL) {
delete[] buffer;
}
if (image_filename_temp) {
unlink(image_filename.c_str());
}
return false;
}
@ -190,13 +247,13 @@ read_image(const string &image_filename, bool image_filename_temp,
jpeg_start_decompress(&cinfo);
width = cinfo.output_width;
height = cinfo.output_height;
num_channels = cinfo.output_components;
image._width = cinfo.output_width;
image._height = cinfo.output_height;
image._num_channels = cinfo.output_components;
int row_stride = width * num_channels;
int row_stride = image._width * image._num_channels;
size_t buffer_size = height * row_stride;
size_t buffer_size = image._height * row_stride;
buffer = new JSAMPLE[buffer_size];
JSAMPLE *buffer_end = buffer + buffer_size;
@ -209,17 +266,99 @@ read_image(const string &image_filename, bool image_filename_temp,
jpeg_finish_decompress(&cinfo);
fclose(fp);
if (image_filename_temp) {
unlink(image_filename.c_str());
}
data.append((const char *)buffer, buffer_size);
delete[] buffer;
return true;
}
////////////////////////////////////////////////////////////////////
// Function: P3DSplashWindow::read_image_data_png
// Access: Protected
// Description: Reads the image filename and sets image parameters
// width, height, num_channels, and data. Returns true
// on success, false on failure.
////////////////////////////////////////////////////////////////////
bool P3DSplashWindow::
read_image_data_png(ImageData &image, string &data,
FILE *fp, const string &image_filename) {
cerr << "read_image_data_png\n";
png_structp png;
png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL,
png_error, png_warning);
if (png == NULL) {
return false;
}
png_infop info;
info = png_create_info_struct(png);
if (info == NULL) {
png_destroy_read_struct(&png, NULL, NULL);
return false;
}
jmp_buf jmpbuf;
if (setjmp(jmpbuf)) {
// This is the ANSI C way to handle exceptions. If setjmp(),
// above, returns true, it means that libpng detected an exception
// while executing the code that reads the header info, below.
png_destroy_read_struct(&png, &info, NULL);
return false;
}
png_init_io(png, fp);
int transforms = PNG_TRANSFORM_STRIP_16 | PNG_TRANSFORM_PACKING | PNG_TRANSFORM_EXPAND | PNG_TRANSFORM_SHIFT;
// transforms |= PNG_TRANSFORM_STRIP_ALPHA;
png_read_png(png, info, transforms, NULL);
png_uint_32 width;
png_uint_32 height;
int bit_depth;
int color_type;
png_get_IHDR(png, info, &width, &height,
&bit_depth, &color_type, NULL, NULL, NULL);
cerr
<< "width = " << width << " height = " << height << " bit_depth = "
<< bit_depth << " color_type = " << color_type << "\n";
image._width = width;
image._height = height;
switch (color_type) {
case PNG_COLOR_TYPE_RGB:
cerr
<< "PNG_COLOR_TYPE_RGB\n";
image._num_channels = 3;
break;
case PNG_COLOR_TYPE_RGB_ALPHA:
cerr
<< "PNG_COLOR_TYPE_RGB_ALPHA\n";
image._num_channels = 4;
break;
default:
cerr
<< "Unsupported color type: " << color_type << "\n";
png_destroy_read_struct(&png, &info, NULL);
return false;
}
int row_stride = image._width * image._num_channels;
png_bytep *row_pointers = png_get_rows(png, info);
for (int yi = 0; yi < image._height; ++yi) {
data.append((const char *)row_pointers[yi], row_stride);
}
png_destroy_read_struct(&png, &info, NULL);
cerr << "successfully read\n";
return true;
}
////////////////////////////////////////////////////////////////////
// Function: P3DSplashWindow::get_bar_placement
// Access: Protected
@ -227,11 +366,128 @@ read_image(const string &image_filename, bool image_filename_temp,
// rectangle in which to place the progress bar.
////////////////////////////////////////////////////////////////////
void P3DSplashWindow::
get_bar_placement(int win_width, int win_height,
int &bar_x, int &bar_y,
get_bar_placement(int &bar_x, int &bar_y,
int &bar_width, int &bar_height) {
bar_width = min((int)(win_width * 0.6), 400);
bar_height = min((int)(win_height * 0.1), 24);
bar_x = (win_width - bar_width) / 2;
bar_y = (win_height - bar_height * 2);
bar_width = min((int)(_win_width * 0.6), 400);
bar_height = min((int)(_win_height * 0.1), 24);
bar_x = (_win_width - bar_width) / 2;
bar_y = (_win_height - bar_height * 2);
}
////////////////////////////////////////////////////////////////////
// Function: P3DSplashWindow::set_button_range
// Access: Protected
// Description: Specifies the image that contains the "ready" button
// image, which in turn determines the clickable
// dimensions of the button within the window.
////////////////////////////////////////////////////////////////////
void P3DSplashWindow::
set_button_range(const ImageData &image) {
// The clickable area has a certain minimum size, even if it's a
// very small image.
_button_width = max(image._width, 64);
_button_height = max(image._height, 64);
// But it can't be larger than the window itself.
_button_width = min(_button_width, _win_width);
_button_height = min(_button_height, _win_height);
// Compute the top-left corner of the button image in window
// coordinates.
_button_x = (_win_width - _button_width) / 2;
_button_y = (_win_height - _button_height) / 2;
}
////////////////////////////////////////////////////////////////////
// Function: P3DSplashWindow::set_mouse_data
// Access: Protected
// Description: Intended to be called by the subclasses as the mouse
// is tracked through the window, whether the button is
// currently active or not. This updates the internal
// state of the mouse pointer, and also (if the button
// is active) updates the button state appropriately,
// and generates the click event when the mouse button
// transitions from down to up over the button area.
////////////////////////////////////////////////////////////////////
void P3DSplashWindow::
set_mouse_data(int mouse_x, int mouse_y, bool mouse_down) {
ButtonState orig_bstate = _bstate;
_mouse_x = mouse_x;
_mouse_y = mouse_y;
_mouse_down = mouse_down;
if (!_button_active) {
// The button isn't active, so it's hidden, regardless of the
// mouse position.
_bstate = BS_hidden;
} else {
// Is the mouse pointer within the button region?
bool is_within = (_mouse_x >= _button_x && _mouse_x < _button_x + _button_width &&
_mouse_y >= _button_y && _mouse_y < _button_y + _button_height);
if (is_within) {
// The mouse is within the button region. This means either
// click or rollover state, according to the mouse button.
if (_mouse_down) {
// We only count it mouse-down if you've clicked down while
// over the button (or you never released the button since the
// last time you clicked down). Clicking down somewhere else
// and dragging over the button doesn't count.
if (orig_bstate == BS_rollover || _button_depressed) {
_button_depressed = true;
_bstate = BS_click;
}
} else {
_button_depressed = false;
if (orig_bstate == BS_click) {
// If we just transitioned from mouse down to mouse up, this
// means a click. And the button automatically hides itself
// after a successful click.
_bstate = BS_hidden;
_button_active = false;
button_click_detected();
} else {
_bstate = BS_rollover;
}
}
} else {
// The mouse is not within the button region. This means ready
// state.
_bstate = BS_ready;
if (!_mouse_down) {
_button_depressed = false;
}
}
}
if (orig_bstate != _bstate) {
// If we've changed button states, we need to refresh the window.
refresh();
}
}
////////////////////////////////////////////////////////////////////
// Function: P3DSplashWindow::button_click_detected
// Access: Protected, Virtual
// Description: Called when a button click by the user is detected in
// set_mouse_data(), this method simply turns around and
// notifies the instance. It's a virtual method to give
// subclasses a chance to redirect this message to the
// main thread or process, as necessary.
////////////////////////////////////////////////////////////////////
void P3DSplashWindow::
button_click_detected() {
assert(_inst != NULL);
_inst->splash_button_clicked();
}
////////////////////////////////////////////////////////////////////
// Function: P3DSplashWindow::refresh
// Access: Protected, Virtual
// Description: Requests that the window will be repainted. This may
// or may not be implemented for a particular
// specialization of P3DSplashWindow.
////////////////////////////////////////////////////////////////////
void P3DSplashWindow::
refresh() {
}

View File

@ -42,26 +42,72 @@ public:
virtual void set_wparams(const P3DWindowParams &wparams);
inline const P3DWindowParams &get_wparams() const;
enum ImagePlacement {
IP_background,
IP_button_ready,
IP_button_rollover,
IP_button_click,
IP_none
};
virtual void set_image_filename(const string &image_filename,
bool image_filename_temp);
ImagePlacement image_placement);
virtual void set_install_label(const string &install_label);
virtual void set_install_progress(double install_progress);
virtual bool handle_event(P3D_event_data event);
void setup_splash_image();
void set_button_active(bool flag);
protected:
bool read_image(const string &image_filename, bool image_filename_temp,
int &height, int &width, int &num_channels, string &data);
void get_bar_placement(int win_width, int win_height,
int &bar_x, int &bar_y,
// This ImageData base class provides minimal functionality for
// storing a loaded image. Most of the real meat of this class is
// provided by the various subclasses.
class ImageData {
public:
inline ImageData();
int _width, _height, _num_channels;
};
bool read_image_data(ImageData &image, string &data,
const string &image_filename);
bool read_image_data_jpeg(ImageData &image, string &data,
FILE *fp, const string &image_filename);
bool read_image_data_png(ImageData &image, string &data,
FILE *fp, const string &image_filename);
void get_bar_placement(int &bar_x, int &bar_y,
int &bar_width, int &bar_height);
void set_button_range(const ImageData &image);
void set_mouse_data(int mouse_x, int mouse_y, bool mouse_down);
virtual void button_click_detected();
virtual void refresh();
protected:
P3DInstance *_inst;
P3DFileParams _fparams;
P3DWindowParams _wparams;
int _win_width, _win_height;
// The region of the window for accepting button clicks.
int _button_width, _button_height;
int _button_x, _button_y;
bool _button_active;
// Tracking the mouse pointer within the window, for the purposes of
// clicking the button.
int _mouse_x, _mouse_y;
bool _mouse_down;
bool _button_depressed;
// The current visual state of the button.
enum ButtonState {
BS_hidden,
BS_ready,
BS_rollover,
BS_click,
};
ButtonState _bstate;
};
#include "p3dSplashWindow.I"

View File

@ -35,7 +35,6 @@ P3DWinSplashWindow(P3DInstance *inst) :
_text_label = NULL;
_thread_running = false;
_image_filename_changed = false;
_image_filename_temp = false;
_install_label_changed = false;
_install_progress = 0.0;
@ -74,18 +73,14 @@ set_wparams(const P3DWindowParams &wparams) {
// Function: P3DWinSplashWindow::set_image_filename
// Access: Public, Virtual
// Description: Specifies the name of a JPEG image file that is
// displayed in the center of the splash window. If
// image_filename_temp is true, the file is immediately
// deleted after it has been read.
// displayed in the center of the splash window.
////////////////////////////////////////////////////////////////////
void P3DWinSplashWindow::
set_image_filename(const string &image_filename,
bool image_filename_temp) {
set_image_filename(const string &image_filename, ImagePlacement image_placement) {
nout << "image_filename = " << image_filename << ", thread_id = " << _thread_id << "\n";
ACQUIRE_LOCK(_install_lock);
if (_image_filename != image_filename) {
_image_filename = image_filename;
_image_filename_temp = image_filename_temp;
_image_filename_changed = true;
}
RELEASE_LOCK(_install_lock);
@ -264,7 +259,7 @@ thread_run() {
ACQUIRE_LOCK(_install_lock);
double install_progress = _install_progress;
if (_image_filename_changed) {
update_image_filename(_image_filename, _image_filename_temp);
update_image_filename(_image_filename);
}
_image_filename_changed = false;
if (_install_label_changed && _progress_bar != NULL) {
@ -395,14 +390,8 @@ make_progress_bar() {
DWORD window_style =
WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS;
RECT rect;
GetClientRect(_hwnd, &rect);
int win_width = rect.right - rect.left;
int win_height = rect.bottom - rect.top;
int bar_x, bar_y, bar_width, bar_height;
get_bar_placement(win_width, win_height,
bar_x, bar_y, bar_width, bar_height);
get_bar_placement(bar_x, bar_y, bar_width, bar_height);
_progress_bar =
CreateWindowEx(0, PROGRESS_CLASS, "", window_style,
@ -450,18 +439,12 @@ update_install_label(const string &install_label) {
DWORD window_style =
SS_OWNERDRAW | WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS;
RECT rect;
GetClientRect(_hwnd, &rect);
int win_width = rect.right - rect.left;
int win_height = rect.bottom - rect.top;
int bar_x, bar_y, bar_width, bar_height;
get_bar_placement(win_width, win_height,
bar_x, bar_y, bar_width, bar_height);
get_bar_placement(bar_x, bar_y, bar_width, bar_height);
int text_width = text_size.cx + 4;
int text_height = text_size.cy + 2;
int text_x = (win_width - text_width) / 2;
int text_x = (_win_width - text_width) / 2;
int text_y = bar_y - text_height - 2;
_text_label = CreateWindowEx(0, "STATIC", text, window_style,
@ -478,7 +461,7 @@ update_install_label(const string &install_label) {
// sub-thread.
////////////////////////////////////////////////////////////////////
void P3DWinSplashWindow::
update_image_filename(const string &image_filename, bool image_filename_temp) {
update_image_filename(const string &image_filename) {
// Clear the old image.
if (_bitmap != NULL) {
DeleteObject(_bitmap);
@ -491,12 +474,16 @@ update_image_filename(const string &image_filename, bool image_filename_temp) {
// Go read the image.
string data;
int num_channels;
if (!read_image(image_filename, image_filename_temp,
_bitmap_height, _bitmap_width, num_channels, data)) {
ImageData image;
if (!read_image_data(image, data, image_filename)) {
return;
}
// Temp legacy support.
_bitmap_width = image._width;
_bitmap_height = image._height;
int num_channels =image._num_channels;
// Massage the data into Windows' conventions.
int row_stride = _bitmap_width * num_channels;
int new_row_stride = (_bitmap_width * 3);
@ -591,8 +578,8 @@ void P3DWinSplashWindow::
paint_window(HDC dc) {
RECT rect;
GetClientRect(_hwnd, &rect);
int win_width = rect.right - rect.left;
int win_height = rect.bottom - rect.top;
_win_width = rect.right - rect.left;
_win_height = rect.bottom - rect.top;
if (_bitmap != NULL) {
// Paint the background splash image.
@ -603,10 +590,10 @@ paint_window(HDC dc) {
int bm_width = _bitmap_width;
int bm_height = _bitmap_height;
int win_cx = win_width / 2;
int win_cy = win_height / 2;
int win_cx = _win_width / 2;
int win_cy = _win_height / 2;
if (bm_width <= win_width && bm_height <= win_height) {
if (bm_width <= _win_width && bm_height <= _win_height) {
// The bitmap fits within the window; center it.
// This is the top-left corner of the bitmap in window coordinates.
@ -621,8 +608,8 @@ paint_window(HDC dc) {
} else {
// The bitmap is larger than the window; scale it down.
double x_scale = (double)win_width / (double)bm_width;
double y_scale = (double)win_height / (double)bm_height;
double x_scale = (double)_win_width / (double)bm_width;
double y_scale = (double)_win_height / (double)bm_height;
double scale = min(x_scale, y_scale);
int sc_width = (int)(bm_width * scale);
int sc_height = (int)(bm_height * scale);

View File

@ -36,7 +36,7 @@ public:
virtual void set_wparams(const P3DWindowParams &wparams);
virtual void set_image_filename(const string &image_filename,
bool image_filename_temp);
ImagePlacement image_placement);
virtual void set_install_label(const string &install_label);
virtual void set_install_progress(double install_progress);
@ -55,8 +55,7 @@ private:
void make_window();
void make_progress_bar();
void update_install_label(const string &install_label);
void update_image_filename(const string &image_filename,
bool image_filename_temp);
void update_image_filename(const string &image_filename);
void close_window();
void paint_window(HDC dc);
@ -67,7 +66,6 @@ private:
bool _got_install;
bool _image_filename_changed;
string _image_filename;
bool _image_filename_temp;
bool _install_label_changed;
string _install_label;
double _install_progress;

View File

@ -58,7 +58,6 @@ P3DX11SplashWindow(P3DInstance *inst) :
_resized_height = 0;
_graphics_context = None;
_bar_context = None;
_image_filename_temp = false;
_install_progress = 0.0;
}
@ -92,13 +91,10 @@ set_wparams(const P3DWindowParams &wparams) {
// Function: P3DX11SplashWindow::set_image_filename
// Access: Public, Virtual
// Description: Specifies the name of a JPEG image file that is
// displayed in the center of the splash window. If
// image_filename_temp is true, the file is immediately
// deleted after it has been read.
// displayed in the center of the splash window.
////////////////////////////////////////////////////////////////////
void P3DX11SplashWindow::
set_image_filename(const string &image_filename,
bool image_filename_temp) {
set_image_filename(const string &image_filename, ImagePlacement image_placement) {
if (_subprocess_pid == -1) {
return;
}
@ -107,7 +103,6 @@ set_image_filename(const string &image_filename,
TiXmlElement *xcommand = new TiXmlElement("command");
xcommand->SetAttribute("cmd", "set_image_filename");
xcommand->SetAttribute("image_filename", image_filename);
xcommand->SetAttribute("image_filename_temp", (int)image_filename_temp);
doc.LinkEndChild(xcommand);
write_xml(_pipe_write, &doc, nout);
@ -367,7 +362,7 @@ subprocess_run() {
}
if (_image_filename != prev_image_filename) {
update_image_filename(_image_filename, _image_filename_temp);
update_image_filename(_image_filename);
needs_redraw = true;
prev_image_filename = _image_filename;
}
@ -401,8 +396,7 @@ subprocess_run() {
// some nonzero progress.
if (_install_progress != 0.0) {
int bar_x, bar_y, bar_width, bar_height;
get_bar_placement(_width, _height,
bar_x, bar_y, bar_width, bar_height);
get_bar_placement(bar_x, bar_y, bar_width, bar_height);
if (needs_draw_label) {
int text_width = _install_label.size() * 6;
@ -485,12 +479,9 @@ receive_command() {
} else if (strcmp(cmd, "set_image_filename") == 0) {
const char *str = xcommand->Attribute("image_filename");
int image_filename_temp = 0;
xcommand->Attribute("image_filename_temp", &image_filename_temp);
if (str != NULL) {
if (_image_filename != string(str)) {
_image_filename = str;
_image_filename_temp = image_filename_temp;
}
}
@ -570,11 +561,11 @@ make_window() {
int x = _wparams.get_win_x();
int y = _wparams.get_win_y();
_width = 320;
_height = 240;
_win_width = 320;
_win_height = 240;
if (_wparams.get_win_width() != 0 && _wparams.get_win_height() != 0) {
_width = _wparams.get_win_width();
_height = _wparams.get_win_height();
_win_width = _wparams.get_win_width();
_win_height = _wparams.get_win_height();
}
Window parent = 0;
@ -602,7 +593,7 @@ make_window() {
assert(_display != NULL);
assert(parent != None);
_window = XCreateSimpleWindow(_display, parent, x, y, _width, _height, 0, 0, -1);
_window = XCreateSimpleWindow(_display, parent, x, y, _win_width, _win_height, 0, 0, -1);
XMapWindow(_display, _window);
}
@ -698,7 +689,7 @@ close_window() {
// sub-thread.
////////////////////////////////////////////////////////////////////
void P3DX11SplashWindow::
update_image_filename(const string &image_filename, bool image_filename_temp) {
update_image_filename(const string &image_filename) {
// Clear the old image.
if (_image != NULL) {
XDestroyImage(_image);
@ -711,12 +702,16 @@ update_image_filename(const string &image_filename, bool image_filename_temp) {
// Go read the image.
string data;
int num_channels;
if (!read_image(image_filename, image_filename_temp,
_image_height, _image_width, num_channels, data)) {
ImageData image;
if (!read_image_data(image, data, image_filename)) {
return;
}
// Temp legacy support.
_image_width = image._width;
_image_height = image._height;
int num_channels =image._num_channels;
Visual *dvisual = DefaultVisual(_display, _screen);
double r_ratio = dvisual->red_mask / 255.0;
double g_ratio = dvisual->green_mask / 255.0;

View File

@ -37,7 +37,7 @@ public:
virtual void set_wparams(const P3DWindowParams &wparams);
virtual void set_image_filename(const string &image_filename,
bool image_filename_temp);
ImagePlacement image_placement);
virtual void set_install_label(const string &install_label);
virtual void set_install_progress(double install_progress);
@ -59,19 +59,16 @@ private:
void redraw();
void make_window();
void setup_gc();
void update_image_filename(const string &image_filename,
bool image_filename_temp);
void update_image_filename(const string &image_filename);
void close_window();
private:
// Data members that are stored in the subprocess.
bool _subprocess_continue;
HandleStream _pipe_read;
int _width, _height;
bool _own_display;
string _image_filename;
bool _image_filename_temp;
string _install_label;
double _install_progress;