Re: deprecated gtk_cairo_create



Hello Sergei,

Unfortunately the use of the gtk3 DrawingArea is a bit obscure and difficult to figure out. However, I have been able to make use of it very effectively in conjunction with Cairo drawing ops.

For various reasons I have created a GUI toolkit library which lies between my app code and gtk3. This library includes an object known as a Canvas, which is essentially a toplevel window with scrollbars parenting a DrawingArea. The Canvas object includes a private off-screen Cairo drawing surface which I create and control completely, and I do all of my application drawing onto that surface. Then, I use the gtk3 draw signal mechanism to copy whatever recatngular areas I want from my private surface onto the DrawingArea. gtk3 passes to my drawing event handler a pointer to a Cairo context which addresses the DrawingArea's own Cairo surface, so all I need to do in the draw event handler is set a source surface on that context (in my case, the source surface is my own private Cairo surface), and copy a rectangle. As far as I know there is no safe way to access the Cairo surface of a gtk3 DrawingArea other than through the Cairo context pointer passed into the drawing event handler.

Following is some incomplete, stripped down code which may help to explain the above ideas. I hope this helps!

Roger Davis
Univ. of Hawaii

######### code follows ###########

/* Here are the relevant parts of the Canvas structure */
typedef struct {
        ...
        GtkWidget *drawarea;    /* created by gtk_drawing_area_new() */
        int width, height;      /* size of DrawingArea */
        cairo_surface_t *surface; /* my private Cairo surface */
        ...
} Canvas;

gboolean
canvasdrawevt(GtkWidget *w, cairo_t *cr, gpointer gp)
/*
   This internal routine handles draw signals sent by GTK+3 to a Canvas'
   GtkDrawingArea widget. These signals will typically be generated by
   expose events or by program calls to gtk_widget_queue_draw_area()
   generated from within canvasflushregion(). This routine merely copies
   the rectangle(s) of interest from the off-screen Cairo drawing surface
   of the canvas into the actual GtkDrawingArea widget.
*/
{
        Canvas *c;

        if (gp == (gpointer) 0)
                return FALSE;
        c= (Canvas *) gp;
        if (w != c->drawarea)
                return FALSE;

        /* here I set the source to my private
           surface which I have already drawn */
        cairo_set_source_surface(cr, c->surface, 0, 0);

        /* note that according to various (and occasionally obscure)
           GTK+3 docs, the context cr passed into this routine has
           already been clipped to any exposed rectangle(s) of interest
           (or whatever canvasflush{reg}() has marked for redraw via
           its call to gtk_widget_queue_draw_area()), so the following
           call to copy the whole drawing surface is not as expensive
           as it looks! */
        cairo_rectangle(cr, 0, 0, (double) c->width, (double) c->height);
        cairo_fill(cr);

        return TRUE;
}

/* Here is how the important parts of the Canvas are created ... */

Canvas *
canvascreate(gint width, gint height, ... )
{
        Canvas *c;
        int i;
        GtkWidget *win, *lo, *sw;
        GdkGeometry geom;

        if ((c= (Canvas *) calloc((MemSizeType) 1, sizeof(Canvas))) == (Canvas *) 0)
                return (Canvas *) 0;

        ...

        /* create the DrawingArea */
        c->width= width;
        c->height= height;
        if ((c->drawarea= gtk_drawing_area_new()) == (GtkWidget *) 0) {
                canvasdestroy(c);
                return (Canvas *) 0;
        }
        gtk_widget_set_size_request(c->drawarea, width, height);
        gtk_widget_set_can_focus(c->drawarea, TRUE);
        ...

        /* attach the drawing event handler */
        g_signal_connect((gpointer) c->drawarea, "draw", G_CALLBACK(canvasdrawevt), (gpointer) c);
        ...

        /* create our private Cairo surface on which we will draw
           everything */
        if ((c->surface= cairo_image_surface_create(CAIRO_FORMAT_ARGB32, (int) width, (int) height)) == 
(cairo_surface_t *) 0) {
                canvasdestroy(c);
                return (GITCanvas *) 0;
        }
        ...

        return c;
}

/* Call this routine to copy our private Cairo surface to the DrawingArea
   whenever we want (i.e., after we have done all our drawing into the
   former).  A call to this routine will result in our drawing event
   handler above being called asynchronously at some later time (but
   probably just about immediately). */
void
canvasflushregion(Canvas *c, int x0, int x1, int y0, int y1)
{
        unsigned int w, h;

        /* my real code checks x0/x1/y0/y1 for
           correctness before proceeding! */
        ...

        w= (unsigned int) (x1-x0+1);
        h= (unsigned int) (y1-y0+1);

        /* the following call will result in GTK+3/GDK sending a draw
           signal on the rectangle of interest that will ultimately be
           handled within canvasdrawevt(); see further comments there */
        gtk_widget_queue_draw_area(c->drawarea, px0, py0, w, h);

        return;
}





[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]