Hi

I am working on an application which requires plots to be displayed within
the context of the program's windows.  In practice this means that I needed
plplot to have the ability to plot to a supplied X Drawable rather
than to an X window which plplot creates and manages itself.

The attached patch is a first pass at implementing this idea within the
xcairo driver (against plplot 5.8.0).  I have tested this in the context of
a gtk application using both a Window and a Pixmap as the destination
drawable and it seems to work fine.

In order for this to work the application must have a way to pass both an
open Display as well as the Drawable itself to plplot.  To achieve this I
implemented a PLESC_DEVINIT function for the xcairo device which expects a
pointer to the new PLXcairoDrawableInfo structure.  The idea is that the
parent application populates this structure with the required information
before calling PLESC_DEVINIT.  Note that for the purposes of this patch
PLXcairoDrawableInfo is defined in cairo.c to keep it self-contained, but
plplot.h would be its ultimate resting place.

I toyed with the idea of having the display/drawable passed in with device
options before plinit() but using PLESC_DEVINIT seems like a neater solution
to me.  Instead I use a single integer option to set xcairo into xdrawable
mode.

When running in this so-called xdrawable mode plplot makes no attempt at
maintaining the plot in terms of handling expose events and the like.  My
reasoning here is that since the drawable was created by the application the
application has a much better change of efficiently handling expose events.

Comments and feedback are welcome.  For what it's worth I think that this
feature would make a worthwhile addition to plplot.  In a way it is similar
in functionality to plplotcanvas but unlike plplotcanvas the xdrawable
solution does not drag in a large number of extraneous dependencies
(gnome-print for example).  Since almost every application framework/toolkit
can provide X drawables it makes plplot accessable to almost every developer
regardless of their preferred GUI framework.

As a final comment, the following code snippet illustrates how I connect a
gtk drawing area (or gdk pixmap) to plplot:

  struct {
    Display *display;
    Drawable drawable;
  } xinfo;

  plsdev("xcairo");
  plsetopt("drvopt", "external_drawable");
  plinit();
  xinfo.display = GDK_WINDOW_XDISPLAY( [a GtkDrawingArea widget] );
  xinfo.drawable = GDK_WINDOW_XID( [a GtkDrawingArea widget] );
  pl_cmd(PLESC_DEVINIT, &xinfo);

  [ do plotting ]

  plend();

To use a GdkPixmap, the xinfo initialisation lines simply become

  xinfo.display = GDK_PIXMAP_XDISPLAY( [a GdkPixmap] ); 
  xinfo.drawable = GDK_PIXMAP_XID( [a GdkPixmap] );

In due course the xinfo structure will be of type PLXcairoDrawableInfo.

Regards
  jonathan

--- drivers/cairo.c-orig        2007-11-19 06:34:11.000000000 +1030
+++ drivers/cairo.c     2008-03-05 13:04:15.000000000 +1030
@@ -73,10 +73,12 @@
 static int text_clipping;
 static int text_anti_aliasing;
 static int graphics_anti_aliasing;
+static int external_drawable;
 
 static DrvOpt cairo_options[] = {{"text_clipping", DRV_INT, &text_clipping, 
"Use text clipping (text_clipping=0|1)"},
                                 {"text_anti_aliasing", DRV_INT, 
&text_anti_aliasing, "Set desired text anti-aliasing 
(text_anti_aliasing=0|1|2|3). The numbers are in the same order as the 
cairo_antialias_t enumeration documented at 
http://cairographics.org/manual/cairo-cairo-t.html#cairo-antialias-t)"},
                                 {"graphics_anti_aliasing", DRV_INT, 
&graphics_anti_aliasing, "Set desired graphics anti-aliasing 
(graphics_anti_aliasing=0|1|2|3). The numbers are in the same order as the 
cairo_antialias_t enumeration documented at 
http://cairographics.org/manual/cairo-cairo-t.html#cairo-antialias-t"},
+                                {"external_drawable", DRV_INT, 
&external_drawable, "Plot to external X drawable"},
                                  {NULL, DRV_INT, NULL, NULL}};
 
 typedef struct {
@@ -89,6 +91,7 @@
   short exit_event_loop;
   Display *XDisplay;
   Window XWindow;
+  unsigned int xdrawable_mode;
 #endif
 #if defined(PLD_memcairo)
   unsigned char *memory;
@@ -116,6 +119,14 @@
 #endif
 ;
 
+/* Structure for passing external drawables to xcairo devices via
+ * the PLESC_DEVINIT escape function.
+ */
+typedef struct {
+  Display *display;
+  Drawable drawable;
+} PLXcairoDrawableInfo;
+
 //---------------------------------------------------------------------
 // Font style and weight lookup tables (copied
 // from the psttf driver).
@@ -206,6 +217,12 @@
 
   aStream = (PLCairo *)pls->dev;
 
+  /* Some Cairo devices support delayed device setup (eg: xcairo with
+   * external drawable).
+   */
+  if (aStream->cairoSurface == NULL)
+    return;
+
   // Fill in the window with the background color.
   cairo_rectangle(aStream->cairoContext, 0.0, 0.0, pls->xlength, pls->ylength);
   cairo_set_source_rgb(aStream->cairoContext,
@@ -661,6 +678,10 @@
 
   // Allocate a cairo stream structure
   aStream = malloc(sizeof(PLCairo));
+  aStream->XDisplay = NULL;
+  aStream->XWindow = -1;
+  aStream->cairoSurface = NULL;
+  aStream->cairoContext = NULL;
 
   // Set text clipping off as this makes the driver pretty slow
   aStream->text_clipping = 0;
@@ -794,6 +815,38 @@
 }
 
 //---------------------------------------------------------------------
+// xcairo_init_cairo()
+//
+// Configures Cairo to use whichever X Drawable is set up in the given
+// stream.  This is called by plD_init_xcairo() in the event we are 
+// drawing into a plplot-managed window, and plD_esc_xcairo() if
+// we are using an external X Drawable.
+//
+// A return value of 0 indicates success.  Currently this function only
+// returns 0.
+//----------------------------------------------------------------------
+
+static signed int xcairo_init_cairo(PLStream *pls)
+{
+  PLCairo *aStream;
+  Visual *defaultVisual;
+
+  aStream = (PLCairo *)pls->dev;
+
+  // Create an cairo surface & context that are associated with the X window.
+  defaultVisual = DefaultVisual(aStream->XDisplay, 0);
+  aStream->cairoSurface = cairo_xlib_surface_create(aStream->XDisplay, 
aStream->XWindow, defaultVisual, pls->xlength, pls->ylength);
+  aStream->cairoContext = cairo_create(aStream->cairoSurface);
+
+  // Invert the surface so that the graphs are drawn right side up.
+  rotate_cairo_surface(pls, 1.0, 0.0, 0.0, -1.0, 0.0, pls->ylength);
+
+  // Set graphics aliasing
+  cairo_set_antialias(aStream->cairoContext, aStream->graphics_anti_aliasing);
+
+  return 0;
+}
+//---------------------------------------------------------------------
 // plD_init_xcairo()
 //
 // Initialize Cairo X Windows device.
@@ -802,47 +855,44 @@
 void plD_init_xcairo(PLStream *pls)
 {
   char plotTitle[40];
-  Visual *defaultVisual;
   XGCValues values;
   PLCairo *aStream;
 
-  // Setup the PLStream and the font lookup table
+  // Setup the PLStream and the font lookup table.
   aStream = stream_and_font_setup(pls, 1);
 
-  // X Windows setup
-  aStream->XDisplay = NULL;
-  aStream->XDisplay = XOpenDisplay(NULL);
-  if(aStream->XDisplay == NULL){
-    printf("Failed to open X Windows display\n");
-    // some sort of error here
-  }
-  XScreen = DefaultScreen(aStream->XDisplay);
-  rootWindow = RootWindow(aStream->XDisplay, XScreen);
-  aStream->exit_event_loop = 0;
+  // Save the pointer to the structure in the PLplot stream
+  pls->dev = aStream;
 
-  // Initialize plot title
-  sprintf(plotTitle, "PLplot");
-    
-  // Create a X Window.
-  aStream->XWindow = XCreateSimpleWindow(aStream->XDisplay, rootWindow, 0, 0, 
pls->xlength, pls->ylength, 
-                                           1, BlackPixel(aStream->XDisplay, 
XScreen), BlackPixel(aStream->XDisplay, XScreen));
-  XStoreName(aStream->XDisplay, aStream->XWindow, plotTitle);
-  XSelectInput(aStream->XDisplay, aStream->XWindow, NoEventMask);
-  XMapWindow(aStream->XDisplay, aStream->XWindow);
+  // Create a X Window if required.
+  if (external_drawable != 0) {
+    aStream->xdrawable_mode = 1;
+  } else {
+    // Initialize plot title
+    sprintf(plotTitle, "PLplot");
 
-  // Create an cairo surface & context that are associated with the X window.
-  defaultVisual = DefaultVisual(aStream->XDisplay, 0);
-  aStream->cairoSurface = cairo_xlib_surface_create(aStream->XDisplay, 
aStream->XWindow, defaultVisual, pls->xlength, pls->ylength);
-  aStream->cairoContext = cairo_create(aStream->cairoSurface);
+    // X Windows setup
+    aStream->XDisplay = NULL;
+    aStream->XDisplay = XOpenDisplay(NULL);
+    if(aStream->XDisplay == NULL){
+      printf("Failed to open X Windows display\n");
+      // some sort of error here
+    }
+    XScreen = DefaultScreen(aStream->XDisplay);
+    rootWindow = RootWindow(aStream->XDisplay, XScreen);
 
-  // Save the pointer to the structure in the PLplot stream
-  pls->dev = aStream;
+    aStream->XWindow = XCreateSimpleWindow(aStream->XDisplay, rootWindow, 0, 
0, pls->xlength, pls->ylength, 
+                                           1, BlackPixel(aStream->XDisplay, 
XScreen), BlackPixel(aStream->XDisplay, XScreen));
+    XStoreName(aStream->XDisplay, aStream->XWindow, plotTitle);
+    XSelectInput(aStream->XDisplay, aStream->XWindow, NoEventMask);
+    XMapWindow(aStream->XDisplay, aStream->XWindow);
+    aStream->xdrawable_mode = 0;
 
-  // Invert the surface so that the graphs are drawn right side up.
-  rotate_cairo_surface(pls, 1.0, 0.0, 0.0, -1.0, 0.0, pls->ylength);
+    xcairo_init_cairo(pls);
+  }
+
+  aStream->exit_event_loop = 0;
 
-  // Set graphics aliasing
-  cairo_set_antialias(aStream->cairoContext, aStream->graphics_anti_aliasing);
 }
 
 //---------------------------------------------------------------------
@@ -863,6 +913,9 @@
 
   aStream = (PLCairo *)pls->dev;
 
+  if (aStream->xdrawable_mode)
+    return;
+
   XFlush(aStream->XDisplay);
 
   // Loop, handling selected events, till the user elects to close the plot.
@@ -906,8 +959,12 @@
 
   plD_tidy_cairo(pls);
 
+  if (aStream->xdrawable_mode)
+    return;
+
   // Close the window and the display.
   XFlush(aStream->XDisplay);
+
   XDestroyWindow(aStream->XDisplay, aStream->XWindow);
 
   XCloseDisplay(aStream->XDisplay);
@@ -941,6 +998,38 @@
       XFlush(aStream->XDisplay);
       xcairo_get_cursor(pls, (PLGraphicsIn*)ptr);
       break;
+    case  PLESC_DEVINIT: { // Set external drawable
+        Window rootwin;
+        PLXcairoDrawableInfo *xinfo = (PLXcairoDrawableInfo *)ptr;
+        signed int x, y;
+        unsigned int w, h, b, d;
+        if (xinfo == NULL) {
+          printf("xcairo: PLESC_DEVINIT ignored, no drawable info provided\n");
+          return;
+        }
+        if (aStream->xdrawable_mode == 0) {
+          printf("xcairo: PLESC_DEVINIT called with drawable but stream not in 
xdrawable mode\n");
+          return;
+        }
+        aStream->XDisplay = xinfo->display;
+        aStream->XWindow = xinfo->drawable;
+
+        /* Ensure plplot knows the real dimensions of the drawable */
+        XGetGeometry(aStream->XDisplay, aStream->XWindow, &rootwin,
+          &x, &y, &w, &h, &b, &d);
+        pls->xlength = w;
+        pls->ylength = h;
+        plP_setphy((PLINT) 0, (PLINT) pls->xlength / DOWNSCALE, (PLINT) 0, 
+          (PLINT) pls->ylength / DOWNSCALE);
+  
+        /* Associate cairo with the supplied drawable */
+        xcairo_init_cairo(pls);
+
+        /* Recalculate dimensions and the like now that the drawable is known 
*/
+        plbop();
+
+        break;
+      }
     }
 }
 

-------------------------------------------------------------------------
This SF.net email is sponsored by: Microsoft
Defy all challenges. Microsoft(R) Visual Studio 2008.
http://clk.atdmt.com/MRT/go/vse0120000070mrt/direct/01/
_______________________________________________
Plplot-devel mailing list
Plplot-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/plplot-devel

Reply via email to