diff --git a/direct/src/plugin/plugin_get_x11.h b/direct/src/plugin/plugin_get_x11.h index 4140c88c5a..dde8f13241 100644 --- a/direct/src/plugin/plugin_get_x11.h +++ b/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 #include -#undef Display -#undef Window -#undef Cursor -#undef Connection +#include "post_x11_include.h" #endif // HAVE_X11 diff --git a/direct/src/plugin_npapi/ppInstance.cxx b/direct/src/plugin_npapi/ppInstance.cxx index df750a5886..402e1d10f5 100644 --- a/direct/src/plugin_npapi/ppInstance.cxx +++ b/direct/src/plugin_npapi/ppInstance.cxx @@ -37,10 +37,10 @@ #include #endif // _WIN32 -#if defined(HAVE_GTK) && defined(HAVE_X11) -#include -#include -#endif // HAVE_GTK +#ifdef HAVE_X11 +#include +#include +#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 diff --git a/direct/src/plugin_npapi/ppInstance.h b/direct/src/plugin_npapi/ppInstance.h index b8163861d2..5a6f26571b 100644 --- a/direct/src/plugin_npapi/ppInstance.h +++ b/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 +#include +#include "post_x11_include.h" +#endif // HAVE_X11 && HAVE_GTK #include @@ -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;