瀏覽代碼

wip, x11 button handling

David Rose 16 年之前
父節點
當前提交
1513171ae8

+ 14 - 0
direct/src/plugin/handleStream.I

@@ -94,3 +94,17 @@ inline FHandle HandleStream::
 get_handle() const {
 get_handle() const {
   return _buf.get_handle();
   return _buf.get_handle();
 }
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: HandleStream::has_gdata
+//       Access: Public
+//  Description: Returns true if there is data in the stream's "get"
+//               buffer, meaning that at least one character can be
+//               extracted from the stream without making an OS read()
+//               call.  Returns false if the get buffer is empty,
+//               meaning the next read call will hit the OS.
+////////////////////////////////////////////////////////////////////
+inline bool HandleStream::
+has_gdata() const {
+  return _buf.has_gdata();
+}

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

@@ -35,6 +35,7 @@ public:
   inline void close_handle();
   inline void close_handle();
 
 
   inline FHandle get_handle() const;
   inline FHandle get_handle() const;
+  inline bool has_gdata() const;
 
 
 private:
 private:
   HandleStreamBuf _buf;
   HandleStreamBuf _buf;

+ 14 - 0
direct/src/plugin/handleStreamBuf.I

@@ -23,3 +23,17 @@ inline FHandle HandleStreamBuf::
 get_handle() const {
 get_handle() const {
   return _handle;
   return _handle;
 }
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: HandleStreamBuf::has_gdata
+//       Access: Public
+//  Description: Returns true if there is data in the stream's "get"
+//               buffer, meaning that at least one character can be
+//               extracted from the stream without making an OS read()
+//               call.  Returns false if the get buffer is empty,
+//               meaning the next read call will hit the OS.
+////////////////////////////////////////////////////////////////////
+inline bool HandleStreamBuf::
+has_gdata() const {
+  return (egptr() != gptr());
+}

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

@@ -38,6 +38,7 @@ public:
   void close_handle();
   void close_handle();
 
 
   inline FHandle get_handle() const;
   inline FHandle get_handle() const;
+  inline bool has_gdata() const;
 
 
 protected:
 protected:
   virtual int overflow(int c);
   virtual int overflow(int c);

+ 10 - 7
direct/src/plugin/p3dSplashWindow.cxx

@@ -137,7 +137,7 @@ handle_event(P3D_event_data event) {
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: P3DSplashWindow::set_button_active
 //     Function: P3DSplashWindow::set_button_active
-//       Access: Public
+//       Access: Public, Virtual
 //  Description: Sets whether the button should be visible and active
 //  Description: Sets whether the button should be visible and active
 //               (true) or invisible and inactive (false).  If active,
 //               (true) or invisible and inactive (false).  If active,
 //               the button image will be displayed in the window, and
 //               the button image will be displayed in the window, and
@@ -417,10 +417,12 @@ set_mouse_data(int mouse_x, int mouse_y, bool mouse_down) {
   _mouse_y = mouse_y;
   _mouse_y = mouse_y;
   _mouse_down = mouse_down;
   _mouse_down = mouse_down;
 
 
+  ButtonState bstate = BS_hidden;
+
   if (!_button_active) {
   if (!_button_active) {
     // The button isn't active, so it's hidden, regardless of the
     // The button isn't active, so it's hidden, regardless of the
     // mouse position.
     // mouse position.
-    _bstate = BS_hidden;
+    bstate = BS_hidden;
   } else {
   } else {
     // Is the mouse pointer within the button region?
     // Is the mouse pointer within the button region?
     bool is_within = (_mouse_x >= _button_x && _mouse_x < _button_x + _button_width &&
     bool is_within = (_mouse_x >= _button_x && _mouse_x < _button_x + _button_width &&
@@ -435,7 +437,7 @@ set_mouse_data(int mouse_x, int mouse_y, bool mouse_down) {
         // and dragging over the button doesn't count.
         // and dragging over the button doesn't count.
         if (orig_bstate == BS_rollover || _button_depressed) {
         if (orig_bstate == BS_rollover || _button_depressed) {
           _button_depressed = true;
           _button_depressed = true;
-          _bstate = BS_click;
+          bstate = BS_click;
         }
         }
       } else {
       } else {
         _button_depressed = false;
         _button_depressed = false;
@@ -443,24 +445,25 @@ set_mouse_data(int mouse_x, int mouse_y, bool mouse_down) {
           // If we just transitioned from mouse down to mouse up, this
           // If we just transitioned from mouse down to mouse up, this
           // means a click.  And the button automatically hides itself
           // means a click.  And the button automatically hides itself
           // after a successful click.
           // after a successful click.
-          _bstate = BS_hidden;
+          bstate = BS_hidden;
           _button_active = false;
           _button_active = false;
           button_click_detected();
           button_click_detected();
         } else {
         } else {
-          _bstate = BS_rollover;
+          bstate = BS_rollover;
         }
         }
       }
       }
     } else {
     } else {
       // The mouse is not within the button region.  This means ready
       // The mouse is not within the button region.  This means ready
       // state.
       // state.
-      _bstate = BS_ready;
+      bstate = BS_ready;
       if (!_mouse_down) {
       if (!_mouse_down) {
         _button_depressed = false;
         _button_depressed = false;
       }
       }
     }
     }
   }
   }
 
 
-  if (orig_bstate != _bstate) {
+  if (_bstate != bstate) {
+    _bstate = bstate;
     // If we've changed button states, we need to refresh the window.
     // If we've changed button states, we need to refresh the window.
     refresh();
     refresh();
   }
   }

+ 1 - 1
direct/src/plugin/p3dSplashWindow.h

@@ -57,7 +57,7 @@ public:
 
 
   virtual bool handle_event(P3D_event_data event);
   virtual bool handle_event(P3D_event_data event);
 
 
-  void set_button_active(bool flag);
+  virtual void set_button_active(bool flag);
 
 
 protected:
 protected:
   // This ImageData base class provides minimal functionality for
   // This ImageData base class provides minimal functionality for

+ 303 - 119
direct/src/plugin/p3dX11SplashWindow.cxx

@@ -17,6 +17,7 @@
 #ifdef HAVE_X11
 #ifdef HAVE_X11
 
 
 #include "get_tinyxml.h"
 #include "get_tinyxml.h"
+#include "binaryXml.h"
 #include <time.h>
 #include <time.h>
 #include <fcntl.h>
 #include <fcntl.h>
 #include <sys/wait.h>
 #include <sys/wait.h>
@@ -47,13 +48,7 @@ P3DX11SplashWindow(P3DInstance *inst) :
   // Init for subprocess
   // Init for subprocess
   _display = None;
   _display = None;
   _window = None;
   _window = None;
-  _image = NULL;
-  _resized_image = NULL;
   _screen = 0;
   _screen = 0;
-  _image_width = 0;
-  _image_height = 0;
-  _resized_width = 0;
-  _resized_height = 0;
   _graphics_context = None;
   _graphics_context = None;
   _bar_context = None;
   _bar_context = None;
   _install_progress = 0.0;
   _install_progress = 0.0;
@@ -101,6 +96,7 @@ set_image_filename(const string &image_filename, ImagePlacement image_placement)
   TiXmlElement *xcommand = new TiXmlElement("command");
   TiXmlElement *xcommand = new TiXmlElement("command");
   xcommand->SetAttribute("cmd", "set_image_filename");
   xcommand->SetAttribute("cmd", "set_image_filename");
   xcommand->SetAttribute("image_filename", image_filename);
   xcommand->SetAttribute("image_filename", image_filename);
+  xcommand->SetAttribute("image_placement", (int)image_placement);
   doc.LinkEndChild(xcommand);
   doc.LinkEndChild(xcommand);
   write_xml(_pipe_write, &doc, nout);
   write_xml(_pipe_write, &doc, nout);
 
 
@@ -150,6 +146,47 @@ set_install_progress(double install_progress) {
   check_stopped();
   check_stopped();
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: P3DX11SplashWindow::set_button_active
+//       Access: Public, Virtual
+//  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 P3DX11SplashWindow::
+set_button_active(bool flag) {
+  if (_subprocess_pid == -1) {
+    return;
+  }
+
+  TiXmlDocument doc;
+  TiXmlElement *xcommand = new TiXmlElement("command");
+  xcommand->SetAttribute("cmd", "set_button_active");
+  xcommand->SetAttribute("button_active", (int)flag);
+  doc.LinkEndChild(xcommand);
+  write_xml(_pipe_write, &doc, nout);
+
+  check_stopped();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: P3DX11SplashWindow::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 P3DX11SplashWindow::
+button_click_detected() {
+  // This method is called in the child process, and must relay
+  // the information to the parent process.
+  cerr << "click detected\n";
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: P3DX11SplashWindow::start_subprocess
 //     Function: P3DX11SplashWindow::start_subprocess
 //       Access: Private
 //       Access: Private
@@ -328,12 +365,9 @@ subprocess_run() {
     return;
     return;
   }
   }
 
 
-  XEvent event;
-  XSelectInput(_display, _window, ExposureMask | StructureNotifyMask);
-  
-  string prev_image_filename;
+  ButtonState prev_bstate = BS_hidden;
   string prev_label;
   string prev_label;
-  double prev_progress;
+  double prev_progress = 0.0;
 
 
   bool needs_redraw = true;
   bool needs_redraw = true;
   bool needs_draw_label = false;
   bool needs_draw_label = false;
@@ -343,26 +377,52 @@ subprocess_run() {
   _subprocess_continue = true;
   _subprocess_continue = true;
   while (_subprocess_continue) {
   while (_subprocess_continue) {
     // First, scan for X events.
     // First, scan for X events.
-    if (XCheckTypedWindowEvent(_display, _window, Expose, &event)
-        || XCheckTypedWindowEvent(_display, _window, GraphicsExpose, &event)) {
-      needs_redraw = true;
-    }
-    
-    if (XCheckTypedWindowEvent(_display, _window, ConfigureNotify, &event)) {
-      if (_resized_image != NULL && (event.xconfigure.width  != _win_width ||
-                                     event.xconfigure.height != _win_height)) {
-        XDestroyImage(_resized_image);
-        _resized_image = NULL;
+    XEvent event;
+    while (XCheckWindowEvent(_display, _window, ~0, &event)) {
+      switch (event.type) {
+      case Expose:
+      case GraphicsExpose:
+        needs_redraw = true;
+        break;
+
+      case ConfigureNotify:
+        if (event.xconfigure.width != _win_width ||
+            event.xconfigure.height != _win_height) {
+          _win_width = event.xconfigure.width;
+          _win_height = event.xconfigure.height;
+          
+          // If the window changes size, we need to recompute all of the
+          // resized images.
+          _background_image.dump_resized_image();
+          _button_ready_image.dump_resized_image();
+          _button_rollover_image.dump_resized_image();
+          _button_click_image.dump_resized_image();
+        }
+        needs_redraw = true;
+        break;
+        
+      case MotionNotify:
+        set_mouse_data(event.xmotion.x, event.xmotion.y, _mouse_down);
+        break;
+
+      case ButtonPress:
+        set_mouse_data(_mouse_x, _mouse_y, true);
+        break;
+        
+      case ButtonRelease:
+        set_mouse_data(_mouse_x, _mouse_y, false);
+        break;
       }
       }
-      _win_width = event.xconfigure.width;
-      _win_height = event.xconfigure.height;
-      needs_redraw = true;
     }
     }
-    
-    if (_image_filename != prev_image_filename) {
-      update_image_filename(_image_filename);
+
+    update_image(_background_image, needs_redraw);
+    update_image(_button_ready_image, needs_redraw);
+    update_image(_button_rollover_image, needs_redraw);
+    update_image(_button_click_image, needs_redraw);
+
+    if (_bstate != prev_bstate) {
       needs_redraw = true;
       needs_redraw = true;
-      prev_image_filename = _image_filename;
+      prev_bstate = _bstate;
     }
     }
 
 
     if (_install_label != prev_label) {
     if (_install_label != prev_label) {
@@ -431,23 +491,30 @@ subprocess_run() {
 
 
 
 
     // Now check for input from the parent.
     // Now check for input from the parent.
-    int read_fd = _pipe_read.get_handle();
-    fd_set fds;
-    FD_ZERO(&fds);
-    FD_SET(read_fd, &fds);
-
-    struct timeval tv;
-    tv.tv_sec = 0;
-    tv.tv_usec = 1;  // Sleep a bit to yield the timeslice if there's
-                     // nothing new.
+    bool input_ready = _pipe_read.has_gdata();
+    if (!input_ready) {
+      int read_fd = _pipe_read.get_handle();
+      fd_set fds;
+      FD_ZERO(&fds);
+      FD_SET(read_fd, &fds);
+      
+      struct timeval tv;
+      tv.tv_sec = 0;
+      tv.tv_usec = 1;  // Sleep a bit to yield the timeslice if there's
+      // nothing new.
+      
+      int result = select(read_fd + 1, &fds, NULL, NULL, &tv);
+      if (result > 0) {
+        // There is some noise on the pipe, so read it.
+        input_ready = true;
+      } else if (result == -1) {
+        // Error in select.
+        perror("select");
+      }
+    }
 
 
-    int result = select(read_fd + 1, &fds, NULL, NULL, &tv);
-    if (result == 1) {
-      // There is some noise on the pipe, so read it.
+    if (input_ready) {
       receive_command();
       receive_command();
-    } else if (result == -1) {
-      // Error in select.
-      perror("select");
     }
     }
   }
   }
 
 
@@ -476,10 +543,35 @@ receive_command() {
         _subprocess_continue = false;
         _subprocess_continue = false;
 
 
       } else if (strcmp(cmd, "set_image_filename") == 0) {
       } else if (strcmp(cmd, "set_image_filename") == 0) {
-        const char *str = xcommand->Attribute("image_filename");
-        if (str != NULL) {
-          if (_image_filename != string(str)) {
-            _image_filename = str;
+        const string *image_filename = xcommand->Attribute(string("image_filename"));
+        int image_placement;
+        if (image_filename != NULL && 
+            xcommand->QueryIntAttribute("image_placement", &image_placement) == TIXML_SUCCESS) {
+          
+          X11ImageData *image = NULL;
+          switch ((ImagePlacement)image_placement) {
+          case IP_background:
+            image = &_background_image;
+            break;
+            
+          case IP_button_ready:
+            image = &_button_ready_image;
+            set_button_range(_button_ready_image);
+            break;
+            
+          case IP_button_rollover:
+            image = &_button_rollover_image;
+            break;
+            
+          case IP_button_click:
+            image = &_button_click_image;
+            break;
+          }
+          if (image != NULL) {
+            if (image->_filename != *image_filename) {
+              image->_filename = *image_filename;
+              image->_filename_changed = true;
+            }
           }
           }
         }
         }
 
 
@@ -496,6 +588,12 @@ receive_command() {
         xcommand->Attribute("install_progress", &install_progress);
         xcommand->Attribute("install_progress", &install_progress);
 
 
         _install_progress = install_progress;
         _install_progress = install_progress;
+
+      } else if (strcmp(cmd, "set_button_active") == 0) {
+        int button_active = 0;
+        xcommand->Attribute("button_active", &button_active);
+
+        P3DSplashWindow::set_button_active(button_active != 0);
       }
       }
     }
     }
   }
   }
@@ -508,51 +606,85 @@ receive_command() {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void P3DX11SplashWindow::
 void P3DX11SplashWindow::
 redraw() {
 redraw() {
-  if (_image == NULL) {
-    XClearWindow(_display, _window);
+  XClearWindow(_display, _window);
+
+  paint_image(_background_image);
+
+  switch (_bstate) {
+  case BS_hidden:
+    break;
+  case BS_ready:
+    paint_image(_button_ready_image);
+    break;
+  case BS_rollover:
+    if (!paint_image(_button_rollover_image)) {
+      paint_image(_button_ready_image);
+    }
+    break;
+  case BS_click:
+    if (!paint_image(_button_click_image)) {
+      paint_image(_button_ready_image);
+    }
+    break;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: P3DX11SplashWindow::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 P3DX11SplashWindow::
+paint_image(X11ImageData &image) {
+  if (image._image == NULL) {
+    return false;
+  }
+
+  // We have an image. Let's see how we can output it.
+  if (image._width <= _win_width && image._height <= _win_height) {
+    // It fits within the window - just draw it.
+    XPutImage(_display, _window, _graphics_context, image._image, 0, 0, 
+              (_win_width - image._width) / 2, (_win_height - image._height) / 2,
+              image._width, image._height);
+  } else if (image._resized_image != NULL) {
+    // We have a resized image already, draw that one.
+    XPutImage(_display, _window, _graphics_context, image._resized_image, 0, 0, 
+              (_win_width - image._resized_width) / 2, (_win_height - image._resized_height) / 2,
+              image._resized_width, image._resized_height);
   } else {
   } else {
-    // We have an image. Let's see how we can output it.
-    if (_image_width <= _win_width && _image_height <= _win_height) {
-      // It fits within the window - just draw it.
-      XPutImage(_display, _window, _graphics_context, _image, 0, 0, 
-                (_win_width - _image_width) / 2, (_win_height - _image_height) / 2,
-                _image_width, _image_height);
-    } else if (_resized_image != NULL) {
-      // We have a resized image already, draw that one.
-      XPutImage(_display, _window, _graphics_context, _resized_image, 0, 0, 
-                (_win_width - _resized_width) / 2, (_win_height - _resized_height) / 2,
-                _resized_width, _resized_height);
-    } else {
-      // Yuck, the bad case - we need to scale it down.
-      double scale = min((double) _win_width  / (double) _image_width,
-                         (double) _win_height / (double) _image_height);
-      _resized_width = (int)(_image_width * scale);
-      _resized_height = (int)(_image_height * scale);
-      char *new_data = (char*) malloc(4 * _win_width * _win_height);
-      _resized_image = XCreateImage(_display, CopyFromParent, DefaultDepth(_display, _screen), 
-                                    ZPixmap, 0, (char *) new_data, _win_width, _win_height, 32, 0);
-      double x_ratio = ((double) _image_width) / ((double) _resized_width);
-      double y_ratio = ((double) _image_height) / ((double) _resized_height);
-      for (int x = 0; x < _win_width; ++x) {
-        for (int y = 0; y < _win_height; ++y) {
-          XPutPixel(_resized_image, x, y, 
-                    XGetPixel(_image,
-                              (int)clamp(x * x_ratio, 0, _image_width),
-                              (int)clamp(y * y_ratio, 0, _image_height)));
-        }
+    // Yuck, the bad case - we need to scale it down.
+    double scale = min((double) _win_width  / (double) image._width,
+                       (double) _win_height / (double) image._height);
+    image._resized_width = (int)(image._width * scale);
+    image._resized_height = (int)(image._height * scale);
+    char *new_data = (char*) malloc(4 * _win_width * _win_height);
+    image._resized_image = XCreateImage(_display, CopyFromParent, DefaultDepth(_display, _screen), 
+                                  ZPixmap, 0, (char *) new_data, _win_width, _win_height, 32, 0);
+    double x_ratio = ((double) image._width) / ((double) image._resized_width);
+    double y_ratio = ((double) image._height) / ((double) image._resized_height);
+    for (int x = 0; x < _win_width; ++x) {
+      for (int y = 0; y < _win_height; ++y) {
+        XPutPixel(image._resized_image, x, y, 
+                  XGetPixel(image._image,
+                            (int)clamp(x * x_ratio, 0, image._width),
+                            (int)clamp(y * y_ratio, 0, image._height)));
       }
       }
-      XPutImage(_display, _window, _graphics_context, _resized_image, 0, 0,
-                (_win_width - _resized_width) / 2, (_win_height - _resized_height) / 2,
-                _resized_width, _resized_height);
     }
     }
+    XPutImage(_display, _window, _graphics_context, image._resized_image, 0, 0,
+              (_win_width - image._resized_width) / 2, (_win_height - image._resized_height) / 2,
+              image._resized_width, image._resized_height);
   }
   }
+
+  return true;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: P3DX11SplashWindow::make_window
 //     Function: P3DX11SplashWindow::make_window
 //       Access: Private
 //       Access: Private
 //  Description: Creates the window for displaying progress.  Runs
 //  Description: Creates the window for displaying progress.  Runs
-//               within the sub-thread.
+//               within the sub-process.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void P3DX11SplashWindow::
 void P3DX11SplashWindow::
 make_window() {
 make_window() {
@@ -591,7 +723,26 @@ make_window() {
   
   
   assert(_display != NULL);
   assert(_display != NULL);
   assert(parent != None);
   assert(parent != None);
-  _window = XCreateSimpleWindow(_display, parent, x, y, _win_width, _win_height, 0, 0, -1);
+
+  int depth = DefaultDepth(_display, _screen);
+  Visual *dvisual = DefaultVisual(_display, _screen);
+
+  long event_mask =
+    ButtonPressMask | ButtonReleaseMask |
+    PointerMotionMask | StructureNotifyMask;
+
+  // Initialize window attributes
+  XSetWindowAttributes wa;
+  wa.background_pixel = XWhitePixel(_display, _screen);
+  wa.border_pixel = 0;
+  wa.event_mask = event_mask;
+
+  unsigned long attrib_mask = CWBackPixel | CWBorderPixel | CWEventMask;
+
+  _window = XCreateWindow
+    (_display, parent, x, y, _win_width, _win_height,
+     0, depth, InputOutput, dvisual, attrib_mask, &wa);
+
   XMapWindow(_display, _window);
   XMapWindow(_display, _window);
 }
 }
 
 
@@ -642,15 +793,10 @@ setup_gc() {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void P3DX11SplashWindow::
 void P3DX11SplashWindow::
 close_window() {
 close_window() {
-  if (_image != NULL) {
-    XDestroyImage(_image);
-    _image = NULL;
-  }
-  
-  if (_resized_image != NULL) {
-    XDestroyImage(_resized_image);
-    _resized_image = NULL;
-  }
+  _background_image.dump_image();
+  _button_ready_image.dump_image();
+  _button_rollover_image.dump_image();
+  _button_click_image.dump_image();
   
   
   if (_bar_context != None) {
   if (_bar_context != None) {
     if (_bar_context != _graphics_context) {
     if (_bar_context != _graphics_context) {
@@ -680,45 +826,41 @@ close_window() {
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: P3DX11SplashWindow::update_image_filename
+//     Function: P3DX11SplashWindow::update_image
 //       Access: Private
 //       Access: Private
-//  Description: Loads the splash image, converts to to bitmap form,
+//  Description: Loads the splash image, converts to to an XImage,
 //               and stores it in _image.  Runs only in the
 //               and stores it in _image.  Runs only in the
-//               sub-thread.
+//               child process.
+//
+//               If the image is changed, sets needs_redraw to true.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void P3DX11SplashWindow::
 void P3DX11SplashWindow::
-update_image_filename(const string &image_filename) {
-  // Clear the old image.
-  if (_image != NULL) {
-    XDestroyImage(_image);
-    _image = NULL;
-  }
-  if (_resized_image != NULL) {
-    XDestroyImage(_resized_image);
-    _resized_image = NULL;
+update_image(X11ImageData &image, bool &needs_redraw) {
+  if (!image._filename_changed) {
+    // No changes.
+    return;
   }
   }
+  image._filename_changed = false;
+  needs_redraw = true;
+
+  // Clear the old image.
+  image.dump_image();
 
 
   // Go read the image.
   // Go read the image.
   string data;
   string data;
-  ImageData image;
-  if (!read_image_data(image, data, image_filename)) {
+  if (!read_image_data(image, data, image._filename)) {
     return;
     return;
   }
   }
 
 
-  // Temp legacy support.
-  _image_width = image._width;
-  _image_height = image._height;
-  int num_channels =image._num_channels;
-
   Visual *dvisual = DefaultVisual(_display, _screen);
   Visual *dvisual = DefaultVisual(_display, _screen);
   double r_ratio = dvisual->red_mask / 255.0;
   double r_ratio = dvisual->red_mask / 255.0;
   double g_ratio = dvisual->green_mask / 255.0;
   double g_ratio = dvisual->green_mask / 255.0;
   double b_ratio = dvisual->blue_mask / 255.0;
   double b_ratio = dvisual->blue_mask / 255.0;
-  uint32_t *new_data = (uint32_t*) malloc(4 * _image_width * _image_height);
+  uint32_t *new_data = (uint32_t*)malloc(4 * image._width * image._height);
 
 
-  if (num_channels == 3) {
+  if (image._num_channels == 4) {
     int j = 0;
     int j = 0;
-    for (int i = 0; i < 3 * _image_width * _image_height; i += 3) {
+    for (int i = 0; i < 4 * image._width * image._height; i += 4) {
       unsigned int r, g, b;
       unsigned int r, g, b;
       r = (unsigned int)(data[i+0] * r_ratio);
       r = (unsigned int)(data[i+0] * r_ratio);
       g = (unsigned int)(data[i+1] * g_ratio);
       g = (unsigned int)(data[i+1] * g_ratio);
@@ -727,13 +869,24 @@ update_image_filename(const string &image_filename) {
                       (g & dvisual->green_mask) |
                       (g & dvisual->green_mask) |
                       (b & dvisual->blue_mask);
                       (b & dvisual->blue_mask);
     }
     }
-  } else if (num_channels == 1) {
-    // A grayscale image.  Replicate out the channels.
-    for (int i = 0; i < _image_width * _image_height; ++i) {
+  } else if (image._num_channels == 3) {
+    int j = 0;
+    for (int i = 0; i < 3 * image._width * image._height; i += 3) {
       unsigned int r, g, b;
       unsigned int r, g, b;
       r = (unsigned int)(data[i+0] * r_ratio);
       r = (unsigned int)(data[i+0] * r_ratio);
       g = (unsigned int)(data[i+1] * g_ratio);
       g = (unsigned int)(data[i+1] * g_ratio);
       b = (unsigned int)(data[i+2] * b_ratio);
       b = (unsigned int)(data[i+2] * b_ratio);
+      new_data[j++] = (r & dvisual->red_mask) |
+                      (g & dvisual->green_mask) |
+                      (b & dvisual->blue_mask);
+    }
+  } else if (image._num_channels == 1) {
+    // A grayscale image.  Replicate out the channels.
+    for (int i = 0; i < image._width * image._height; ++i) {
+      unsigned int r, g, b;
+      r = (unsigned int)(data[i] * r_ratio);
+      g = (unsigned int)(data[i] * g_ratio);
+      b = (unsigned int)(data[i] * b_ratio);
       new_data[i] = (r & dvisual->red_mask) |
       new_data[i] = (r & dvisual->red_mask) |
                     (g & dvisual->green_mask) |
                     (g & dvisual->green_mask) |
                     (b & dvisual->blue_mask);
                     (b & dvisual->blue_mask);
@@ -741,8 +894,39 @@ update_image_filename(const string &image_filename) {
   }
   }
 
 
   // Now load the image.
   // Now load the image.
-  _image = XCreateImage(_display, CopyFromParent, DefaultDepth(_display, _screen), 
-                ZPixmap, 0, (char *) new_data, _image_width, _image_height, 32, 0);
+  image._image = XCreateImage(_display, CopyFromParent, DefaultDepth(_display, _screen), 
+                              ZPixmap, 0, (char *)new_data, image._width, image._height, 32, 0);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: P3DX11SplashWindow::X11ImageData::dump_image
+//       Access: Public
+//  Description: Frees the previous image data.
+////////////////////////////////////////////////////////////////////
+void P3DX11SplashWindow::X11ImageData::
+dump_image() {
+  if (_image != NULL) {
+    XDestroyImage(_image);
+    _image = NULL;
+  }
+  if (_resized_image != NULL) {
+    XDestroyImage(_resized_image);
+    _resized_image = NULL;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: P3DX11SplashWindow::X11ImageData::dump_resized_image
+//       Access: Public
+//  Description: Frees the previous resized image data only, retaining
+//               the original image data.
+////////////////////////////////////////////////////////////////////
+void P3DX11SplashWindow::X11ImageData::
+dump_resized_image() {
+  if (_resized_image != NULL) {
+    XDestroyImage(_resized_image);
+    _resized_image = NULL;
+  }
 }
 }
 
 
 #endif  // HAVE_X11
 #endif  // HAVE_X11

+ 31 - 6
direct/src/plugin/p3dX11SplashWindow.h

@@ -41,6 +41,11 @@ public:
   virtual void set_install_label(const string &install_label);
   virtual void set_install_label(const string &install_label);
   virtual void set_install_progress(double install_progress);
   virtual void set_install_progress(double install_progress);
 
 
+  virtual void set_button_active(bool flag);
+
+protected:
+  virtual void button_click_detected();
+
 private:
 private:
   void start_subprocess();
   void start_subprocess();
   void stop_subprocess();
   void stop_subprocess();
@@ -53,22 +58,44 @@ private:
 
 
 private:
 private:
   // These methods run only within the subprocess.
   // These methods run only within the subprocess.
+  class X11ImageData;
+
   void subprocess_run();
   void subprocess_run();
   void receive_command();
   void receive_command();
 
 
   void redraw();
   void redraw();
+  bool paint_image(X11ImageData &image);
   void make_window();
   void make_window();
   void setup_gc();
   void setup_gc();
-  void update_image_filename(const string &image_filename); 
+  void update_image(X11ImageData &image, bool &needs_redraw); 
   void close_window();
   void close_window();
 
 
 private:
 private:
   // Data members that are stored in the subprocess.
   // Data members that are stored in the subprocess.
+  class X11ImageData : public ImageData {
+  public:
+    inline X11ImageData();
+    inline ~X11ImageData();
+    void dump_image();
+    void dump_resized_image();
+
+    string _filename;
+    bool _filename_changed;
+    XImage *_image;
+
+    XImage *_resized_image;
+    int _resized_width, _resized_height;
+  };
+
+  X11ImageData _background_image;
+  X11ImageData _button_ready_image;
+  X11ImageData _button_rollover_image;
+  X11ImageData _button_click_image;
+
   bool _subprocess_continue;
   bool _subprocess_continue;
   HandleStream _pipe_read;
   HandleStream _pipe_read;
   
   
   bool _own_display;
   bool _own_display;
-  string _image_filename;
   string _install_label;
   string _install_label;
   double _install_progress;
   double _install_progress;
   
   
@@ -79,14 +106,12 @@ private:
   GC _graphics_context;
   GC _graphics_context;
   GC _bar_context;
   GC _bar_context;
   unsigned long _blue_pixel;
   unsigned long _blue_pixel;
-  XImage* _image;
-  XImage* _resized_image;
-  int _image_width, _image_height;
-  int _resized_width, _resized_height;
   
   
   Window _window;
   Window _window;
 };
 };
 
 
+#include "p3dX11SplashWindow.I"
+
 #endif  // HAVE_X11
 #endif  // HAVE_X11
 
 
 #endif
 #endif