GNOME Bugzilla – Bug 113310
Unset bg when mapping override-redirect toplevels
Last modified: 2011-02-04 16:17:18 UTC
To reduce flicker, GtkMenu's windows should have a background of None so they won't be gray for a moment before drawing the menu.
Actually, thinking about it a little more, I've changed my mind, and I thinki we should do something generically for all override-redirect toplevels. But instead of setting bg=NONE permanently, I think we should do a recursive gdk_window_tmp_unset_bg() before mapping the window, and then reset the backgrounds afterwards. This is very much similar to what we do when moving windows (and perhaps we should also do it when mapping child windows... that might reduce flicker a bit when switching notebook tabs with entries in them, though you still get flicker on unmap, and that's probably more noticeable.) Retitling accordingly, though feel free to change back if you disagree.
If we did this when mapping child windows, what about mapping the new windows before unmapping the old when switching pages in a notebook? The effect would be to inhibit the clearing to background from any unmaps of windows occuppying the same position. I think this could be done with good effect for drop-down menus too. When you move the mouse across a menu bar, if you map the new menu before unmapping the old, and the new menu had bg=none, you wouldn't see a flash from clearing to the background before drawing the new menu. It may also be worth considering whether to set bg=None for the parent of the unmapped window. That way we won't get any flashing at all.
I don't see how mapping the new before unmapping the old fixes the problem for either dialogs or menus. For notebooks, frequently you have a situation where there is one entry on the old page and no entries on the new page. I guess it would help for the case of switching between near-identical notebook pages. For menus, there is typically only a small overlap between the old and new menus. The idea of unsetting the parent background on subwindow unmap seems sound, and should completely fix all GtkNotebook flicker. I suppose there is a small win to the map/unmap-ordering for the menus case, but the idea of further complicating the logic in GtkMenu* just doesn't appeal to me.
Created attachment 24033 [details] [review] A proof-of-concept patch
The patch I just attached does three things: 1) predictive expose when mapping override redirect and child windows (see bug 113040) 2) temporarily unsets its background before mapping override redirect and child windows 3) when unmapping a window, temporarily unset the background of _all_ known windows intersection the same position on the screen. The idea behind (3) is that when you unmap a menu, it will usually be on top of an application window, so unsetting the background of that window will prevent an annoying clear-to-background. For things like gnome-terminal where the application window is white and the menus are gray, the effect is noticably less flicker. Also the patch eliminates the annoying flicker when switching notebook pages with subwindows in them. Gnumeric is good example of that. I consider this patch only proof-of-concept because I think it does too much window walking, and the hack to allow recursive invalidation of the root window is probably not such a good idea.
If it wasn't clear, the patch does predictive exposes for all known windows intersecting the position of the unmapped window.
After trying the patch for a while, I don't think the predictive exposing of all known windows is good, because if you have many overlapping toplevels from the same application, popping down a menu in one of them will cause all other toplevels to be redrawn. In the case of nautilus and list views this is very noticable. The recursive bg-unsetting of all known windows is a nice effect and can be done quickly. (It is just noise on the profile). One possible problem is that the gtk+ version of Emacs relies on the assumption that exposed areas are being cleared to the background without double buffering. This is not true currently because we already do the bg-unsetting trick in various places, but applying this patch makes it much more apparent (you can see menu leftovers on the text area in Emacs).
Created attachment 27122 [details] [review] New patch that doesn't do aggressive invalidation
Created attachment 27126 [details] [review] New patch that doesn't do aggressive invalidation Sorry, that wasn't right. Here is the patch.
Let's go ahead and commit this to HEAD. I think the gtk-emacs assumption is just broken and they need to fix their code. (The fact that they haven't released a stable version of emacs with that code makes it easier for me to go ahead and say this.) Comments: - I'd like to see viewability checks here; we could be spending a lot of time tweaking the background of windows we know aren't viewable. - For pre_unmap(), for GDK_WINDOW_CHILD(), start_window just needs to be the parent, not the toplevel. Quibble: - 'list' as iterator variable.
I noticed a bug with this patch too. Windows without EXPOSURE in their event_mask may not get their background drawn. Fixed in the new patch.
Created attachment 28642 [details] [review] op
I think that we shouldn't unset the background for windows without the EXPOSURE mask - if you unset the EXPOSURE mask, to me, that says that you want what the X server is going to draw. What do you think about moving the viewability checks into the unset_bg/reset_bg calls? I'm a little nervous not having the checks at all in the unmap case ... we might be doing a *lot* of X traffic on windows that we know aren't viewable in some cases. (Unfortunately, the place that it would help most - in other toplevel windows - doesn't work, since managed toplevel windows always need to be considered viewable.)
Created attachment 29390 [details] [review] The committed patch I don't think it's a good idea to move the viewability checks into unset_bg/reset_bg, because that would make the whole operation O(d^2) where d is the depth of the window hierarchy. What I did instead was to just stop the recursion when we see an unmapped window. This works because a window never changes mapped state between unset and reset, not even when we are unmapping a window. (In that case the mapped state is calculated before pre_unmap()). I committed the attached patch.