2
0
David Rose 16 жил өмнө
parent
commit
76679a765f

+ 33 - 8
direct/src/p3d/panda3d.pdef

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

+ 4 - 4
direct/src/plugin/Sources.pp

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

+ 35 - 0
direct/src/plugin/p3dInstance.I

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

+ 286 - 76
direct/src/plugin/p3dInstance.cxx

@@ -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,7 +942,22 @@ 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);
+
+  // 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);
+    }
+  }
+}
 
-  } 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;
+////////////////////////////////////////////////////////////////////
+//     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,14 +1503,40 @@ 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);
+  }
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: P3DInstance::report_instance_progress
 //       Access: Private
@@ -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);
     }
   }
 }

+ 44 - 5
direct/src/plugin/p3dInstance.h

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

+ 25 - 0
direct/src/plugin/p3dOsxSplashWindow.I

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

+ 299 - 124
direct/src/plugin/p3dOsxSplashWindow.cxx

@@ -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;
-  }
-
-  QDErr err;
-  Rect src_rect = { 0, 0, _image_height, _image_width };
-
-  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;
+set_image_filename(const string &image_filename, ImagePlacement image_placement) {
+  switch (image_placement) {
+  case IP_background:
+    load_image(_background_image, image_filename);
+    break;
+
+  case IP_button_ready:
+    load_image(_button_ready_image, image_filename);
+    set_button_range(_button_ready_image);
+    break;
+
+  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) {
+
+  // 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;
-
-      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;
+  paint_image(out_port, _background_image);
+
+  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__

+ 30 - 5
direct/src/plugin/p3dOsxSplashWindow.h

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

+ 11 - 17
direct/src/plugin/p3dPackage.I

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

+ 41 - 0
direct/src/plugin/p3dPackage.cxx

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

+ 3 - 1
direct/src/plugin/p3dPackage.h

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

+ 1 - 0
direct/src/plugin/p3dPythonRun.cxx

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

+ 4 - 20
direct/src/plugin/p3dSession.cxx

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

+ 0 - 1
direct/src/plugin/p3dSession.h

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

+ 12 - 0
direct/src/plugin/p3dSplashWindow.I

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

+ 301 - 45
direct/src/plugin/p3dSplashWindow.cxx

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

+ 52 - 6
direct/src/plugin/p3dSplashWindow.h

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

+ 21 - 34
direct/src/plugin/p3dWinSplashWindow.cxx

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

+ 2 - 4
direct/src/plugin/p3dWinSplashWindow.h

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

+ 17 - 22
direct/src/plugin/p3dX11SplashWindow.cxx

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

+ 2 - 5
direct/src/plugin/p3dX11SplashWindow.h

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