Apologies in advance for the length of this post, I’m trying to describe something rather complex.
There are a couple of possibilies, either I have grabbed completely the wrong end of the stick in the interaction between widgets and gtk,
or I’ve identified potential for a significant painting optimisation on wxWidgets GTK when using non-native controls and buttons (possibly only with gtk on Xserver).
Before I launch into the post below in anger as a minor aside is there any reason that freeze() and thaw() are not implemented for gtk since the underlying GDK has them ?
In considering the bulk of the post below then please let me know if I’ve found something valid, and also the prospects of the proposed fix working.
…right – ready ? – here we go….
This is going to get really complicated to explain, but I believe this is the most significant cause of our ‘slow screen draw’ problem
Reading this page is fundamental to understanding the problem, along with understanding how wxWidgets maps onto GTK:gdk
http://library.gnome.org/devel/gtk/stable/chap-drawing-model.html
Note the major difference in how child ‘window’ and ‘non-window’ widgets are redrawn and that for hierarchical redrawing “This process is applied recursively for all the GTK_NO_WINDOW descendants of the original widget.”
And that….” When you draw on a GdkWindow, your drawing gets clipped by any child windows that it may intersect. Sometimes you need to draw over your child windows as well; for example, when drawing a drag-handle to resize something. In this case, turn on the GDK_INCLUDE_INFERIORS subwindow mode for the GdkGC which you use for drawing”
Also pay attention to the section on double buffering and in particular automatic double buffering.
Now lets turn to wxWidgets. The base window class has several requirements that don’t map too well onto gdk, which are that it can be a container for further windows and controls and that the client area can be drawn on, this resulted in the creation of the gtk_pizza class which performs these tasks. At a GTK level this derives from GtkScolledWindow – So on GTK on Xserver this means every control is a separate child window.
This must have been seen to be a problem, since some classes derived from wxWindow (e.g. wxButton) use a ‘native’ gtk button instead of a gtk_pizza, hence at a GTK level they are ‘non-window’ widgets.
(still with me ? – said this was going to get complicated)
….now our control (which is essentially a custom bitmap button) is derived from wxWindow, not wxBitmapButton. This means at a GTK level it is a SEPARATE CHILD WINDOW, not a ‘non-window’ widget.
So what I believe is happening is that when a window is sent an ‘expose’ the background (clipped to it’s children) is painted – the duration of this call being double buffered – and after this expose call returns it is shown – so we get an outer frame with ‘holes’ where all the buttons are showing the previous dialog through it
Each child gtk WINDOW in turn then gets sent an expose – each one of these individual ‘exposes’ being double buffered, but there is no overall double buffering of the entire process. (I believe I have proved this is the case by switching on visual updates in the GTK runtime and it looks like this)
So the short term fix is to derive from wxBitmapButton which has the additional bonus of shoving the bitmap right down to the gtk layer. (there is a flag on the style to have 'no border' and some other gtk level styles we might want to tinker with)
I have knocked up a demo of this and it partially works - I think it falls apart because I'm re-generating the bitmap in the 'onsize' handler and the 'set bitmap' method on the wxBitmapButton currently queues up a resize. - it does demonstrate the faster repainting - and I have a tool that hilights and delays the redrawing of the invalidated regions by manipulating the gtk update flag ( updates =
Visual feedback about window updates http://library.gnome.org/devel/gtk/unstable/gtk-running.html )
This does not solve the more general situation where we have other ‘custom’ controls which are made up of several other wxWindows. If the custom controls all do their own painting then we can blit to a device context under our own control in the wxWidgets layer, however a better general solution would be to carry out the fix at the widget/gtk interface layer so it works for all controls.
On the surface of it I’m thinking that in the ‘parent’ control we turn on ‘GDK_INCLUDE_INFERIORS’ during the processing of the expose event – then we see which children (both window and non-window) gtk widgets are in the region to be painted (or maybe the area)(area is a simple single rectangle, region is a collection). Then for each of these widgets we redirect it’s output to the parent gtk window using
gdk_window_redirect_to_drawable
http://library.gnome.org/devel/gdk/unstable/gdk-Windows.html#gdk-window-redirect-to-drawable
http://library.gnome.org/devel/gtk/stable/GtkWidget.html#gtk-widget-intersect
(a gtkWindow derives from a gtkDrawable)
then prod the child window to paint itself using gtk_widget_draw to perform an immediate update, then cancel the redirection.
- or it may be better to allow the ‘normal’ processing of the non-window gtk widgets. I’m not entirely sure how the return value from the expose callback controls further processing.
Problems still remain in that (1) the theory is good but I have not tested it (2) how do we block the subsequent expose events sent to the children so they do not draw twice (3) the ordering of parent / children expose calls may change depending on whether we are showing an entire window – or uncovering (part of) an existing window by the user moving the window on top of it.
Many thanks
Owen
> Before I launch into the post below in anger as a minor aside is there
> any reason that freeze() and thaw() are not implemented for gtk since
> the underlying GDK has them ?
This has been implemented for GTK+ (in TRUNK at least).
> Now lets turn to wxWidgets. The base window class has several
> requirements that don’t map too well onto gdk, which are that it can
> be a container for further windows and controls and that the client
> area can be drawn on, this resulted in the creation of the gtk_pizza
> class which performs these tasks. At a GTK level this derives from
> GtkScolledWindow
In TRUNK it only does that if it is supposed to have scrollbars,
but that is secondary here.
> – So on GTK on Xserver this means every control is a separate
> child window.
>
> This must have been seen to be a problem, since some classes derived
> from wxWindow (e.g. wxButton) use a ‘native’ gtk button instead of a
> gtk_pizza, hence at a GTK level they are ‘non-window’ widgets.
This is fundamentally wrong. Most native GTK+ widget are "window"
widgets and this includes GtkButton. Just look at gtk_button_realize
in gtkbutton.c
> ….now our control (which is essentially a custom bitmap button) is
> derived from wxWindow, not wxBitmapButton. This means at a GTK level
> it is a SEPARATE CHILD WINDOW, not a ‘non-window’ widget.
Just as the native counterpart.
> So what I believe is happening is that when a window is sent an
> ‘expose’ the background (clipped to it’s children) is painted – the
> duration of this call being double buffered – and after this expose
> call returns it is shown – so we get an outer frame with ‘holes’ where
> all the buttons are showing the previous dialog through it
> Each child gtk WINDOW in turn then gets sent an expose – each one of
> these individual ‘exposes’ being double buffered, but there is no
> overall double buffering of the entire process. (I believe I have
> proved this is the case by switching on visual updates in the GTK
> runtime and it looks like this)
Cannot be different when using native controls.
> On the surface of it I’m thinking that in the ‘parent’ control we turn
> on ‘GDK_INCLUDE_INFERIORS’ during the processing of the expose event –
> then we see which children (both window and non-window) gtk widgets
> are in the region to be painted (or maybe the area)(area is a simple
> single rectangle, region is a collection). Then for each of these
> widgets we redirect it’s output to the parent gtk window using
>
> gdk_window_redirect_to_drawable
> http://library.gnome.org/devel/gdk/unstable/gdk-Windows.html#gdk-window-redirect-to-drawable
> http://library.gnome.org/devel/gtk/stable/GtkWidget.html#gtk-widget-intersect
> (a gtkWindow derives from a gtkDrawable)
>
> then prod the child window to paint itself using gtk_widget_draw to
> perform an immediate update, then cancel the redirection.
>
> - or it may be better to allow the ‘normal’ processing of the
> non-window gtk widgets. I’m not entirely sure how the return value
> from the expose callback controls further processing.
I haven't thought about the redirect to drawable code, but please
bear in mind that the entire drawing system of GDK has been rewritten
in GTK+ 2.18 [1] and every trick we do now may have different effects
for later GTK+ versions. But please, try using GTK+ 2.18 first before
designing such fundamental changes.
This doesn't mean at all that wxGTK drawing and child window
clipping cannot be improved...
Robert
[1]
http://library.gnome.org/devel/gtk/2.18/gtk-migrating-ClientSideWindows.html
No, GtkButton is GTK_NO_WINDOW, look at gtk_button_init. It uses a
GdkWindow, but it is an invisible, input-only one.