GNOME Bugzilla – Bug 412882
gdkwindow should support the concept of "composited"
Last modified: 2007-11-28 23:14:14 UTC
if you take a look at gdk_window_invalidate_maybe_recurse() it contains code to clip the invalidate region when child windows overlap it. the logic goes like this: if the child window is not input-only: subtract the area that the child overlaps the parent from the redraw region if (recurse) emit expose event on the child for this region of course, when a child is composited, we really want the expose event to go to the parent and not at all to the child. for this reason, gdkwindows should support the concept of being "composited". for now, all i really need is for this to be a flag that can be flipped on and off. no support is required for making the XComposite calls, but perhaps this could be added too. the exact semantics of this is that the logic in gdk_window_invalidate_maybe_recurse would treat composited windows _exactly_ as it treats input-only windows. specifically: 1) the parts of child windows overlapping their parent would not be clipped from the expose region for the parent 2) the child windows would never receive expose events as a result of their parent window being invalidated. point 1 really needs to be done. point 2 is a bit more up for debate. some comments on 2: a) the child doesn't need these expose events since it is composited (and therefore pixmap-backed). chances are that people who are playing around with composited windows will be quite aware of exactly what they want to do with them and can therefore send these events themselves, if need be. b) if you send the child expose events it causes the child to redraw itself. this causes damage notification which will cause the parent to want to invalidate itself again (to recomposite the child's contents). in the best case this leads to extra processing and if you're not careful it can cause infinite loops. c) on the other hand, if the user calls invalidate with recurse set to TRUE it's probably because they want their child windows to receive expose events. the real problem in this debate, of course, is that gtk internally generates invalidates. when the user calls invalidate, they can just do what they want to do -- but they might get angry at what gtk does.
oh. i forgot to address an issue: you might say something like the following: "if the invalidate recurses to children then the child windows will receive expose events and redraw themselves, generating damage notifications. the code watching for these notifications could then use _this_ notification to draw the updated image." this fails to be true when the entire body of the composited window is overlapped by a non-composited window like a GtkPlug in another process. the draw will be entirely clipped by the opaque sub-child window (as it should be). even if the draw occured, it would be obscured by the window. then you say "well. that's fine, since the child will cause the invalidate on the parent to be obscured but the child will receive the event and draw itself!" of course, in the case of a GtkPlug, the window belongs to another process so there is no way to even propagate the invalidate to it (without sending synthetic expose events or something -- but this is _REALLY_ the wrong solution).
Not sure what the concrete API proposal is here. In terms of: 2) the child windows would never receive expose events as a result of their parent window being invalidated. This is a misunderstanding of what the invalidate_children flag to gdk_window_invalidate_rect(). The only reason anybody would ever invalidate descendants is because the *contents* of the descendants changed. That is unaffected by whether we are compositing those descendants or not. See the usage in gtk/ for examples.
Created attachment 84812 [details] [review] xcomposite support in gdk while talking with owen it was decided that it would be better to implement the actual composite/damage stuff in gdk itself. this first crack at a patch does that. feedback is welcome.
Looks reasonable at first glance. No need to update the linux-fb backend though. Do you have a simple example using this ?
Putting one more comment from IRC here: it may be better to handle the damage events in gdk_event_translate instead of adding an event filter for each composited window.
Another thing that is really needed here is an example in the api docs that shows how to handle a composited child window in an expose handler.
Created attachment 84934 [details] [review] xcomposite support in gdk changed the following things: - no longer modify linux-fb - erased the filter func - moved that code to the gdk_event_translate function (using the example of how xkb and xfixes events are handled there) as for the docs -- it's not really possible to implement a proper expose event handler until the cairo patch lands: http://bugs.freedesktop.org/show_bug.cgi?id=10329
heh. please ignore that business about the trayicon. :) i was experimenting and it's included by accident.
+ if (!gdk_display_supports_composite (display) && composited) + { Indentation problem? Slightly more importantly, the docs for set_composited() should be written in abstract terms, and not talk about X. - Describe what a composited window is (drawn to an offscreen buffer, application is responsible for painting on the parent) - Say that it isn't universally supported on all window systems or window system versions and how to check - Mention that it only works for child windows But my main comment would be on the local case; right now, it's a round-trip to propagate changes to a child owned by the same GDK process. It would be cool to short-circuit that. I think that consists of: - Automatically generating invalidations on end_paint() for the child (I wouldn't bother with non double-buffered operations) - When process updates for a window or all windows, process composited children before parents - Remove invalidations from Damage events with the anti-expose queue the same way we remove invalidations for expose events.
Created attachment 85736 [details] [review] xcomposite support in gdk new patch
Created attachment 85737 [details] test case for the new patch owen: here is a test case for the new patch (which i've added a bunch of debug output to for demonstration purposes). here is what should happen: 0. application requests a redraw (for example, when you mouseover the button) 1. gdk window paint begin on the affected area 2. blah blah blah 3. gdk window paint end 4. i report expose event on the parent window for the painted area _only_ (some subset of the area covered by the composited window) 5. gdk queues antiexpose for this region 6. gdk emits expose event on the parent for this region 7. blah blah blah 8. gdk goes to sleep 9. xdamage event comes in from the server 10. antiexpose area is subtracted from it 11. region is now empty so we don't redraw. in reality, though, by the time we get to 11 the region is _not_ empty. the X server appears to over-report damage. specifically, in this case, it reports that the entire composited window has been damaged when really only a subset of it has been. this means that the region is non-empty even after the antiexpose is subtracted from it and an expose event still gets dispatched on the parent (for the part of the child that was not affected by the gdk window end paint). maybe i've just made a mistake, though.
one more thing: i can't figure out a way to get the damage region from the server (as opposed to the bounding rect) and even if i could, it would be an extra round-trip.....
Created attachment 85745 [details] [review] xcomposite support in gdk when using the NonEmpty trigger for damage reports the X server sends a null region as the damage event. it appears that this gets clipped to the window (or something... i don't really fully understand it). in any case, we can work around this and get X to report the actual area by registering with a BoundingBox trigger. doing this requires that we are a little bit more careful with respect to reporting which areas we will be repairing so the subtract code is changed a bit too.
Created attachment 85746 [details] updated test case here is a better test case. it draws a small rectangle "dot" once per second to test that draws outside of doublebuffering work too.
Created attachment 85748 [details] [review] xcomposite support in gdk (clean) this is the version of the patch that i would commit. it differs from the other version: - all debugging output is removed - style fixups (i had a lot of spaces instead of tabs) - slight rework of the for loop in the backing store commit code
Patch looks good to me, although I can't really test it, since it requires a patched X server. It would be good to check for a new enough version of the composite extension once the X patch actually makes it into a release. I still think that the docs for set_composited() need an example that shows in what way the contents of the redirected child can be merged into the parent.
Ryan, did I recently see a mail by keithp saying that he committed the X server change that this requires ? I think he also mentioned bumping the minor version of the composite extension, so you may want to update your patch to check for that.
Created attachment 88645 [details] example application screenshot
Created attachment 88646 [details] composite example code this is the example code that i propose including. it's small, simple, self-contained and documented. you can compile it on its own with no extra code and it works.
prefix the example with the following explanation: /* An example application showing a possible use of composited GDK * windows in a GTK application. * * In the example, a button is placed inside of an event box inside of * a window. The event box is set as 'composited' and therefore is no * longer automatically drawn to the screen. * * When the contents of the event box change, an expose event is * generated on its parent window (which, in this case, belongs to * the toplevel GtkWindow). The expose handler for this widget is * responsible for merging the changes back on the screen in the way * that it wishes. * * In our case, we merge the contents with a 50% transparency. We * also set the background colour of the window to red. The effect is * that the background shows through the button. * * We set the background of the event box to be fully-transparent so * that the default GTK background colour from the event box isn't * composited along with the button (think: if the current theme draws * non-rectangular buttons). */
Here is an integrated patch; looks ok ?
Created attachment 89156 [details] [review] my patch
looks good to me. we can add the version-check stuff later once we hear back from keith -- probably just a one-off g_warning when you try to set your first window as composited. thanks for the gtktest work. want me to write up a changelog and commit or would you prefer to?
about calling set_composited on a toplevel: it works. if you setup your own Damage, it might even be a meaningful thing to do... i really can't think of how i'd use it, though. of course, things may become confused if you set it, then xcompmgr sets it then you unset it, ... we could add a check to proscribe people from doing it, but i think it's ok. i was careful in the double-buffer commit code to make sure that windows with NULL parents didn't cause problems...
oh. and maybe a note in the demo code that merging the content of your child if your child has children of its own only works properly with (unreleased) cairo 1.6 :)
2007-06-01 Matthias Clasen <mclasen@redhat.com> Add support for composited child windows. (#412882, Ryan Lortie) * gdk/gdk.symbols: * gdk/gdkdisplay.h: * gdk/gdkinternals.h: * gdk/gdkwindow.[hc]: Add gdk_display_supports_composite() and gdk_window_set_composited(). * gdk/x11/gdkevents-x11.c: * gdk/x11/gdkdisplay-x11.[hc]: * gdk/x11/gdkwindow-x11.[hc]: X11 implementation. * gdk/win32/gdkdisplay-win32.c: * gdk/win32/gdkwindow-win32.c: Dummy win32 implementration. * gdk/quartz/gdkdisplay-quartz.c: * gdk/quartz/gdkwindow-quartz.c: Dummy Quartz implementation. * gdk/directfb/gdkdisplay-directfb.c: * gdk/directfb/gdkwindow-directfb.c: Dummy DirectFB implementation.
thanks so much for sticking with this one for so long :) cheers
Folks this is sweet stuff. Have a look at the slick things we can do now... http://macslow.thepimp.net/?p=147 code-example will follow soon.