Index: gtk/gtkwidget.c
===================================================================
diff -Nup gtk/.svn/text-base/gtkwidget.c.svn-base /tmp/svndiff.tmp
--- gtk/gtkwidget.c	(revision 19742)
+++ gtk/gtkwidget.c	(working copy)
@@ -127,6 +127,7 @@ enum {
   QUERY_TOOLTIP,
   KEYNAV_FAILED,
   DRAG_FAILED,
+  DAMAGE_EVENT,
   LAST_SIGNAL
 };
 
@@ -1994,6 +1995,28 @@ gtk_widget_class_init (GtkWidgetClass *k
 		  GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
 
   /**
+   * GtkWidget::damage-event:
+   * @widget: the object which received the signal
+   * @event: the #GdkEventExpose event
+   *
+   * Emitted when a redirected window belonging to @widget gets drawn into.
+   * The region/area members of the event shows what area of the redirected
+   * drawable was drawn into.
+   *
+   * Returns: %TRUE to stop other handlers from being invoked for the event.
+   *   %FALSE to propagate the event further.
+   *
+   * Since: 2.16
+   */
+  widget_signals[DAMAGE_EVENT] =
+    g_signal_new ("damage_event",
+		  G_TYPE_FROM_CLASS (gobject_class),
+		  G_SIGNAL_RUN_LAST, 0,
+		  _gtk_boolean_handled_accumulator, NULL,
+		  _gtk_marshal_BOOLEAN__BOXED,
+		  G_TYPE_BOOLEAN, 1,
+		  GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
+/**
    * GtkWidget::grab-broken-event:
    * @widget: the object which received the signal
    * @event: the #GdkEventGrabBroken event
@@ -4667,6 +4690,9 @@ gtk_widget_event_internal (GtkWidget *wi
 	case GDK_GRAB_BROKEN:
 	  signal_num = GRAB_BROKEN;
 	  break;
+	case GDK_DAMAGE:
+	  signal_num = DAMAGE_EVENT;
+	  break;
 	default:
 	  g_warning ("gtk_widget_event(): unhandled event type: %d", event->type);
 	  signal_num = -1;
@@ -8285,6 +8311,49 @@ gtk_widget_unref (GtkWidget *widget)
   g_object_unref ((GObject*) widget);
 }
 
+/**
+ * gtk_widget_get_snapshot:
+ * @widget: a #GtkWidget
+ *
+ * Creates a #GdkPixmap of the contents of the widget and its
+ * children. Works even if the widget is obscured.
+ *
+ * Return value: #GdkPixmap of the widget
+ * Since: 2.16
+ **/
+GdkPixmap*
+gtk_widget_get_snapshot (GtkWidget *widget)
+{
+  GdkPixmap *pixmap;
+  int x, y;
+
+  if (!GTK_WIDGET_REALIZED (widget))
+    gtk_widget_realize (widget);
+
+  pixmap = gdk_pixmap_new (widget->window,
+			   widget->allocation.width,
+			   widget->allocation.height,
+			   gdk_drawable_get_depth (widget->window));
+  if (GTK_WIDGET_NO_WINDOW (widget))
+    {
+      x = widget->allocation.x;
+      y = widget->allocation.y;
+    }
+  else
+    x = y = 0;
+
+  gdk_window_redirect_to_drawable (widget->window,
+				   pixmap,
+				   x, y,
+				   0, 0,
+				   widget->allocation.width,
+				   widget->allocation.height);
+  gtk_widget_queue_draw (widget);
+  gdk_window_process_updates (widget->window, TRUE);
+  gdk_window_remove_redirection (widget->window);
+
+  return pixmap;
+}
 
 /* style properties
  */
Index: gtk/gtkwidget.h
===================================================================
diff -Nup gtk/.svn/text-base/gtkwidget.h.svn-base /tmp/svndiff.tmp
--- gtk/gtkwidget.h	(revision 19742)
+++ gtk/gtkwidget.h	(working copy)
@@ -416,6 +416,10 @@ struct _GtkWidgetClass
 				       gint        y,
 				       gboolean    keyboard_tooltip,
 				       GtkTooltip *tooltip);
+  /* Signals without a C default handler class slot:
+   * gboolean	(*damage_event)	(GtkWidget      *widget,
+   *                             GdkEventExpose *event);
+   */
 
   /* Padding for future expansion */
   void (*_gtk_reserved5) (void);
@@ -610,6 +614,7 @@ GdkWindow *   gtk_widget_get_root_window
 GtkSettings*  gtk_widget_get_settings    (GtkWidget *widget);
 GtkClipboard *gtk_widget_get_clipboard   (GtkWidget *widget,
 					  GdkAtom    selection);
+GdkPixmap *   gtk_widget_get_snapshot    (GtkWidget *widget);
 
 #ifndef GTK_DISABLE_DEPRECATED
 #define gtk_widget_set_visual(widget,visual)  ((void) 0)
Index: gtk/gtkmain.c
===================================================================
diff -Nup gtk/.svn/text-base/gtkmain.c.svn-base /tmp/svndiff.tmp
--- gtk/gtkmain.c	(revision 19742)
+++ gtk/gtkmain.c	(working copy)
@@ -1531,6 +1531,7 @@ gtk_main_do_event (GdkEvent *event)
     case GDK_VISIBILITY_NOTIFY:
     case GDK_WINDOW_STATE:
     case GDK_GRAB_BROKEN:
+    case GDK_DAMAGE:
       gtk_widget_event (event_widget, event);
       break;
 
Index: tests/testgtk.c
===================================================================
diff -Nup tests/.svn/text-base/testgtk.c.svn-base /tmp/svndiff.tmp
--- tests/testgtk.c	(revision 19742)
+++ tests/testgtk.c	(working copy)
@@ -12206,6 +12206,155 @@ create_properties (GtkWidget *widget)
   
 }
 
+struct SnapshotData {
+  GtkWidget *toplevel_button;
+  GtkWidget **window;
+  GdkCursor *cursor;
+  gboolean in_query;
+  gboolean is_toplevel;
+  gint handler;
+};
+
+static void
+destroy_snapshot_data (GtkWidget             *widget,
+		       struct SnapshotData *data)
+{
+  if (*data->window)
+    *data->window = NULL;
+  
+  if (data->cursor)
+    {
+      gdk_cursor_unref (data->cursor);
+      data->cursor = NULL;
+    }
+
+  if (data->handler)
+    {
+      g_signal_handler_disconnect (widget, data->handler);
+      data->handler = 0;
+    }
+
+  g_free (data);
+}
+
+static gint
+snapshot_widget_event (GtkWidget	       *widget,
+		       GdkEvent	       *event,
+		       struct SnapshotData *data)
+{
+  GtkWidget *res_widget = NULL;
+
+  if (!data->in_query)
+    return FALSE;
+  
+  if (event->type == GDK_BUTTON_RELEASE)
+    {
+      gtk_grab_remove (widget);
+      gdk_display_pointer_ungrab (gtk_widget_get_display (widget),
+				  GDK_CURRENT_TIME);
+      
+      res_widget = find_widget_at_pointer (gtk_widget_get_display (widget));
+      if (data->is_toplevel && res_widget)
+	res_widget = gtk_widget_get_toplevel (res_widget);
+      if (res_widget)
+	{
+	  GdkPixmap *pixmap;
+	  GtkWidget *window, *image;
+
+	  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+	  pixmap = gtk_widget_get_snapshot (res_widget);
+	  image = gtk_image_new_from_pixmap (pixmap, NULL);
+	  gtk_container_add (GTK_CONTAINER (window), image);
+          g_object_unref (pixmap);
+	  gtk_widget_show_all (window);
+	}
+
+      data->in_query = FALSE;
+    }
+  return FALSE;
+}
+
+
+static void
+snapshot_widget (GtkButton *button,
+		 struct SnapshotData *data)
+{
+  gint failure;
+
+  g_signal_connect (button, "event",
+		    G_CALLBACK (snapshot_widget_event), data);
+
+  data->is_toplevel = GTK_WIDGET (button) == data->toplevel_button;
+  
+  if (!data->cursor)
+    data->cursor = gdk_cursor_new_for_display (gtk_widget_get_display (GTK_WIDGET (button)),
+					       GDK_TARGET);
+  
+  failure = gdk_pointer_grab (GTK_WIDGET (button)->window,
+			      TRUE,
+			      GDK_BUTTON_RELEASE_MASK,
+			      NULL,
+			      data->cursor,
+			      GDK_CURRENT_TIME);
+
+  gtk_grab_add (GTK_WIDGET (button));
+
+  data->in_query = TRUE;
+}
+
+static void
+create_snapshot (GtkWidget *widget)
+{
+  static GtkWidget *window = NULL;
+  GtkWidget *button;
+  GtkWidget *vbox;
+  struct SnapshotData *data;
+
+  data = g_new (struct SnapshotData, 1);
+  data->window = &window;
+  data->in_query = FALSE;
+  data->cursor = NULL;
+  data->handler = 0;
+
+  if (!window)
+    {
+      window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+
+      gtk_window_set_screen (GTK_WINDOW (window),
+			     gtk_widget_get_screen (widget));      
+
+      data->handler = g_signal_connect (window, "destroy",
+					G_CALLBACK (destroy_snapshot_data),
+					data);
+
+      gtk_window_set_title (GTK_WINDOW (window), "test snapshot");
+      gtk_container_set_border_width (GTK_CONTAINER (window), 10);
+
+      vbox = gtk_vbox_new (FALSE, 1);
+      gtk_container_add (GTK_CONTAINER (window), vbox);
+            
+      button = gtk_button_new_with_label ("Snapshot widget");
+      gtk_box_pack_start (GTK_BOX (vbox), button, TRUE, TRUE, 0);
+      g_signal_connect (button, "clicked",
+			G_CALLBACK (snapshot_widget),
+			data);
+      
+      button = gtk_button_new_with_label ("Snapshot toplevel");
+      data->toplevel_button = button;
+      gtk_box_pack_start (GTK_BOX (vbox), button, TRUE, TRUE, 0);
+      g_signal_connect (button, "clicked",
+			G_CALLBACK (snapshot_widget),
+			data);
+    }
+
+  if (!GTK_WIDGET_VISIBLE (window))
+    gtk_widget_show_all (window);
+  else
+    gtk_widget_destroy (window);
+  
+}
+
+
 
 /*
  * Color Preview
@@ -13489,6 +13638,7 @@ struct {
   { "scrolled windows", create_scrolled_windows },
   { "shapes", create_shapes },
   { "size groups", create_size_groups },
+  { "snapshot", create_snapshot },
   { "spinbutton", create_spins },
   { "statusbar", create_statusbar },
   { "styles", create_styles },
@@ -13524,7 +13674,7 @@ create_main_window (void)
 
   window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
   gtk_widget_set_name (window, "main window");
-  gtk_widget_set_uposition (window, 20, 20);
+  gtk_widget_set_uposition (window, 50, 20);
   gtk_window_set_default_size (GTK_WINDOW (window), -1, 400);
 
   geometry.min_width = -1;
Index: ChangeLog
===================================================================
diff -Nup .svn/text-base/ChangeLog.svn-base ChangeLog
--- ChangeLog	(revision 19742)
+++ ChangeLog	(working copy)
@@ -1,3 +1,65 @@
+2008-03-04 14:46:13  Tim Janik  <timj@imendio.com>
+
+	* Applied pixmap redirection patch by Alexander Larsson with
+	various updates from:
+	Bug 318807 – Offscreen windows and window redirection.
+
+
+	Updates:
+
+	* updated docs to mention "Since 2.16".
+
+	* tests/testgtk.c: fixed snapshooting pixmap leak.
+
+	* gdk/gdkwindow.[hc]: made GdkWindowRedirect private.
+
+	* gdk/gdkwindow.c: removed damage idle handler, there's no aparent
+	need for it. enqueue damage notification as GDK_DAMAGE events
+	for each painting redirection at the start of the event queue.
+	consider windows with a redirection fully visible when invalidating,
+	and when updating from backing store.
+	cleaned up stale variables.
+
+	* gdk/gdkevents.c: added _gdk_event_queue_prepend().
+
+	* gtk/gtkwidget.c: fixed coordinates for !NO_WINDOW widgets in
+	gtk_widget_get_snapshot; this fixes garbage snap offsets for gammacurve,
+	tree, drawingarea, text, handlebox, etc.
+	clip the redirected window hierarchy to window sizes, the visible
+	rectangles don't need to be taken into account here.
+
+	* gdk/x11/gdkwindow-x11.c: removed _gdk_windowing_window_get_visible_rect().
+
+
+	Base patch:
+
+	* tests/testgtk.c: add a "Snapshot" test to demonstrate snapshooting
+	of possibly obscured widgets into an offscreen pixmap.
+
+	* gtk/gtkwidget.[hc]: add GtkWidget::damage-event signal, add
+	gtk_widget_get_snapshot() to render a widget's contents to a GdkPixmap.
+
+	* gtk/gtkmain.c: dispatch GDK_DAMAGE events.
+
+	* gdk/gdkwindow.c: moved outer gdk_window_new() and gdk_window_reparent()
+	implementations here, adapted them to propagate redirects to child windows.
+	gdk_window_end_paint(): copy repainted window contents to redirection pixmap,
+	clipped to visible region. queue GDK_DAMAGE event delivery.
+	gdk_window_redirect_to_drawable(): install window painting redirection.
+	gdk_window_remove_redirection(): remove previously installed redirection.
+
+	* gdk/x11/gdkwindow-x11.c: added _gdk_windowing_window_get_visible_rect(),
+	renamed _gdk_window_new() and _gdk_window_reparent().
+
+	* gdk/gdkwindow.h: added GdkWindowRedirect* to GdkWindowObject, export
+	gdk_window_redirect_to_drawable() and gdk_window_remove_redirection().
+
+	* gdk/gdkevents.h: added GDK_DAMAGE event type.
+
+	* gdk/gdkevents.c: extract time and state from GDK_DAMAGE events.
+
+	* gdk/gdkinternals.h: added internal prototypes.
+
 2008-03-10  Emmanuele Bassi  <ebassi@gnome.org>
 
 	* README.in: Reword the 2.14 release notes entry about the
Index: gdk/gdkevents.c
===================================================================
diff -Nup gdk/.svn/text-base/gdkevents.c.svn-base /tmp/svndiff.tmp
--- gdk/gdkevents.c	(revision 19742)
+++ gdk/gdkevents.c	(working copy)
@@ -79,6 +79,25 @@ _gdk_event_queue_find_first (GdkDisplay 
 }
 
 /**
+ * _gdk_event_queue_prepend:
+ * @display: a #GdkDisplay
+ * @event: Event to prepend.
+ *
+ * Prepends an event before the head of the event queue.
+ *
+ * Returns: the newly prepended list node.
+ **/
+GList*
+_gdk_event_queue_prepend (GdkDisplay *display,
+			  GdkEvent   *event)
+{
+  display->queued_events = g_list_prepend (display->queued_events, event);
+  if (!display->queued_tail)
+    display->queued_tail = display->queued_events;
+  return display->queued_events;
+}
+
+/**
  * _gdk_event_queue_append:
  * @display: a #GdkDisplay
  * @event: Event to append.
@@ -541,6 +560,7 @@ gdk_event_get_time (const GdkEvent *even
       case GDK_CONFIGURE:
       case GDK_FOCUS_CHANGE:
       case GDK_NOTHING:
+      case GDK_DAMAGE:
       case GDK_DELETE:
       case GDK_DESTROY:
       case GDK_EXPOSE:
@@ -611,6 +631,7 @@ gdk_event_get_state (const GdkEvent     
       case GDK_SELECTION_NOTIFY:
       case GDK_PROXIMITY_IN:
       case GDK_PROXIMITY_OUT:
+      case GDK_DAMAGE:
       case GDK_DRAG_ENTER:
       case GDK_DRAG_LEAVE:
       case GDK_DRAG_MOTION:
Index: gdk/gdkevents.h
===================================================================
diff -Nup gdk/.svn/text-base/gdkevents.h.svn-base /tmp/svndiff.tmp
--- gdk/gdkevents.h	(revision 19742)
+++ gdk/gdkevents.h	(working copy)
@@ -146,7 +146,8 @@ typedef enum
   GDK_WINDOW_STATE      = 32,
   GDK_SETTING           = 33,
   GDK_OWNER_CHANGE      = 34,
-  GDK_GRAB_BROKEN       = 35
+  GDK_GRAB_BROKEN       = 35,
+  GDK_DAMAGE            = 36
 } GdkEventType;
 
 /* Event masks. (Used to select what types of events a window
Index: gdk/gdkinternals.h
===================================================================
diff -Nup gdk/.svn/text-base/gdkinternals.h.svn-base /tmp/svndiff.tmp
--- gdk/gdkinternals.h	(revision 19742)
+++ gdk/gdkinternals.h	(working copy)
@@ -183,6 +183,8 @@ GdkEvent* _gdk_event_unqueue (GdkDisplay
 GList* _gdk_event_queue_find_first  (GdkDisplay *display);
 void   _gdk_event_queue_remove_link (GdkDisplay *display,
 				     GList      *node);
+GList*  _gdk_event_queue_prepend    (GdkDisplay *display,
+				     GdkEvent   *event);
 GList*  _gdk_event_queue_append     (GdkDisplay *display,
 				     GdkEvent   *event);
 void _gdk_event_button_generate     (GdkDisplay *display,
@@ -305,6 +307,14 @@ GdkWindow* _gdk_windowing_window_at_poin
 gint _gdk_windowing_get_bits_for_depth (GdkDisplay *display,
 					gint        depth);
 
+GdkWindow* _gdk_window_new                        (GdkWindow     *parent,
+						   GdkWindowAttr *attributes,
+						   gint           attributes_mask);
+void       _gdk_window_reparent                   (GdkWindow     *window,
+						   GdkWindow     *new_parent,
+						   gint           x,
+						   gint           y);
+
 #define GDK_WINDOW_IS_MAPPED(window) ((((GdkWindowObject*)window)->state & GDK_WINDOW_STATE_WITHDRAWN) == 0)
 
 /* Called before processing updates for a window. This gives the windowing
Index: gdk/gdk.symbols
===================================================================
diff -Nup gdk/.svn/text-base/gdk.symbols.svn-base gdk/gdk.symbols
--- gdk/gdk.symbols	(revision 19742)
+++ gdk/gdk.symbols	(working copy)
@@ -646,6 +646,8 @@ gdk_window_impl_x11_get_type G_GNUC_CONS
 #if IN_HEADER(__GDK_WINDOW_H__)
 #if IN_FILE(__GDK_WINDOW_C__)
 gdk_get_default_root_window
+gdk_window_new
+gdk_window_reparent
 gdk_window_add_filter
 gdk_window_at_pointer
 gdk_window_begin_paint_rect
@@ -703,7 +705,6 @@ gdk_window_move_region
 
 #if IN_HEADER(__GDK_WINDOW_H__)
 #if IN_FILE(__GDK_WINDOW_X11_C__)
-gdk_window_new
 gdk_window_foreign_new_for_display
 gdk_window_lookup
 gdk_window_lookup_for_display
@@ -714,7 +715,6 @@ gdk_window_withdraw
 gdk_window_move
 gdk_window_resize
 gdk_window_move_resize
-gdk_window_reparent
 gdk_window_raise
 gdk_window_lower
 gdk_window_focus
Index: gdk/gdkwindow.c
===================================================================
diff -Nup gdk/.svn/text-base/gdkwindow.c.svn-base /tmp/svndiff.tmp
--- gdk/gdkwindow.c	(revision 19742)
+++ gdk/gdkwindow.c	(working copy)
@@ -47,6 +47,26 @@ struct _GdkWindowPaint
   cairo_surface_t *surface;
 };
 
+typedef struct {
+  GdkRegion *old_region;
+  gint old_clip_x_origin;
+  gint old_clip_y_origin;
+  gint x_offset;
+  gint y_offset;
+} GdkWindowClipData;
+
+struct _GdkWindowRedirect
+{
+  GdkWindowObject *redirected;
+  GdkDrawable *pixmap;
+  gint src_x;
+  gint src_y;
+  gint dest_x;
+  gint dest_y;
+  gint width;
+  gint height;
+};
+
 static GdkGC *gdk_window_create_gc      (GdkDrawable     *drawable,
                                          GdkGCValues     *values,
                                          GdkGCValuesMask  mask);
@@ -191,6 +211,23 @@ static void gdk_window_clear_backing_rec
 					   gint       y,
 					   gint       width,
 					   gint       height);
+static void setup_redirect_clip           (GdkWindow         *window,
+					   GdkGC             *gc,
+					   GdkWindowClipData *data);
+static void reset_redirect_clip           (GdkWindow         *offscreen,
+					   GdkGC             *gc,
+					   GdkWindowClipData *data);
+static void gdk_window_redirect_free      (GdkWindowRedirect *redirect);
+static void apply_redirect_to_children    (GdkWindowObject   *private,
+					   GdkWindowRedirect *redirect);
+static void remove_redirect_from_children (GdkWindowObject   *private,
+					   GdkWindowRedirect *redirect);
+static GdkRegion *_gdk_window_calculate_full_clip_region (GdkWindow *window,
+							  GdkWindow *base_window,
+							  GdkGC *gc,
+							  gboolean do_children,
+							  gint *base_x_offset,
+							  gint *base_y_offset);
 
 static gpointer parent_class = NULL;
 
@@ -311,6 +348,91 @@ gdk_window_finalize (GObject *object)
   G_OBJECT_CLASS (parent_class)->finalize (object);
 }
 
+/**
+ * gdk_window_new:
+ * @parent: a #GdkWindow, or %NULL to create the window as a child of
+ *   the default root window for the default display.
+ * @attributes: attributes of the new window
+ * @attributes_mask: mask indicating which fields in @attributes are valid
+ * 
+ * Creates a new #GdkWindow using the attributes from
+ * @attributes. See #GdkWindowAttr and #GdkWindowAttributesType for
+ * more details.  Note: to use this on displays other than the default
+ * display, @parent must be specified.
+ * 
+ * Return value: the new #GdkWindow
+ **/
+GdkWindow*
+gdk_window_new (GdkWindow     *parent,
+		GdkWindowAttr *attributes,
+		gint           attributes_mask)
+{
+  GdkWindow *window;
+  GdkWindowObject *private, *parent_private;
+  
+  g_return_val_if_fail (attributes != NULL, NULL);
+
+  window = _gdk_window_new (parent, attributes, attributes_mask);
+
+  /* Inherit redirection from parent */
+  if (parent != NULL)
+    {
+      parent_private = GDK_WINDOW_OBJECT (parent);
+      private = GDK_WINDOW_OBJECT (window);
+      private->redirect = parent_private->redirect;
+    }
+  
+  return window;
+}
+
+/**
+ * gdk_window_reparent:
+ * @window: a #GdkWindow
+ * @new_parent: new parent to move @window into
+ * @x: X location inside the new parent
+ * @y: Y location inside the new parent
+ *
+ * Reparents @window into the given @new_parent. The window being
+ * reparented will be unmapped as a side effect.
+ * 
+ **/
+void
+gdk_window_reparent (GdkWindow *window,
+		     GdkWindow *new_parent,
+		     gint       x,
+		     gint       y)
+{
+  GdkWindowObject *private;
+  
+  g_return_if_fail (GDK_IS_WINDOW (window));
+  g_return_if_fail (new_parent == NULL || GDK_IS_WINDOW (new_parent));
+  g_return_if_fail (GDK_WINDOW_TYPE (window) != GDK_WINDOW_ROOT);
+
+  if (GDK_WINDOW_DESTROYED (window) ||
+      (new_parent && GDK_WINDOW_DESTROYED (new_parent)))
+    {
+      return;
+    }
+
+  private = (GdkWindowObject *) window;
+
+  /* Break up redirection if inherited */
+  if (private->redirect && private->redirect->redirected != private)
+    {
+      remove_redirect_from_children (private, private->redirect);
+      private->redirect = NULL;
+    }
+  
+  _gdk_window_reparent (window, new_parent, x, y);
+
+  /* Inherit parent redirect if we don't have our own */
+  if (private->parent && private->redirect == NULL)
+    {
+      private->redirect = private->parent->redirect;
+      apply_redirect_to_children (private, private->redirect);
+    }
+}
+
 static void
 window_remove_filters (GdkWindow *window)
 {
@@ -446,6 +568,11 @@ _gdk_window_destroy_hierarchy (GdkWindow
 	  window_remove_filters (window);
 
           gdk_drawable_set_colormap (GDK_DRAWABLE (window), NULL);
+
+	  /* If we own the redirect, free it */
+	  if (private->redirect && private->redirect->redirected == private)
+	    gdk_window_redirect_free (private->redirect);
+	  private->redirect = NULL;
 	}
       break;
     }
@@ -1089,6 +1216,20 @@ gdk_window_end_paint (GdkWindow *window)
                      clip_box.x - x_offset, clip_box.y - y_offset,
                      clip_box.width, clip_box.height);
 
+  if (private->redirect)
+    {
+      GdkWindowClipData data;
+      
+      setup_redirect_clip (window, tmp_gc, &data);
+      gdk_draw_drawable (private->redirect->pixmap, tmp_gc, paint->pixmap,
+			 clip_box.x - paint->x_offset,
+			 clip_box.y - paint->y_offset,
+			 clip_box.x + data.x_offset,
+			 clip_box.y + data.y_offset,
+			 clip_box.width, clip_box.height);
+      reset_redirect_clip (window, tmp_gc, &data);
+    }
+  
   /* Reset clip region of the cached GdkGC */
   gdk_gc_set_clip_region (tmp_gc, NULL);
 
@@ -1946,6 +2087,65 @@ gdk_window_clear_backing_rect (GdkWindow
 #endif
 }
 
+static void
+gdk_window_clear_backing_rect_redirect (GdkWindow *window,
+					gint       x,
+					gint       y,
+					gint       width,
+					gint       height)
+{
+  GdkWindowObject *private = (GdkWindowObject *)window;
+  GdkWindowRedirect *redirect = private->redirect;
+  GdkRegion *clip_region;
+  gint x_offset, y_offset;
+  BackingRectMethod method;
+  GdkWindowPaint paint;
+  
+  if (GDK_WINDOW_DESTROYED (window))
+    return;
+
+  paint.x_offset = x_offset;
+  paint.y_offset = y_offset;
+  paint.pixmap = redirect->pixmap;
+  paint.surface = _gdk_drawable_ref_cairo_surface (redirect->pixmap);
+  
+  clip_region = _gdk_window_calculate_full_clip_region (window,
+							GDK_WINDOW (redirect->redirected),
+							NULL, TRUE,
+							&x_offset, &y_offset);
+  
+
+  method.cr = NULL;
+  method.gc = NULL;
+  setup_backing_rect_method (&method, window, &paint, 0, 0);
+
+  if (method.cr)
+    {
+      g_assert (method.gc == NULL);
+
+      cairo_rectangle (method.cr, x, y, width, height);
+      cairo_clip (method.cr);
+
+      gdk_cairo_region (method.cr, clip_region);
+      cairo_fill (method.cr);
+
+      cairo_destroy (method.cr);
+    }
+  else
+    {
+      g_assert (method.gc != NULL);
+
+      gdk_gc_set_clip_region (method.gc, clip_region);
+      gdk_draw_rectangle (window, method.gc, TRUE, x, y, width, height);
+      g_object_unref (method.gc);
+
+    }
+
+  gdk_region_destroy (clip_region);
+  cairo_surface_destroy (paint.surface);
+}
+
+
 /**
  * gdk_window_clear:
  * @window: a #GdkWindow
@@ -1992,7 +2192,12 @@ gdk_window_clear_area (GdkWindow *window
   if (private->paint_stack)
     gdk_window_clear_backing_rect (window, x, y, width, height);
   else
-    _gdk_windowing_window_clear_area (window, x, y, width, height);
+    {
+      if (private->redirect)
+	gdk_window_clear_backing_rect_redirect (window, x, y, width, height);
+	
+      _gdk_windowing_window_clear_area (window, x, y, width, height);
+    }
 }
 
 /**
@@ -2025,6 +2230,9 @@ gdk_window_clear_area_e (GdkWindow *wind
   if (private->paint_stack)
     gdk_window_clear_backing_rect (window, x, y, width, height);
 
+  if (private->redirect)
+    gdk_window_clear_backing_rect_redirect (window, x, y, width, height);
+  
   _gdk_windowing_window_clear_area_e (window, x, y, width, height);
 }
 
@@ -2629,7 +2837,18 @@ gdk_window_invalidate_maybe_recurse (Gdk
       return;
     }
 
-  visible_region = gdk_drawable_get_visible_region (window);
+  /* windows that a redirection has ben setup for need to be considered
+   * fully visible, in order to avoid missing redirected paint ops
+   * anywhere in the window area.
+   */
+  if (private->redirect && private->redirect->redirected == private)
+    {
+      GdkRectangle visible_rect = { 0, 0, 0, 0 };
+      gdk_drawable_get_size (GDK_DRAWABLE (window), &visible_rect.width, &visible_rect.height);
+      visible_region = gdk_region_rectangle (&visible_rect);
+    }
+  else
+    visible_region = gdk_drawable_get_visible_region (window);
   gdk_region_intersect (visible_region, region);
 
   tmp_list = private->children;
@@ -2678,7 +2897,7 @@ gdk_window_invalidate_maybe_recurse (Gdk
     {
       if (debug_updates)
         draw_ugly_color (window, region);
-      
+
       if (private->update_area)
 	{
 	  gdk_region_union (private->update_area, visible_region);
@@ -3223,5 +3442,348 @@ gdk_window_set_composited (GdkWindow *wi
   private->composited = composited;
 }
 
+
+static void
+remove_redirect_from_children (GdkWindowObject *private, GdkWindowRedirect *redirect)
+{
+  GList *l;
+  GdkWindowObject *child;
+
+  for (l = private->children; l != NULL; l = l->next)
+    {
+      child = l->data;
+
+      /* Don't redirect this child if it already has another redirect */
+      if (child->redirect == redirect)
+	{
+	  child->redirect = NULL;
+	  remove_redirect_from_children (child, redirect);
+	}
+    }
+}
+
+/**
+ * gdk_window_remove_redirection:
+ * @window: a #GdkWindow
+ *
+ * Removes and active redirection started by
+ * gdk_window_redirect_to_drawable().
+ **/
+void
+gdk_window_remove_redirection (GdkWindow *window)
+{
+  GdkWindowObject *private;
+  
+  g_return_if_fail (GDK_IS_WINDOW (window));
+
+  private = (GdkWindowObject *) window;
+
+  if (private->redirect &&
+      private->redirect->redirected == private)
+    {
+      remove_redirect_from_children (private, private->redirect);
+      gdk_window_redirect_free (private->redirect);
+      private->redirect = NULL;
+    }
+}
+
+static void
+apply_redirect_to_children (GdkWindowObject *private, GdkWindowRedirect *redirect)
+{
+  GList *l;
+  GdkWindowObject *child;
+
+  for (l = private->children; l != NULL; l = l->next)
+    {
+      child = l->data;
+
+      /* Don't redirect this child if it already has another redirect */
+      if (!child->redirect)
+	{
+	  child->redirect = redirect;
+	  apply_redirect_to_children (child, redirect);
+	}
+    }
+}
+
+/**
+ * gdk_window_redirect_to_drawable:
+ * @window: a #GdkWindow
+ * @drawable: a #GdkDrawable
+ * src_x: x position in @window
+ * src_y: y position in @window
+ * dest_x: x position in @drawable
+ * dest_y: y position in @drawable
+ * width: width of redirection
+ * height: height of redirection
+ *
+ * Redirects drawing into @windows so that drawing to the
+ * window in the rectangle specified by @src_x, @src_y,
+ * @width and @height is also drawn into @drawable at
+ * @dest_x, @dest_y.
+ *
+ * Only drawing between gdk_window_begin_paint_region() and
+ * gdk_window_end_paint() is redirected.
+ *
+ * Redirection is active until gdk_window_remove_redirection()
+ * is called.
+ *
+ * This function should not be used on windows created by
+ * gdk_window_new_offscreen(), as that is implemented using
+ * redirection.
+ **/
+void
+gdk_window_redirect_to_drawable (GdkWindow *window,
+				 GdkDrawable *drawable,
+				 gint src_x, gint src_y,
+				 gint dest_x, gint dest_y,
+				 gint width, gint height)
+{
+  GdkWindowObject *private;
+  
+  g_return_if_fail (GDK_IS_WINDOW (window));
+  g_return_if_fail (GDK_IS_DRAWABLE (drawable));
+  g_return_if_fail (GDK_WINDOW_TYPE (window) != GDK_WINDOW_ROOT);
+
+  private = (GdkWindowObject *) window;
+
+  if (private->redirect)
+    gdk_window_remove_redirection (window);
+
+  if (width == -1 || height == -1)
+    {
+      gint w, h;
+      gdk_drawable_get_size (GDK_DRAWABLE (window), &w, &h);
+      if (width == -1)
+	width = w;
+      if (height == -1)
+	height = h;
+    }
+  
+  private->redirect = g_new0 (GdkWindowRedirect, 1);
+  private->redirect->redirected = private;
+  private->redirect->pixmap = g_object_ref (drawable);
+  private->redirect->src_x = src_x;
+  private->redirect->src_y = src_y;
+  private->redirect->dest_x = dest_x;
+  private->redirect->dest_y = dest_y;
+  private->redirect->width = width;
+  private->redirect->height = height;
+
+  apply_redirect_to_children (private, private->redirect);
+}
+
+static void
+window_get_size_rectangle (GdkWindow    *window,
+                           GdkRectangle *rect)
+{
+  rect->x = rect->y = 0;
+  gdk_drawable_get_size (GDK_DRAWABLE (window), &rect->width, &rect->height);
+}
+
+/* Calculates the real clipping region for a window, in window coordinates,
+ * taking into account other windows, gc clip region and gc clip mask.
+ */
+static GdkRegion *
+_gdk_window_calculate_full_clip_region (GdkWindow *window,
+					GdkWindow *base_window,
+					GdkGC *gc,
+					gboolean do_children,
+					gint *base_x_offset,
+					gint *base_y_offset)
+{
+  GdkWindowObject *private = GDK_WINDOW_OBJECT (window);
+  GdkRectangle visible_rect;
+  GdkRegion *real_clip_region, *tmpreg;
+  gint x_offset, y_offset;
+  GdkWindowObject *parentwin, *lastwin;
+
+  if (base_x_offset)
+    *base_x_offset = 0;
+  if (base_y_offset)
+    *base_y_offset = 0;
+  
+  if (!GDK_WINDOW_IS_MAPPED (window) || private->input_only)
+    return gdk_region_new ();
+
+  window_get_size_rectangle (window, &visible_rect);
+
+  /* windows that a redirection has ben setup for need to be considered
+   * fully visible, in order to avoid missing redirected paint ops
+   * anywhere in the window area.
+   */
+  if (private->redirect && private->redirect->redirected == private)
+    return gdk_region_rectangle (&visible_rect);
+
+  /* real_clip_region is in window coordinates */
+  real_clip_region = gdk_region_rectangle (&visible_rect);
+
+  x_offset = y_offset = 0;
+
+  lastwin = private;
+  if (do_children)
+    parentwin = lastwin;
+  else
+    parentwin = lastwin->parent;
+  
+  /* Remove the areas of all overlapping windows above parentwin in the hiearachy */
+  for (; parentwin != NULL && (parentwin == private || lastwin != (GdkWindowObject *)base_window);
+       lastwin = parentwin, parentwin = lastwin->parent)
+    {
+      GList *cur;
+      GdkRectangle real_clip_rect;
+      
+      if (parentwin != private)
+	{
+	  x_offset += GDK_WINDOW_OBJECT (lastwin)->x;
+	  y_offset += GDK_WINDOW_OBJECT (lastwin)->y;
+	}
+      
+      /* children is ordered in reverse stack order */
+      for (cur = GDK_WINDOW_OBJECT (parentwin)->children; cur && cur->data != lastwin; cur = cur->next)
+	{
+	  GdkWindow *child = cur->data;
+	  GdkWindowObject *child_private = (GdkWindowObject *)child;
+	  
+	  if (!GDK_WINDOW_IS_MAPPED (child) || child_private->input_only)
+	    continue;
+	  
+	  window_get_size_rectangle (child, &visible_rect);
+	  
+	  /* Convert rect to "window" coords */
+	  visible_rect.x += child_private->x - x_offset;
+	  visible_rect.y += child_private->y - y_offset;
+	  
+	  /* This shortcut is really necessary for performance when there are a lot of windows */
+	  gdk_region_get_clipbox (real_clip_region, &real_clip_rect);
+	  if (visible_rect.x >= real_clip_rect.x + real_clip_rect.width ||
+	      visible_rect.x + visible_rect.width <= real_clip_rect.x ||
+	      visible_rect.y >= real_clip_rect.y + real_clip_rect.height ||
+	      visible_rect.y + visible_rect.height <= real_clip_rect.y)
+	    continue;
+	  
+	  tmpreg = gdk_region_rectangle (&visible_rect);
+	  gdk_region_subtract (real_clip_region, tmpreg);
+	  gdk_region_destroy (tmpreg);
+	}
+      
+    }
+
+  if (gc)
+    {
+      GdkRegion *clip_region = _gdk_gc_get_clip_region (gc);
+      
+      if (clip_region)
+	{
+	  /* clip_region is relative to gc clip origin which is relative to the window */
+	  /* offset it to window relative: */
+	  tmpreg = gdk_region_copy (clip_region);
+	  gdk_region_offset (real_clip_region,
+			     gc->clip_x_origin,
+			     gc->clip_y_origin);
+	  /* Intersect it with window hierarchy cliprect: */
+	  gdk_region_intersect (real_clip_region, tmpreg);
+	  gdk_region_destroy (tmpreg);
+	}
+    }
+
+  if (base_x_offset)
+    *base_x_offset = x_offset;
+  if (base_y_offset)
+    *base_y_offset = y_offset;
+
+  return real_clip_region;
+}
+
+static void
+gdk_window_add_damage (GdkWindow *toplevel,
+		       GdkRegion *damaged_region)
+{
+  GdkDisplay *display;
+  GdkEvent event = { 0, };
+  event.expose.type = GDK_DAMAGE;
+  event.expose.window = toplevel;
+  event.expose.send_event = FALSE;
+  event.expose.region = damaged_region;
+  gdk_region_get_clipbox (event.expose.region, &event.expose.area);
+  display = gdk_drawable_get_display (event.expose.window);
+  _gdk_event_queue_append (display, gdk_event_copy (&event));
+}
+
+static void
+setup_redirect_clip (GdkWindow         *window,
+		     GdkGC             *gc,
+		     GdkWindowClipData *data)
+{
+  GdkWindowObject *private = (GdkWindowObject *)window;
+  GdkRegion *visible_region;
+  GdkRectangle dest_rect;
+  GdkRegion *tmpreg;
+  GdkWindow *toplevel;
+
+  data->old_region = _gdk_gc_get_clip_region (gc);
+  if (data->old_region) 
+    data->old_region = gdk_region_copy (data->old_region);
+
+  data->old_clip_x_origin = gc->clip_x_origin;
+  data->old_clip_y_origin = gc->clip_y_origin;
+
+  toplevel = GDK_WINDOW (private->redirect->redirected);
+  
+  /* Get the clip region for gc clip rect + window hierarchy in
+     window relative coords */
+  visible_region =
+    _gdk_window_calculate_full_clip_region (window, toplevel,
+					    gc, TRUE,
+					    &data->x_offset, 
+					    &data->y_offset);
+
+  /* Compensate for the source pos/size */
+  data->x_offset -= private->redirect->src_x;
+  data->y_offset -= private->redirect->src_y;
+  dest_rect.x = -data->x_offset;
+  dest_rect.y = -data->y_offset;
+  dest_rect.width = private->redirect->width;
+  dest_rect.height = private->redirect->height;
+  tmpreg = gdk_region_rectangle (&dest_rect);
+  gdk_region_intersect (visible_region, tmpreg);
+  gdk_region_destroy (tmpreg);
+
+  /* Compensate for the dest pos */
+  data->x_offset += private->redirect->dest_x;
+  data->y_offset += private->redirect->dest_y;
+
+  gdk_gc_set_clip_region (gc, visible_region); /* This resets clip origin! */
+
+  /* offset clip and tiles from window coords to pixmaps coords */
+  gdk_gc_offset (gc, -data->x_offset, -data->y_offset);
+
+  /* Offset region to abs coords and add to damage */
+  gdk_region_offset (visible_region, data->x_offset, data->y_offset);
+  gdk_window_add_damage (toplevel, visible_region);
+  
+  gdk_region_destroy (visible_region);
+}
+
+static void
+reset_redirect_clip (GdkWindow *offscreen, GdkGC *gc, GdkWindowClipData *data)
+{
+  /* offset back */
+  gdk_gc_offset (gc, data->x_offset, data->y_offset);
+
+  /* reset old clip */
+  gdk_gc_set_clip_region (gc, data->old_region);
+  if (data->old_region)
+    gdk_region_destroy (data->old_region);
+  gdk_gc_set_clip_origin (gc, data->old_clip_x_origin, data->old_clip_y_origin);
+}
+
+static void
+gdk_window_redirect_free (GdkWindowRedirect *redirect)
+{
+  g_object_unref (redirect->pixmap);
+  g_free (redirect);
+}
+
 #define __GDK_WINDOW_C__
 #include "gdkaliasdef.c"
Index: gdk/gdkwindow.h
===================================================================
diff -Nup gdk/.svn/text-base/gdkwindow.h.svn-base /tmp/svndiff.tmp
--- gdk/gdkwindow.h	(revision 19742)
+++ gdk/gdkwindow.h	(working copy)
@@ -36,6 +36,7 @@ G_BEGIN_DECLS
 typedef struct _GdkGeometry           GdkGeometry;
 typedef struct _GdkWindowAttr	      GdkWindowAttr;
 typedef struct _GdkPointerHooks	      GdkPointerHooks;
+typedef struct _GdkWindowRedirect     GdkWindowRedirect;
 
 /* Classes of windows.
  *   InputOutput: Almost every window should be of this type. Such windows
@@ -300,6 +301,8 @@ struct _GdkWindowObject
   GdkEventMask event_mask;
 
   guint update_and_descendants_freeze_count;
+
+  GdkWindowRedirect *redirect;
 };
 
 struct _GdkWindowObjectClass
@@ -638,6 +641,13 @@ GdkPointerHooks *gdk_set_pointer_hooks (
 
 GdkWindow *gdk_get_default_root_window (void);
 
+void gdk_window_redirect_to_drawable (GdkWindow *window,
+				      GdkDrawable *drawable,
+				      gint src_x, gint src_y,
+				      gint dest_x, gint dest_y,
+				      gint width, gint height);
+void gdk_window_remove_redirection   (GdkWindow *window);
+
 #ifndef GDK_DISABLE_DEPRECATED
 #define GDK_ROOT_PARENT()             (gdk_get_default_root_window ())
 #define gdk_window_get_size            gdk_drawable_get_size
Index: gdk/x11/gdkwindow-x11.c
===================================================================
diff -Nup gdk/x11/.svn/text-base/gdkwindow-x11.c.svn-base /tmp/svndiff.tmp
--- gdk/x11/gdkwindow-x11.c	(revision 19742)
+++ gdk/x11/gdkwindow-x11.c	(working copy)
@@ -647,24 +647,10 @@ setup_toplevel_window (GdkWindow *window
   ensure_sync_counter (window);
 }
 
-/**
- * gdk_window_new:
- * @parent: a #GdkWindow, or %NULL to create the window as a child of
- *   the default root window for the default display.
- * @attributes: attributes of the new window
- * @attributes_mask: mask indicating which fields in @attributes are valid
- * 
- * Creates a new #GdkWindow using the attributes from
- * @attributes. See #GdkWindowAttr and #GdkWindowAttributesType for
- * more details.  Note: to use this on displays other than the default
- * display, @parent must be specified.
- * 
- * Return value: the new #GdkWindow
- **/
 GdkWindow*
-gdk_window_new (GdkWindow     *parent,
-		GdkWindowAttr *attributes,
-		gint           attributes_mask)
+_gdk_window_new (GdkWindow     *parent,
+		 GdkWindowAttr *attributes,
+		 gint           attributes_mask)
 {
   GdkWindow *window;
   GdkWindowObject *private;
@@ -1843,22 +1829,11 @@ gdk_window_move_resize (GdkWindow *windo
     }
 }
 
-/**
- * gdk_window_reparent:
- * @window: a #GdkWindow
- * @new_parent: new parent to move @window into
- * @x: X location inside the new parent
- * @y: Y location inside the new parent
- *
- * Reparents @window into the given @new_parent. The window being
- * reparented will be unmapped as a side effect.
- * 
- **/
 void
-gdk_window_reparent (GdkWindow *window,
-		     GdkWindow *new_parent,
-		     gint       x,
-		     gint       y)
+_gdk_window_reparent (GdkWindow *window,
+		      GdkWindow *new_parent,
+		      gint       x,
+		      gint       y)
 {
   GdkWindowObject *window_private;
   GdkWindowObject *parent_private;
@@ -1866,16 +1841,6 @@ gdk_window_reparent (GdkWindow *window,
   GdkWindowImplX11 *impl;
   gboolean was_toplevel;
   
-  g_return_if_fail (GDK_IS_WINDOW (window));
-  g_return_if_fail (new_parent == NULL || GDK_IS_WINDOW (new_parent));
-  g_return_if_fail (GDK_WINDOW_TYPE (window) != GDK_WINDOW_ROOT);
-
-  if (GDK_WINDOW_DESTROYED (window) ||
-      (new_parent && GDK_WINDOW_DESTROYED (new_parent)))
-    {
-      return;
-    }
-  
   if (!new_parent)
     new_parent = gdk_screen_get_root_window (GDK_WINDOW_SCREEN (window));
 
