Browse Source

twirling icon on X11 too

David Rose 14 years ago
parent
commit
7a11d70f14

+ 3 - 8
direct/src/plugin/plugin_get_x11.h

@@ -16,6 +16,7 @@
 #define PLUGIN_GET_X11_H
 
 #include "pandabase.h"
+#include "p3d_plugin_config.h"
 
 #ifdef HAVE_X11 
 // This header file is designed to help work around some of the
@@ -27,18 +28,12 @@
 // should include this file instead of including the X11 headers
 // directly.
 
-#define Display X11_Display
-#define Window X11_Window
-#define Cursor X11_Cursor
-#define Connection X11_Connection
+#include "pre_x11_include.h"
 
 #include <X11/Xlib.h>
 #include <X11/Xutil.h>
 
-#undef Display
-#undef Window
-#undef Cursor
-#undef Connection
+#include "post_x11_include.h"
 
 #endif  // HAVE_X11
 

+ 281 - 19
direct/src/plugin_npapi/ppInstance.cxx

@@ -37,10 +37,10 @@
 #include <sys/time.h>
 #endif  // _WIN32
 
-#if defined(HAVE_GTK) && defined(HAVE_X11)
-#include <gtk/gtk.h>
-#include <gdk/gdkx.h>
-#endif  // HAVE_GTK
+#ifdef HAVE_X11
+#include <sys/types.h>
+#include <sys/wait.h>
+#endif  // HAVE_X11
 
 
 PPInstance::FileDatas PPInstance::_file_datas;
@@ -131,6 +131,13 @@ PPInstance(NPMIMEType pluginType, NPP instance, uint16_t mode,
   _use_xembed = false;
   _got_window = false;
   _python_window_open = false;
+#ifdef HAVE_X11
+  _twirl_subprocess_pid = -1;
+#ifdef HAVE_GTK
+  _plug = NULL;
+#endif  // HAVE_GTK
+#endif  // HAVE_X11
+
 #ifdef _WIN32
   _hwnd = NULL;
   _bg_brush = NULL;
@@ -349,6 +356,23 @@ set_window(NPWindow *window) {
   _window = *window;
   _got_window = true;
 
+#ifdef HAVE_X11
+#ifdef HAVE_GTK
+  if (_use_xembed) {
+    // Create a GtkPlug to bind to the XEmbed socket.
+    _plug = gtk_plug_new((GdkNativeWindow)_window.window);
+    gtk_widget_show(_plug);
+    
+    nout << "original XID is " << _window.window << ", created X11 window "
+         << GDK_DRAWABLE_XID(_plug->window) << "\n";
+  }
+#endif  // HAVE_GTK
+
+  if (!_failed && _p3d_inst == NULL) {
+    x11_start_twirl_subprocess();
+  }
+#endif  // HAVE_X11
+
   if (!_failed) {
     if (_p3d_inst == NULL) {
       create_instance();
@@ -1752,6 +1776,10 @@ create_instance() {
   }
 #endif  // __APPLE__
 
+#ifdef HAVE_X11
+  x11_stop_twirl_subprocess();
+#endif  // HAVE_X11
+
   // In the Windows case, we let the timer keep running, because it
   // also checks for wayward messages.
 
@@ -1853,25 +1881,19 @@ send_window() {
       // If we're using the XEmbed model, we've actually received an
       // XID for a GtkSocket.
 #ifdef HAVE_GTK
-      // Create the appropriate GtkPlug.
-      GtkWidget *plug = gtk_plug_new((GdkNativeWindow)_window.window);
-      gtk_widget_show(plug);
-
-      // Now just get the X11 Window pointer to pass down to Panda,
-      // since that's what it will be expecting.  (Hmm, it would be
-      // nice to pass the XID object and use this system in general
-      // within Panda, but that's for the future, I think.)
-      nout << "original XID is " << _window.window << ", created X11 window " 
-           << GDK_DRAWABLE_XID(plug->window) << "\n";
+      // If we're using XEmbed, pass the X11 Window pointer of our
+      // plug down to Panda.  (Hmm, it would be nice to pass the XID
+      // object and use this system in general within Panda, but
+      // that's for the future, I think.)
+      assert(_plug != NULL);
       parent_window._window_handle_type = P3D_WHT_x11_window;
-      parent_window._handle._x11_window._xwindow = (unsigned long)GDK_DRAWABLE_XID(plug->window);
+      parent_window._handle._x11_window._xwindow = GDK_DRAWABLE_XID(_plug->window);
 #endif  // HAVE_GTK
     } else {
       // If we're not using XEmbed, this is just a standard X11 Window
-      // pointer.  We make it an 'unsigned long' instead of 'Window'
-      // to avoid nppanda3d.so getting a dependency on X11.
+      // pointer.
       parent_window._window_handle_type = P3D_WHT_x11_window;
-      parent_window._handle._x11_window._xwindow = (unsigned long)(_window.window);
+      parent_window._handle._x11_window._xwindow = (X11_Window)(_window.window);
     }
     x = 0;
     y = 0;
@@ -1961,8 +1983,19 @@ cleanup_window() {
         _twirl_bitmaps[step] = NULL;
       }
     }
-
 #endif  // _WIN32
+
+#ifdef HAVE_X11
+    x11_stop_twirl_subprocess();
+
+#ifdef HAVE_GTK
+    if (_plug != NULL) {
+      gtk_widget_destroy(_plug);
+      _plug = NULL;
+    }
+#endif  // HAVE_GTK
+#endif  // HAVE_X11
+
     _got_window = false;
   }
 }
@@ -2795,6 +2828,235 @@ twirl_timer_callback() {
 }
 #endif  // __APPLE__
 
+#ifdef HAVE_X11
+////////////////////////////////////////////////////////////////////
+//     Function: PPInstance::x11_start_twirl_subprocess
+//       Access: Public
+//  Description: Spawns a separate process to twirl the loading icon
+//               in the X11 browser window.
+////////////////////////////////////////////////////////////////////
+void PPInstance::
+x11_start_twirl_subprocess() {
+  assert(_twirl_subprocess_pid == -1);
+
+  // Fork and exec.
+  pid_t child = fork();
+  if (child < 0) {
+    perror("fork");
+    return;
+  }
+
+  if (child == 0) {
+    // Here we are in the child process.
+    x11_twirl_subprocess_run();
+    _exit(0);
+  }
+
+  // In the parent process.
+  _twirl_subprocess_pid = child;
+}
+#endif  // HAVE_X11
+
+#ifdef HAVE_X11
+////////////////////////////////////////////////////////////////////
+//     Function: PPInstance::x11_stop_twirl_subprocess
+//       Access: Public
+//  Description: Kills the twirl process that was started earlier.
+////////////////////////////////////////////////////////////////////
+void PPInstance::
+x11_stop_twirl_subprocess() {
+  if (_twirl_subprocess_pid == -1) {
+    // Already stopped.
+    return;
+  }
+
+  kill(_twirl_subprocess_pid, SIGKILL);
+
+  int status;
+  pid_t result = waitpid(_twirl_subprocess_pid, &status, 0);
+
+  nout << "Twirl window process has successfully stopped.\n";
+  if (WIFEXITED(status)) {
+    nout << "  exited normally, status = "
+         << WEXITSTATUS(status) << "\n";
+  } else if (WIFSIGNALED(status)) {
+    nout << "  signalled by " << WTERMSIG(status) << ", core = " 
+         << WCOREDUMP(status) << "\n";
+  } else if (WIFSTOPPED(status)) {
+    nout << "  stopped by " << WSTOPSIG(status) << "\n";
+  }
+}
+#endif  // HAVE_X11
+
+#ifdef HAVE_X11
+////////////////////////////////////////////////////////////////////
+//     Function: PPInstance::x11_twirl_subprocess_run
+//       Access: Public
+//  Description: The code that is run within a subprocess.  This code
+//               is responsible for twirling the loading icon
+//               endlessly.
+////////////////////////////////////////////////////////////////////
+void PPInstance::
+x11_twirl_subprocess_run() {
+  // Since everything within this function happens within a subprocess
+  // that will just exit, we can be a little sloppy with our resource
+  // allocation.  It is all done directly within this function, and we
+  // don't need to worry about freeing stuff.
+
+  // First, sleep for 0.5 seconds, so we don't start twirling right
+  // away (to avoid distracting the user unnecessarily).
+
+  struct timespec req;
+  req.tv_sec = 0;
+  req.tv_nsec = 500000000;  // 500 ms
+  nanosleep(&req, NULL);
+
+  // We haven't been killed yet, so the plugin is still loading.
+  // Start twirling.
+
+  // First, embed a window.
+  X11_Display *display = XOpenDisplay(NULL);
+  assert(display != NULL);
+  int screen = DefaultScreen(display);
+
+  int depth = DefaultDepth(display, screen);
+  Visual *dvisual = DefaultVisual(display, screen);
+
+  long event_mask = ExposureMask;
+
+  // Allocate the foreground and background colors.
+  Colormap colormap = DefaultColormap(display, screen);
+
+  XColor fg;
+  fg.red = _fgcolor_r * 0x101;
+  fg.green = _fgcolor_g * 0x101;
+  fg.blue = _fgcolor_b * 0x101;
+  fg.flags = DoRed | DoGreen | DoBlue;
+  unsigned long fg_pixel = -1;
+  if (XAllocColor(display, colormap, &fg)) {
+    fg_pixel = fg.pixel;
+  }
+
+  XColor bg;
+  bg.red = _bgcolor_r * 0x101;
+  bg.green = _bgcolor_g * 0x101;
+  bg.blue = _bgcolor_b * 0x101;
+  bg.flags = DoRed | DoGreen | DoBlue;
+  unsigned long bg_pixel = -1;
+  if (XAllocColor(display, colormap, &bg)) {
+    bg_pixel = bg.pixel;
+  }
+
+  // Initialize window attributes
+  XSetWindowAttributes wa;
+  wa.background_pixel = XWhitePixel(display, screen);
+  if (bg_pixel != -1) {
+    wa.background_pixel = bg_pixel;
+  }
+  wa.border_pixel = 0;
+  wa.event_mask = event_mask;
+
+  unsigned long attrib_mask = CWBackPixel | CWBorderPixel | CWEventMask;
+
+  X11_Window parent = GDK_DRAWABLE_XID(_plug->window);
+  X11_Window window = XCreateWindow
+    (display, parent, 0, 0, _window.width, _window.height,
+     0, depth, InputOutput, dvisual, attrib_mask, &wa);
+  XMapWindow(display, window);
+
+  // Create a graphics context.
+  XGCValues gcval;
+  gcval.function = GXcopy;
+  gcval.plane_mask = AllPlanes;
+  gcval.foreground = BlackPixel(display, screen);
+  if (fg_pixel != -1) {
+    gcval.foreground = fg_pixel;
+  }
+  gcval.background = WhitePixel(display, screen);
+  if (bg_pixel != -1) {
+    gcval.background = bg_pixel;
+  }
+  GC graphics_context = XCreateGC(display, window, 
+    GCFunction | GCPlaneMask | GCForeground | GCBackground, &gcval); 
+
+  // Load up the twirling images.
+  XImage *images[twirl_num_steps];
+  double r_ratio = dvisual->red_mask / 255.0;
+  double g_ratio = dvisual->green_mask / 255.0;
+  double b_ratio = dvisual->blue_mask / 255.0;
+
+  static const size_t twirl_size = twirl_width * twirl_height;
+  unsigned char twirl_data[twirl_size * 3];
+
+  for (int step = 0; step < twirl_num_steps; ++step) {
+    get_twirl_data(twirl_data, twirl_size, step,
+                   _fgcolor_r, _fgcolor_g, _fgcolor_b, 
+                   _bgcolor_r, _bgcolor_g, _bgcolor_b);
+    uint32_t *new_data = new uint32_t[twirl_size];
+    int j = 0;
+    for (int i = 0; i < twirl_size * 3; i += 3) {
+      unsigned int r, g, b;
+      r = (unsigned int)(twirl_data[i+0] * r_ratio);
+      g = (unsigned int)(twirl_data[i+1] * g_ratio);
+      b = (unsigned int)(twirl_data[i+2] * b_ratio);
+      new_data[j++] = ((r & dvisual->red_mask) |
+                       (g & dvisual->green_mask) |
+                       (b & dvisual->blue_mask));
+    }
+
+    // Now load the image.
+    images[step] = XCreateImage(display, CopyFromParent, DefaultDepth(display, screen), 
+                                ZPixmap, 0, (char *)new_data, twirl_width, twirl_height, 32, 0);
+  }
+
+  // Now twirl indefinitely, until our parent process kills us.
+  bool needs_redraw = true;
+  int last_step = -1;
+  while (true) {
+    // First, scan for X events.
+    XEvent event;
+    while (XCheckWindowEvent(display, window, ~0, &event)) {
+      switch (event.type) {
+      case Expose:
+      case GraphicsExpose:
+        needs_redraw = true;
+        break;
+      }
+
+      // We should probably track the resize event, but this window
+      // will be short-lived (and probably won't have an opportunity
+      // to resize anyway) so we don't bother.
+    }
+
+    // What step are we on now?
+    struct timeval tv;
+    gettimeofday(&tv, (struct timezone *)NULL);
+    double now = (double)(tv.tv_sec - _init_sec) + (double)(tv.tv_usec - _init_usec) / 1000000.0;
+    int step = ((int)(now * 10.0)) % twirl_num_steps;
+    if (step != last_step) {
+      needs_redraw = true;
+    }
+
+    if (needs_redraw) {
+      XClearWindow(display, window);
+      int xo = (_window.width - twirl_width) / 2;
+      int yo = (_window.height - twirl_height) / 2;
+      XPutImage(display, window, graphics_context, images[step], 0, 0, 
+                xo, yo, twirl_width, twirl_height);
+
+      XFlush(display);
+      needs_redraw = false;
+      last_step = step;
+    }
+
+    struct timespec req;
+    req.tv_sec = 0;
+    req.tv_nsec = 100000000;  // 100 ms
+    nanosleep(&req, NULL);
+  }
+}
+#endif  // HAVE_X11
+
 ////////////////////////////////////////////////////////////////////
 //     Function: PPInstance::StreamingFileData::Constructor
 //       Access: Public

+ 23 - 0
direct/src/plugin_npapi/ppInstance.h

@@ -20,6 +20,15 @@
 #include "get_tinyxml.h"
 #include "p3d_lock.h"
 #include "get_twirl_data.h"
+#include "p3d_plugin_config.h"
+#include "plugin_get_x11.h"
+
+#if defined(HAVE_X11) && defined(HAVE_GTK)
+#include "pre_x11_include.h"
+#include <gtk/gtk.h>
+#include <gdk/gdkx.h>
+#include "post_x11_include.h"
+#endif // HAVE_X11 && HAVE_GTK
 
 #include <vector>
 
@@ -140,6 +149,12 @@ private:
   void twirl_timer_callback();
 #endif  // __APPLE__
 
+#ifdef HAVE_X11
+  void x11_start_twirl_subprocess();
+  void x11_stop_twirl_subprocess();
+  void x11_twirl_subprocess_run();
+#endif // HAVE_X11
+
 private:
   NPP _npp_instance;
   unsigned int _npp_mode;
@@ -238,6 +253,10 @@ private:
   bool _use_xembed;
   bool _got_window;
   NPWindow _window;
+#if defined(HAVE_X11) && defined(HAVE_GTK)
+  GtkWidget *_plug;
+#endif  // HAVE_X11 && HAVE_GTK
+
 #ifdef _WIN32
   LONG_PTR _orig_window_proc;
   HWND _hwnd;
@@ -266,6 +285,10 @@ private:
   bool _got_twirl_images;
 #endif  // MACOSX_HAS_EVENT_MODELS
 
+#ifdef HAVE_X11
+  pid_t _twirl_subprocess_pid;
+#endif  // HAVE_X11
+
 #ifndef _WIN32
   long _init_sec;
   long _init_usec;