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