Index: configure.in
===================================================================
--- configure.in	(revision 17540)
+++ configure.in	(working copy)
@@ -1295,6 +1295,24 @@
     GTK_PACKAGES_FOR_X="$GTK_PACKAGES_FOR_X xfixes"
   fi
 
+  # Checks for Xcomposite extension
+
+  if $PKG_CONFIG --exists xcomposite ; then
+    AC_DEFINE(HAVE_XCOMPOSITE, 1, Have the XCOMPOSITE X extension)
+
+    X_PACKAGES="$X_PACKAGES xcomposite"
+    GTK_PACKAGES_FOR_X="$GTK_PACKAGES_FOR_X xcomposite"
+  fi
+
+  # Checks for Xdamage extension
+
+  if $PKG_CONFIG --exists xdamage ; then
+    AC_DEFINE(HAVE_XDAMAGE, 1, Have the XDAMAGE X extension)
+
+    X_PACKAGES="$X_PACKAGES xdamage"
+    GTK_PACKAGES_FOR_X="$GTK_PACKAGES_FOR_X xdamage"
+  fi
+
   if $have_base_x_pc ; then
     GDK_EXTRA_LIBS="$x_extra_libs"
   else
Index: ChangeLog
===================================================================
--- ChangeLog	(revision 17540)
+++ ChangeLog	(working copy)
@@ -1,3 +1,28 @@
+2007-03-17  Ryan Lortie  <desrt@desrt.ca>
+
+	* configure.in: add checks for xcomposite, xdamage extensions
+	* gdk/gdk.symbols: new public functions
+	gdk_display_supports_composite(), gdk_window_set_composited()
+	* gdk/gdkdisplay.h: add gdk_display_supports_composite()
+	* gdk/gdkwindow.h: add gdk_window_set_composited() and 'composited'
+	flag in GtkWindowObject
+	* gdk/gdkinternals.h: add _gdk_windowing_window_set_composited ()
+	* gdk/gdkwindow.c (gdk_window_set_composited),
+	(gdk_window_invalidate_maybe_recurse): new function for setting a
+	gdkwindow as 'composited'.  also, do not clip composited windows out
+	of the invalidated region of their parent.
+	* gdk/x11/gdkdisplay-x11.c (gdk_display_open),
+	(gdk_display_supports_composite): query for xcomposite and xdamage
+	extensions and add api to check for these abilities
+	* gdk/x11/gdkwindow-x11.c: add the actual composite/damage code by
+	implementing _gdk_windowing_window_set_composited
+	* gdk/directfb/gdkdisplay-directfb.c:
+	* gdk/directfb/gdkwindow-directfb.c:
+	* gdk/win32/gdkdisplay-win32.c:
+	* gdk/win32/gdkwindow-win32.c:
+	* gdk/quartz/gdkdisplay-quartz.c:
+	* gdk/quartz/gdkwindow-quartz.c: add stubs for compositing functions
+
 2007-03-17  Chris Wilson  <chris@chris-wilson.co.uk>
 
 	* gdk/x11/gdkvisual-x11.c (_gdk_visual_init):
Index: gdk/directfb/gdkdisplay-directfb.c
===================================================================
--- gdk/directfb/gdkdisplay-directfb.c	(revision 17540)
+++ gdk/directfb/gdkdisplay-directfb.c	(working copy)
@@ -491,6 +491,13 @@
 {
 }
 
+
+gboolean
+gdk_display_supports_composite (GdkDisplay *display)
+{
+    return FALSE;
+}
+
 #define __GDK_DISPLAY_X11_C__
 #include "gdkaliasdef.c"
 
Index: gdk/directfb/gdkwindow-directfb.c
===================================================================
--- gdk/directfb/gdkwindow-directfb.c	(revision 17540)
+++ gdk/directfb/gdkwindow-directfb.c	(working copy)
@@ -2997,6 +2997,12 @@
   gdk_display_beep (gdk_display_get_default());
 }
 
+void
+_gdk_windowing_window_set_composited (GdkWindow *window,
+                                      gboolean composited)
+{
+}
+
 #define __GDK_WINDOW_X11_C__
 #include "gdkaliasdef.c"
 
Index: gdk/quartz/gdkdisplay-quartz.c
===================================================================
--- gdk/quartz/gdkdisplay-quartz.c	(revision 17540)
+++ gdk/quartz/gdkdisplay-quartz.c	(working copy)
@@ -165,3 +165,11 @@
 {
   /* FIXME: Implement */
 }
+
+
+gboolean
+gdk_display_supports_composite (GdkDisplay *display)
+{
+  /* FIXME: Implement */
+  return FALSE;
+}
Index: gdk/quartz/gdkwindow-quartz.c
===================================================================
--- gdk/quartz/gdkwindow-quartz.c	(revision 17540)
+++ gdk/quartz/gdkwindow-quartz.c	(working copy)
@@ -2016,3 +2016,9 @@
 {
   gdk_display_beep (_gdk_display);
 }
+
+void
+_gdk_windowing_window_set_composited (GdkWindow *window,
+                                      gboolean composited)
+{
+}
Index: gdk/gdkinternals.h
===================================================================
--- gdk/gdkinternals.h	(revision 17540)
+++ gdk/gdkinternals.h	(working copy)
@@ -337,6 +337,9 @@
 void _gdk_windowing_display_set_sm_client_id (GdkDisplay  *display,
 					      const gchar *sm_client_id);
 
+void _gdk_windowing_window_set_composited (GdkWindow *window,
+					   gboolean composited);
+
 #define GDK_TYPE_PAINTABLE            (_gdk_paintable_get_type ())
 #define GDK_PAINTABLE(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), GDK_TYPE_PAINTABLE, GdkPaintable))
 #define GDK_IS_PAINTABLE(obj)	      (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GDK_TYPE_PAINTABLE))
Index: gdk/gdkdisplay.h
===================================================================
--- gdk/gdkdisplay.h	(revision 17540)
+++ gdk/gdkdisplay.h	(working copy)
@@ -181,6 +181,7 @@
 
 gboolean gdk_display_supports_shapes           (GdkDisplay    *display);
 gboolean gdk_display_supports_input_shapes     (GdkDisplay    *display);
+gboolean gdk_display_supports_composite        (GdkDisplay    *display);
 
 G_END_DECLS
 
Index: gdk/gdk.symbols
===================================================================
--- gdk/gdk.symbols	(revision 17540)
+++ gdk/gdk.symbols	(working copy)
@@ -465,6 +465,7 @@
 gdk_display_supports_selection_notification
 gdk_display_supports_shapes
 gdk_display_supports_input_shapes
+gdk_display_supports_composite
 #endif
 #endif
 
@@ -672,6 +673,7 @@
 gdk_window_set_debug_updates
 gdk_window_set_user_data
 gdk_window_thaw_updates
+gdk_window_set_composited
 #endif
 #endif
 
Index: gdk/gdkwindow.c
===================================================================
--- gdk/gdkwindow.c	(revision 17540)
+++ gdk/gdkwindow.c	(working copy)
@@ -1042,6 +1042,7 @@
 {
 #ifdef USE_BACKING_STORE
   GdkWindowObject *private = (GdkWindowObject *)window;
+  GdkWindowObject *composited;
   GdkWindowPaint *paint;
   GdkGC *tmp_gc;
   GdkRectangle clip_box;
@@ -1081,6 +1082,9 @@
   gdk_gc_set_clip_region (tmp_gc, paint->region);
   gdk_gc_set_clip_origin (tmp_gc, - x_offset, - y_offset);
 
+  g_print ("draw drawable %s (%d %d %d %d)\n",
+           (const char *) g_object_get_data (window, "debug-name"),
+           clip_box.x, clip_box.y, clip_box.width, clip_box.height);
   gdk_draw_drawable (private->impl, tmp_gc, paint->pixmap,
                      clip_box.x - paint->x_offset,
                      clip_box.y - paint->y_offset,
@@ -1094,6 +1098,39 @@
   g_object_unref (paint->pixmap);
   gdk_region_destroy (paint->region);
   g_free (paint);
+
+  /* find a composited window in our hierarchy to signal its parent to redraw.
+   * by the time we are finished, 'composited' should be set to the composited
+   * window (or NULL) and clip_box should be set to the region that was just
+   * drawn.
+   */
+
+  for (composited = private; composited->parent; composited = composited->parent)
+    {
+      int width, height;
+
+      gdk_drawable_get_size (GDK_DRAWABLE (composited->parent), &width, &height);
+      clip_box.x += composited->x;
+      clip_box.y += composited->y;
+      clip_box.width = MIN (clip_box.width, width - clip_box.x);
+      clip_box.height = MIN (clip_box.height, height - clip_box.y);
+
+      g_print ("  ... %s (%d %d %d %d)\n",
+               (const char *) g_object_get_data (composited->parent, "debug-name"),
+               clip_box.x, clip_box.y, clip_box.width, clip_box.height);
+
+      if (composited->composited)
+        break;
+    }
+
+  if (composited->parent)
+    {
+      g_print ("so i draw.\n\n");
+      gdk_window_invalidate_rect (GDK_WINDOW (composited->parent),
+                                  &clip_box, FALSE);
+    }
+  else
+    g_print ("no draw this time.\n\n");
 #endif /* USE_BACKING_STORE */
 }
 
@@ -2301,7 +2338,10 @@
               gdk_display_sync (gdk_drawable_get_display (window));
               g_usleep (70000);
             }
-          
+         
+	  g_print ("queue antiexpose on %s\n",
+                   g_object_get_data (G_OBJECT (window), "debug-name"));
+
 	  save_region = _gdk_windowing_window_queue_antiexpose (window, update_area);
 
 	  if (save_region)
@@ -2601,7 +2641,8 @@
 	  child_region = gdk_region_rectangle (&child_rect);
 	  
 	  /* remove child area from the invalid area of the parent */
-	  if (GDK_WINDOW_IS_MAPPED (child) && !child->shaped)
+	  if (GDK_WINDOW_IS_MAPPED (child) && !child->shaped &&
+              !child->composited)
 	    gdk_region_subtract (visible_region, child_region);
 	  
 	  if (child_func && (*child_func) ((GdkWindow *)child, user_data))
@@ -3056,5 +3097,59 @@
   return gdk_window_foreign_new_for_display (gdk_display_get_default (), anid);
 }
 
+/**
+ * gdk_window_set_composited:
+ * @window: a #GdkWindow
+ *
+ * Sets a #GdkWindow as composited.  Composited windows do
+ * not automatically have their contents drawn to the screen.
+ * Drawing is redirected to an offscreen buffer and an expose
+ * event is emitted on the parent of the composited window.
+ * It is the responsibility of the parent's expose handler to
+ * manually merge the off-screen content onto the screen in
+ * whatever way it sees fit.
+ *
+ * It only makes sense for child windows to be composited.
+ *
+ * An additional effect of this call is that the area of this
+ * window is no longer clipped from regions marked for
+ * invalidation on its parent.  Draws done on the parent
+ * window are also no longer clipped by the child.
+ *
+ * This call is only supported on some systems (currently,
+ * only X11 with Xcomposite and Xdamage extensions).  You
+ * must call gdk_display_supports_composite() to check if
+ * setting a window as composited is supported before
+ * attempting to do so.
+ */
+void
+gdk_window_set_composited (GdkWindow *window,
+                           gboolean composited)
+{
+  GdkWindowObject *private = (GdkWindowObject *)window;
+  GdkDisplay *display;
+
+  g_return_if_fail (window != NULL);
+  g_return_if_fail (GDK_IS_WINDOW (window));
+
+  composited = composited != FALSE;
+
+  if (private->composited == composited)
+    return;
+
+  display = gdk_drawable_get_display (GDK_DRAWABLE (window));
+
+  if (!gdk_display_supports_composite (display) && composited)
+    {
+      g_warning ("gdk_window_set_composited called but "
+                 "compositing is not supported");
+      return;
+    }
+
+  _gdk_windowing_window_set_composited (window, composited);
+
+  private->composited = composited;
+}
+
 #define __GDK_WINDOW_C__
 #include "gdkaliasdef.c"
Index: gdk/gdkwindow.h
===================================================================
--- gdk/gdkwindow.h	(revision 17540)
+++ gdk/gdkwindow.h	(working copy)
@@ -287,6 +287,7 @@
   guint guffaw_gravity : 1;
   guint input_only : 1;
   guint modal_hint : 1;
+  guint composited : 1;
   
   guint destroyed : 2;
 
@@ -392,6 +393,9 @@
  */
 void gdk_window_set_child_shapes (GdkWindow *window);
 
+void gdk_window_set_composited   (GdkWindow *window,
+                                  gboolean composited);
+
 /*
  * This routine allows you to merge (ie ADD) child shapes to your
  * own window's shape keeping its current shape and ADDING the child
Index: gdk/win32/gdkwindow-win32.c
===================================================================
--- gdk/win32/gdkwindow-win32.c	(revision 17540)
+++ gdk/win32/gdkwindow-win32.c	(working copy)
@@ -3372,3 +3372,9 @@
 {
   gdk_display_beep (_gdk_display);
 }
+
+void
+_gdk_windowing_window_set_composited (GdkWindow *window,
+                                      gboolean composited)
+{
+}
Index: gdk/win32/gdkdisplay-win32.c
===================================================================
--- gdk/win32/gdkdisplay-win32.c	(revision 17540)
+++ gdk/win32/gdkdisplay-win32.c	(working copy)
@@ -389,3 +389,9 @@
 
   return FALSE;
 }
+
+gboolean
+gdk_display_supports_composite (GdkDisplay *display)
+{
+  return FALSE;
+}
Index: gdk/x11/gdkdisplay-x11.c
===================================================================
--- gdk/x11/gdkdisplay-x11.c	(revision 17540)
+++ gdk/x11/gdkdisplay-x11.c	(working copy)
@@ -54,7 +54,15 @@
 #include <X11/extensions/shape.h>
 #endif
 
+#ifdef HAVE_XCOMPOSITE
+#include <X11/extensions/Xcomposite.h>
+#endif
 
+#ifdef HAVE_XDAMAGE
+#include <X11/extensions/Xdamage.h>
+#endif
+
+
 static void   gdk_display_x11_dispose            (GObject            *object);
 static void   gdk_display_x11_finalize           (GObject            *object);
 
@@ -206,6 +214,29 @@
 #endif
     display_x11->have_xfixes = FALSE;
 
+#ifdef HAVE_XCOMPOSITE
+  if (XCompositeQueryExtension (display_x11->xdisplay,
+                                &ignore, &ignore))
+      display_x11->have_xcomposite = TRUE;
+  else
+#endif
+    display_x11->have_xcomposite = FALSE;
+
+#ifdef HAVE_XDAMAGE
+  if (XDamageQueryExtension (display_x11->xdisplay,
+                             &display_x11->xdamage_event_base,
+                             &ignore))
+    {
+      display_x11->have_xdamage = TRUE;
+
+      gdk_x11_register_standard_event_type (display,
+                                            display_x11->xdamage_event_base,
+                                            XDamageNumberEvents);
+    }
+  else
+#endif
+    display_x11->have_xdamage = FALSE;
+
   display_x11->have_shapes = FALSE;
   display_x11->have_input_shapes = FALSE;
 #ifdef HAVE_SHAPE_EXT
@@ -1318,7 +1349,6 @@
   return GDK_DISPLAY_X11 (display)->have_input_shapes;
 }
 
-
 /**
  * gdk_x11_display_get_startup_notification_id:
  * @display: a #GdkDisplay
@@ -1334,5 +1364,29 @@
   return GDK_DISPLAY_X11 (display)->startup_notification_id;
 }
 
+/**
+ * gdk_display_supports_composite:
+ * @display: a #GdkDisplay
+ *
+ * Returns %TRUE if gdk_window_set_composited() can be used
+ * to redirect drawing on the window using compositing.
+ *
+ * Currently this only works on X11 with XComposite and
+ * XDamage extensions available.
+ *
+ * Returns: %TRUE if windows may be composited.
+ *
+ * Since: 2.12
+ */
+gboolean
+gdk_display_supports_composite (GdkDisplay *display)
+{
+  GdkDisplayX11 *x11_display = GDK_DISPLAY_X11 (display);
+
+  return x11_display->have_xcomposite &&
+         x11_display->have_xdamage &&
+         x11_display->have_xfixes;
+}
+
 #define __GDK_DISPLAY_X11_C__
 #include "gdkaliasdef.c"
Index: gdk/x11/gdkdisplay-x11.h
===================================================================
--- gdk/x11/gdkdisplay-x11.h	(revision 17540)
+++ gdk/x11/gdkdisplay-x11.h	(working copy)
@@ -81,6 +81,10 @@
   gboolean have_xfixes;
   gint xfixes_event_base;
 
+  gboolean have_xcomposite;
+  gboolean have_xdamage;
+  gint xdamage_event_base;
+
   /* If the SECURITY extension is in place, whether this client holds 
    * a trusted authorization and so is allowed to make various requests 
    * (grabs, properties etc.) Otherwise always TRUE. */
Index: gdk/x11/gdkgeometry-x11.c
===================================================================
--- gdk/x11/gdkgeometry-x11.c	(revision 17540)
+++ gdk/x11/gdkgeometry-x11.c	(working copy)
@@ -1192,6 +1192,9 @@
 		    }
 		  else		/* anti-expose */
 		    {
+                      g_print ("  doing antiexpose %s\n",
+                               g_object_get_data (G_OBJECT (window),
+                                                  "debug-name"));
 		      gdk_region_subtract (invalidate_region, item->u.antiexpose.area);
 		    }
 		}
@@ -1209,7 +1212,12 @@
   gdk_region_intersect (invalidate_region, clip_region);
 
   if (!gdk_region_empty (invalidate_region))
+  {
+    g_print ("non-empty region so delivering expose event.\n");
     gdk_window_invalidate_region (window, invalidate_region, FALSE);
+  }
+  else
+    g_print ("region is now empty, no delivery.\n");
   
   gdk_region_destroy (invalidate_region);
   gdk_region_destroy (clip_region);
Index: gdk/x11/gdkevents-x11.c
===================================================================
--- gdk/x11/gdkevents-x11.c	(revision 17540)
+++ gdk/x11/gdkevents-x11.c	(working copy)
@@ -2101,6 +2101,39 @@
 	}
       else
 #endif
+#if defined(HAVE_XCOMPOSITE) && defined (HAVE_XDAMAGE) && defined (HAVE_XFIXES)
+      if (display_x11->have_xdamage && window_private->composited &&
+          xevent->type == display_x11->xdamage_event_base + XDamageNotify)
+        {
+          XDamageNotifyEvent *damage_event = (XDamageNotifyEvent *) xevent;
+          XserverRegion parts;
+          GdkRectangle rect;
+
+          rect.x = window_private->x + damage_event->area.x;
+          rect.y = window_private->y + damage_event->area.y;
+          rect.width = damage_event->area.width;
+          rect.height = damage_event->area.height;
+
+          parts = XFixesCreateRegion (display_x11->xdisplay, 0, 0);
+          XDamageSubtract (display_x11->xdisplay,
+                           window_impl->damage,
+                           None, parts);
+          XFixesDestroyRegion (display_x11->xdisplay, parts);
+
+          if (window_private->parent != NULL)
+            g_print ("damage on %s -> expose on %s (%d %d %d %d)\n",
+                     (const char *) g_object_get_data (G_OBJECT (window),
+                                                       "debug-name"),
+                     (const char *) g_object_get_data (G_OBJECT (window_private->parent),
+                                                       "debug-name"),
+                     rect.x, rect.y, rect.width, rect.height);
+	    _gdk_window_process_expose (GDK_WINDOW (window_private->parent),
+                                        damage_event->serial, &rect);
+
+          return_val = TRUE;
+        }
+      else
+#endif
 	{
 	  /* something else - (e.g., a Xinput event) */
 	  
Index: gdk/x11/gdkwindow-x11.c
===================================================================
--- gdk/x11/gdkwindow-x11.c	(revision 17540)
+++ gdk/x11/gdkwindow-x11.c	(working copy)
@@ -59,6 +59,18 @@
 #include <X11/extensions/shape.h>
 #endif
 
+#ifdef HAVE_XCOMPOSITE
+#include <X11/extensions/Xcomposite.h>
+#endif
+
+#ifdef HAVE_XFIXES
+#include <X11/extensions/Xfixes.h>
+#endif
+
+#ifdef HAVE_XDAMAGE
+#include <X11/extensions/Xdamage.h>
+#endif
+
 const int _gdk_event_mask_table[21] =
 {
   ExposureMask,
@@ -185,6 +197,14 @@
 
   _gdk_xgrab_check_destroy (GDK_WINDOW (wrapper));
 
+#if defined(HAVE_XCOMPOSITE) && defined(HAVE_XDAMAGE) && defined (HAVE_XFIXES)
+  if (window_impl->damage != None)
+  {
+    XDamageDestroy (GDK_WINDOW_XDISPLAY (object), window_impl->damage);
+    window_impl->damage = None;
+  }
+#endif
+
   if (!GDK_WINDOW_DESTROYED (wrapper))
     {
       GdkDisplay *display = GDK_WINDOW_DISPLAY (wrapper);
@@ -6379,5 +6399,38 @@
     gdk_display_beep (display);
 }
 
+void
+_gdk_windowing_window_set_composited (GdkWindow *window,
+                                      gboolean composited)
+{
+#if defined(HAVE_XCOMPOSITE) && defined(HAVE_XDAMAGE) && defined (HAVE_XFIXES)
+  GdkWindowObject *private = (GdkWindowObject *) window;
+  GdkDisplayX11 *x11_display;
+  GdkWindowImplX11 *impl;
+  GdkDisplay *display;
+  Display *dpy;
+  Window xid;
+
+  impl = GDK_WINDOW_IMPL_X11 (private->impl);
+
+  display = gdk_screen_get_display (GDK_DRAWABLE_IMPL_X11 (impl)->screen);
+  x11_display = GDK_DISPLAY_X11 (display);
+  dpy = GDK_DISPLAY_XDISPLAY (display);
+  xid = GDK_WINDOW_XWINDOW (private);
+
+  if (composited)
+  {
+    XCompositeRedirectWindow (dpy, xid, CompositeRedirectManual);
+    impl->damage = XDamageCreate (dpy, xid, XDamageReportNonEmpty);
+  }
+  else
+  {
+    XCompositeUnredirectWindow (dpy, xid, CompositeRedirectManual);
+    XDamageDestroy (dpy, impl->damage);
+    impl->damage = None;
+  }
+#endif
+}
+
 #define __GDK_WINDOW_X11_C__
 #include "gdkaliasdef.c"
Index: gdk/x11/gdkwindow-x11.h
===================================================================
--- gdk/x11/gdkwindow-x11.h	(revision 17540)
+++ gdk/x11/gdkwindow-x11.h	(working copy)
@@ -29,6 +29,10 @@
 
 #include <gdk/x11/gdkdrawable-x11.h>
 
+#ifdef HAVE_XDAMAGE
+#include <X11/extensions/Xdamage.h>
+#endif
+
 #ifdef HAVE_XSYNC
 #include <X11/extensions/sync.h>
 #endif
@@ -79,6 +83,10 @@
   gint8 toplevel_window_type;
   guint override_redirect : 1;
   guint use_synchronized_configure : 1;
+
+#if defined (HAVE_XCOMPOSITE) && defined(HAVE_XDAMAGE) && defined (HAVE_XFIXES)
+  Damage damage;
+#endif
 };
  
 struct _GdkWindowImplX11Class 
