After an evaluation, GNOME has moved from Bugzilla to GitLab. Learn more about GitLab.
No new issues can be reported in GNOME Bugzilla anymore.
To report an issue in a GNOME project, go to GNOME GitLab.
Do not go to GNOME Gitlab for: Bluefish, Doxygen, GnuCash, GStreamer, java-gnome, LDTP, NetworkManager, Tomboy.
Bug 161561 - GtkWindow subclass not automatically repainting style->bg_pixmap
GtkWindow subclass not automatically repainting style->bg_pixmap
Status: RESOLVED FIXED
Product: gtk+
Classification: Platform
Component: Widget: Other
unspecified
Other Linux
: High normal
: Small fix
Assigned To: gtk-bugs
gtk-bugs
Depends on:
Blocks:
 
 
Reported: 2004-12-17 17:11 UTC by Brian Tarricone
Modified: 2004-12-22 21:47 UTC
See Also:
GNOME target: ---
GNOME version: ---


Attachments
test case (2.55 KB, text/plain)
2004-12-18 16:05 UTC, Owen Taylor
  Details
Possible fix for problem described in comment 8 (2.97 KB, patch)
2004-12-22 17:25 UTC, Soren Sandmann Pedersen
none Details | Review

Description Brian Tarricone 2004-12-17 17:11:21 UTC
I've written a GtkWindow subclass for use with the Xfce desktop.  To display a
"wallpaper" to the user, I simply create a GdkPixmap and set it as the window's
GtkStyle::bg_pixmap[GTK_STATE_NORMAL].  This worked fine in gtk 2.2/2.4, but now
with 2.6 the window isn't automatically repainted when a menu is popped up on
it.  I am *not* overriding the expose handler.

Screenshot provided by a user:
http://linuce.free.fr/Public/screenshots/xfce/xfce-cvs-gtk+-2.6.0.png
Comment 1 Owen Taylor 2004-12-17 19:36:23 UTC
Can you provide a small test case? Hard to tell from the description
if this is a bug or not. 
Comment 2 Owen Taylor 2004-12-17 19:37:54 UTC
(I should clarify, it's hard to tell if it's a bug *in GTK+* or not.
Obviously it's either a bug in GTK+ or in your widget.)
Comment 3 Brian Tarricone 2004-12-17 21:02:55 UTC
I'm at work now; I'll try to come up with a pared-down testcase when I get home.

However, I was just skimming through my code, and I re-noticed this series of
lines ("desktop" is the instance of my subclass):

/* ---------------------------- */
/* create a new style, attach it to the window, and add the new pixmap */
style = gtk_style_new();
gtk_style_attach(style, desktop->window);
if(style->bg_pixmap[GTK_STATE_NORMAL]) {
    /* if the style already has a BG pixmap set, ditch it */
    g_object_unref(G_OBJECT(style->bg_pixmap[GTK_STATE_NORMAL]));
}
style->bg_pixmap[GTK_STATE_NORMAL] = pmap;
/* set the widget's window style and queue it for drawing */
gtk_widget_set_style(desktop, style);
g_object_unref(G_OBJECT(style));
/* FIXME: all we should need to do is gtk_widget_queue_draw_area(), but that
 * isn't working for some reason */
/* gtk_widget_queue_draw_area(desktop, rect.x, rect.y, rect.width, rect.height);
    evt.type = GDK_EXPOSE;
evt.window = desktop->window;
evt.send_event = FALSE;
memcpy(&evt.area, &rect, sizeof(GdkRectangle));
evt.region = gdk_region_rectangle(&rect);
evt.count = 0;
gtk_widget_send_expose(desktop, (GdkEvent *)&evt);
gdk_region_destroy(evt.region);
/* ---------------------------- */

Sure, this looks kinda questionable.  When I originally wrote this, the event
creation and gtk_widget_send_expose() wasn't there, and I simply used
gtk_widget_queue_draw_area()  (yes, there's an unrelated reason I was using
draw_area() and not draw()).  But, that didn't work: dragging windows over the
desktop would cause the exposed portions to not be repainted.  Hence the manual
sending of a GdkEventExpose.

Regardless, the fact remains that this code works perfectly on gtk 2.2 and gtk
2.4, and only stopped working for 2.6.  At the very least, a behavior has
changed in gtk, and, unless I was unknowingly abusing an undocumented "feature",
I consider that a bug in gtk 2.6.

Either way, I'll play with this a bit when I get home and try to come up with
something smaller and self-contained that exhibits the behavior.
Comment 4 Brian Tarricone 2004-12-17 21:05:30 UTC
Err, sorry for the spam - it looks like I inadvertently cut something out of
there.  A repaste of the code section:

/* create a new style, attach it to the window, and add the new pixmap */
style = gtk_style_new();
gtk_style_attach(style, desktop->window);
if(style->bg_pixmap[GTK_STATE_NORMAL]) {
    /* if the style already has a BG pixmap set, ditch it */
    g_object_unref(G_OBJECT(style->bg_pixmap[GTK_STATE_NORMAL]));
}
style->bg_pixmap[GTK_STATE_NORMAL] = pmap;

/* set the widget's window style and queue it for drawing */
gtk_widget_set_style(desktop, style);
g_object_unref(G_OBJECT(style));
/* FIXME: all we should need to do is gtk_widget_queue_draw_area(), but that
 * isn't working for some reason */
/*gtk_widget_queue_draw_area(desktop, rect.x, rect.y, rect.width, rect.height);*/
evt.type = GDK_EXPOSE;
evt.window = desktop->window;
evt.send_event = FALSE;
memcpy(&evt.area, &rect, sizeof(GdkRectangle));
evt.region = gdk_region_rectangle(&rect);
evt.count = 0;
gtk_widget_send_expose(desktop, (GdkEvent *)&evt);
gdk_region_destroy(evt.region);
Comment 5 Owen Taylor 2004-12-17 21:48:43 UTC
There's a known change in GTK+-2.6 with menu handling ... we're more
aggressive about not clearing to the background when we know the 
application will redraw an area. GTK+-2.0/2.2/2.4 do that as well,
but in a more limited set of cases.

(This greatly reduces flashing when unmapping a menu.)

But that doesn't sound exactly like what you are doing ...  
gtk_window_expose() will properly redraw the areas. So, that's why we
need the test case... to figure out what is going on.

The code you've quoted is pretty dubious stuff... gtk_set_style()
can't be called by an application like that and 
gtk_widget_set_style() already queues a redraw. It doesn't loook like
it should cause this particular bug, but it might be working around
a bug elsewhere in your code that is also responsible for the menu
behavior.
Comment 6 Brian Tarricone 2004-12-18 11:32:05 UTC
I've been hacking around for a while, and I'm just beyond confused at this
point.  Setting the widget back to being double-buffered didn't help.  Catching
expose events and calling gtk_widget_queue_draw() made the problem worse. 
gdk_window_clear_area() didn't do a thing.  gdk_widget_set_back_pixmap() didn't
help either.

The only thing that returns us to normal operation is catching expose events and
manually drawing the pixmap with gdk_draw_drawable() (this is with
double-buffering disabled).  If this is the only recommended/blessed way of
doing something like this, then that's fine by me, and this bug can be closed.

Btw, I attempted to put together a small test case, and I was unable to get even
a simple GtkWindow to draw a GdkPixmap as its background.  Then again, I'm half
asleep and cross-eyed, so it's possible I'm being stupid.  To bed with me...
Comment 7 Owen Taylor 2004-12-18 16:05:52 UTC
Created attachment 34974 [details]
test case

Attached test case shows that:

 A) Doing the obvious thing works... as long as double buffering is on
    *or* GTK+ 
 B) By setting app_paintable and unsettings double_buffered it's possible
    to get the described symptoms. That's expected breakage, though a  
    a bit unpleasant, since it worked well enough with 2.4 that people
    may well have been doing it. ("I've set the window background, I
    don't want GTK+ redrawing the background again!")
 C) If GDK_EXPOSURE_MASK isn't set, then it's expected to work because
    GDK now knows you aren't going to redraw. But it doesn't. This
    is probably a GDK bug. I'll add another comment that explains what
    is going on.

I'm not sure where (if anywhere) your problem falls in the above
sequence.
Comment 8 Owen Taylor 2004-12-18 16:11:43 UTC
Soeren - the problem without GDK_EXPOSURE_MASK is that the code in 
gdk_window_process_updates_internal()

                  /* Windows without GDK_EXPOSURE_MASK still need to
                   * get their backgrounds painted, because it
                   * may have been temporarily set to None when
                   * the expose was generated
                   */
                  gdk_window_begin_paint_region (window, expose_region);
                  gdk_window_end_paint (window);

Doesn't work when unsetting a background isn't accompanied by invalidating
the window, because no exposure is genrerated. 

You could get a problem with an exposure event generated just as the window
background was unset during a scroll or resize even with the old unsetting.
But with the unsetting we do for GTK_WINDOW_TEMP that mismatch always happens.

I think we might have to skip unsetting  for windows without 
GDK_EXPOSURE_MASK... that's less than ideal because you could get 
circumstances where a menu disappears in two stages, but it will
give the right results.

Comment 9 Brian Tarricone 2004-12-19 23:14:26 UTC
Hmm, interesting.  I was wondering what app_paintable was for...

Anyway, it seems that gdk_window_set_back_pixmap() and connecting an
expose-event handler that does gdk_window_clear_area() works fine for my
purposes, though the fact that I'm not overriding the widget's GtkStyle anymore
means it needs a style-set handler as well to avoid losing the bg image when the
gtk theme changes.

So, is there really any bug here?  Sure, there are some subtle behavioral
changes (which my pedantic self wants to say are bugs, but I'll relent), but it
appears that there's a "more correct" way of doing things that work on all
versions of gtk2, so...
Comment 10 Owen Taylor 2004-12-20 16:21:04 UTC
gtk_window_set_app_paintable() just disables GtkWindow's default expose
handler.

Comment 8 describes what I consider to be a real bug discovered when
investigating your problem. It probably wasn't actually what you
were encountering.

The changes made in 2.6 are definitely not bugs, in that they were
intentional, and they don't break any code that we would have said
was correct before hand. But they are certainly a bit marginal in
being acceptable compatible with existing code. If they didn't make
quite large improvements in the appearance of GTK+, we certainly 
wouldn't have made them.
Comment 11 Soren Sandmann Pedersen 2004-12-22 17:25:44 UTC
Created attachment 35133 [details] [review]
Possible fix for problem described in comment 8
Comment 12 Owen Taylor 2004-12-22 18:17:19 UTC
Patch looks right to me. It would break if the exposure mask got
added between unset and reset, but we don't need to worry about that,
do we?
Comment 13 Soren Sandmann Pedersen 2004-12-22 19:21:31 UTC
We don't need to worry about that because we only do the setting/unsetting in
places where we know what is going on in between the calls (ie. no callbacks to
application code etc.)


Wed Dec 22 14:14:02 2004  Søren Sandmann  <sandmann@redhat.com>

	Bug #161561
	
	* gdk/x11/gdkwindow-x11.c (tmp_{re,un}set_bg): Only set/reset the
	background for windows that get expose events. 

	* gdk/gdkwindow.c (gdk_window_process_updates_internal): Remove
	hack to repaint !expose windows.