GNOME Bugzilla – Bug 161561
GtkWindow subclass not automatically repainting style->bg_pixmap
Last modified: 2004-12-22 21:47:04 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
Can you provide a small test case? Hard to tell from the description if this is a bug or not.
(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.)
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.
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);
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.
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...
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.
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.
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...
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.
Created attachment 35133 [details] [review] Possible fix for problem described in comment 8
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?
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.