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
_______________________________________________
gtk-app-devel-list mailing list
gtk-app-devel-list@gnome.org
http://mail.gnome.org/mailman/listinfo/gtk-app-devel-list