problems understanding gtk3/gdk/cairo interaction



Hi all,

I am having trouble understanding gtk3/gdk/cairo interaction and thus developing a usable model for some large drawing applications I am porting from X11. I am currently working mostly on CentOS 6 with self-installed gtk3 3.4.0, gdk-pixbuf-2.26.0 and cairo 1.12.0 packages. Obviously I can minimize my effort by having the ported code conform to my existing X11/Motif drawing model as much as possible (besides the fact that I think that model works well for my own particular needs), so that's my preferred solution. If that's not possible then I need to adapt the code to whatever model gtk3/gdk/cairo actually supports, but the less modification the better, of course. (By 'model' I mean the way in which cairo interacts with gtk3 as compared with the way Xlib interacts with the Motif widget set.)

Here's the big picture of what I need to do (after which I'll explain the difficulties I am having so far in implementing a solution):

(1) I need a toplevel window to enclose a fixed-size largish (e.g., 1000x1000) plotting area for imagery display. I need scrollbars as necessary so that if the user downsizes the toplevel window any part of the image can still be scrolled to. If the user upsizes the toplevel window beyond the predetermined fixed size I don't particularly care what happens with regard to any excess exposed area, although preferably not something hideously ugly. Ideally the window would be mapped at program initialization time at the fixed size so that scrollbars are not visible, and are made visible only if the user downsizes the toplevel window.

(2) Drawing operations must be immediately performable under background program control as well as in response to user interaction and expose events. The program is reading external automated data inputs which are supplying the imagery data on a real-time basis. Under my existing X11 model, I have my own self-allocated X11 off-screen pixmap which I modify as desired via a registered work procedure which monitors these inputs. That pixmap can also be redrawn in response to user interaction. It is copied to the on-screen X11 window, which belongs to a Motif drawing area widget, both (i) explicitly via the work procedure as new imagery data become available or in response to user activity as well as (ii) indirectly via X11 expose events which filter through a Motif callback which copies any exposed rectangular pixel subsection(s) of my off-screen pixmap into the on-screen buffer of the Motif drawing area widget.

(3) Although wanting to perform the bulk of my drawing to an off-screen buffer which is fully under my own control, there are still frequent occasions where I want to make small transient alterations (e.g., draw a rubberband line) directly to the visible on-screen window as quickly and with as little overhead as possible.

I have embarked on a process of mimicking this model via a GtkDrawingArea embedded within a GtkScrolledWindow as my on-screen display and a cairo_surface_t as my off-screen drawing buffer, but am encountering some unexpected issues, primary of which is the fact that I can't figure out how to draw into the GtkDrawingArea except from within a GtkDrawingArea draw() callback, which seems insufficient for my purposes. GtkDrawingArea has two available callbacks, the realize() and draw() methods -- ideally at realize() time I would like to obtain some permanent reference into the widget so that at any time I wish, e.g., from within a registered work procedure or from within some other callback such as a menu procedure, I can transfer data from my self-maintained off-screen cairo surface into the widget's viewable on-screen buffer via that reference. I see no obvious way to do this, however -- it appears that the only way I can draw anything to the widget is via the draw() callback. I find this limiting because (i) I do not want to wait around to draw until some part of gtk3 outside my control decides I should do so (and in fact, the gtk3 documentation seems rather vague on exactly when and for what reasons the draw() callback is triggered), and (ii) drawing is extremely expensive for my application, involving compositing of many different subimages together with user annotations, etc. If I have to draw in response to an expose event, for instance, I don't want to have to do anything more involved than copy a rectangular area of pre-existing pixels from my off-screen cairo surface to the widget (and seemingly the draw() callback does not even provide detailed exposure region information :-( ). And of course I want to be able to draw directly to the widget for reasons other than expose events, such as rubberband lines rendered by a callback triggered by mouse activity.

The GtkDrawingArea documentation provides a very simple example on using the cairo_t context argument to draw(). In experimenting with my proposed model with relation to this example I discovered to my surprise that when I resize my toplevel window, monkey with the scrollbars, map/unmap the window, etc., when draw() is called its cairo_t context argument is always the same, but when I look at the underlying cairo_surface_t referenced by that context it changes from time to time (especially in reponse to scrollbar activity), even though the GdkWindow belonging to the GtkDrawingArea also seems, like the cairo_t context argument, to never change:

draw(GtkWidget *w, cairo_t *cr, gpointer gp)
{
        GdkWindow *win;
        cairo_surface_t *surf;

        win= gtk_widget_get_window(w);
        surf= cairo_get_target(cr);
printf("cr=%1x win=%1x surf=%1x\n", (unsigned long) cr, (unsigned long) win, (unsigned long) surf);

        ...
}

Here is sample output from the above, generated by messing with the toplevel window and scrollbars as described:

cr=2714640   win=2573d80   surf=2726620
cr=2714640   win=2573d80   surf=2727140
cr=2714640   win=2573d80   surf=2726620
cr=2714640   win=2573d80   surf=2724350

This behavior does not make me hopeful that I can find some way to create a permanent cairo reference into my GtkDrawingArea widget that I can use outside of draw() (or at least derive a usable reference at any time that I desire outside of that callback). Although draw()'s cairo_t context argument does appear to never change, the fact that the underlying surface seems to change makes me very nervous that unpredictable circumstances which I simply have not yet encountered through my as-yet-limited experiments might in fact cause the context to change as well at some point, since this behavior does not appear to be defined by the documentation.

Experimenting further with GtkDrawingArea's realize() method, I tried this:

static GdkWindow drawareawin;
static cairo_t drawareawincr;

realize(GtkWidget *w, gpointer gp)
{
        drawareawin= gtk_widget_get_window(w);
        drawareawincr= gdk_cairo_create(drawareawin);

        ...
}

This code does in fact correctly return the same GdkWindow which I can derive via the above draw() callback's call to gtk_widget_get_window(), but if I then attempt to draw using the cairo context returned by the above call to gdk_cairo_create() on that very same GdkWindow from within the draw() callback, nothing appears on-screen. (And not surprisingly, cairo_get_target() shows a different underlying cairo surface for the context made by gdk_cairo_create() vs. the surface which underlies the context passed in as an argument to draw().)

There is another piece to this puzzle which I also do not understand, namely that GTK widgets are supposed to be double-buffered by default, and presumably this includes the GtkDrawingArea widget as well. If there is documentation on exactly how this double-buffering works with regard to GtkDrawingArea I would be greatly interested in seeing it to figure out if there is some way I can make use of it. In particular, exactly when does either of the two buffers get written, when and how does material get transferred from one buffer (presumably the backing store buffer?) to the other buffer (presumably the visible on-screen buffer?) and how can I directly control such transfers and/or write directly myself via cairo functions into either of the buffers, either from within a draw() callback or at any other time?

So, what can I do about all of the above? Any insights into the apparently strange behavior I am observing or suggestions as to what I should be doing instead? I suspect there is much here that I may be misunderstanding, but I'm not getting very far on my own, so pointers to more detailed documentation would be greatly appreciated. Am I completely barking up the wrong tree with GtkDrawingArea/GtkScrolledWindow, should I be using something else? Is there no way to do what I need to do, i.e., get a scrollable drawing area which I can render either from within or without a draw() callback? Should I give up and annoy the people on the KDE/Qt mailing lists for a while? ;->

Thanks for your incredible patience in making it to the end of this long-winded query!

Roger Davis
Univ. of Hawaii




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