diff --git a/gdk/Makefile.am b/gdk/Makefile.am
index 500c950..75a9f11 100644
--- a/gdk/Makefile.am
+++ b/gdk/Makefile.am
@@ -121,6 +121,7 @@ gdk_c_sources =                 \
 	gdkintl.h		\
 	gdkkeys.c		\
 	gdkkeyuni.c		\
+	gdkoffscreenwindow.c	\
 	gdkpango.c		\
 	gdkpixbuf-drawable.c	\
 	gdkpixbuf-render.c	\
diff --git a/gdk/gdk.symbols b/gdk/gdk.symbols
index 30c77b7..ee36169 100644
--- a/gdk/gdk.symbols
+++ b/gdk/gdk.symbols
@@ -254,12 +254,6 @@ gdk_visual_type_get_type G_GNUC_CONST
 #endif
 #endif
 
-#if IN_HEADER(__GDK_PIXMAP_H__)
-#if IN_FILE(__GDK_PIXMAP_X11_C__)
-gdk_bitmap_create_from_data
-#endif
-#endif
-
 #if IN_HEADER(__GDK_FONT_H__)
 #if IN_FILE(__GDK_FONT_C__)
 #ifndef GDK_DISABLE_DEPRECATED
@@ -718,6 +712,16 @@ gdk_window_set_composited
 #endif
 
 #if IN_HEADER(__GDK_WINDOW_H__)
+#if IN_FILE(__GDK_OFFSCREEN_WINDOW_C__)
+gdk_window_set_offscreen_hooks
+gdk_offscreen_window_get_type
+gdk_offscreen_window_new
+gdk_window_get_offscreen_pixmap
+gdk_window_is_offscreen
+#endif
+#endif
+
+#if IN_HEADER(__GDK_WINDOW_H__)
 #if IN_FILE(__GDK_DND_X11_C__)
 gdk_window_register_dnd
 #endif
@@ -921,23 +925,24 @@ gdk_pixbuf_render_to_drawable_alpha
 
 #if IN_HEADER(__GDK_PIXMAP_H__)
 #if IN_FILE(__GDK_PIXMAP_C__)
+gdk_bitmap_create_from_data
 gdk_pixmap_colormap_create_from_xpm
+gdk_pixmap_create_from_data
 gdk_pixmap_create_from_xpm
 gdk_pixmap_colormap_create_from_xpm_d
 gdk_pixmap_create_from_xpm_d
 gdk_pixmap_get_type G_GNUC_CONST
+gdk_pixmap_new
 #endif
 #endif
 
 #if IN_HEADER(__GDK_PIXMAP_H__)
 #if IN_FILE(__GDK_PIXMAP_X11_C__)
-gdk_pixmap_create_from_data
 gdk_pixmap_foreign_new
 gdk_pixmap_foreign_new_for_display
 gdk_pixmap_foreign_new_for_screen
 gdk_pixmap_lookup
 gdk_pixmap_lookup_for_display
-gdk_pixmap_new
 #endif
 #endif
 
diff --git a/gdk/gdkdisplay.c b/gdk/gdkdisplay.c
index 2a5b92c..2815f03 100644
--- a/gdk/gdkdisplay.c
+++ b/gdk/gdkdisplay.c
@@ -22,9 +22,11 @@
  */
 
 #include "config.h"
+#include <math.h>
 #include <glib.h>
 #include "gdk.h"		/* gdk_event_send_client_message() */
 #include "gdkdisplay.h"
+#include "gdkwindowimpl.h"
 #include "gdkinternals.h"
 #include "gdkmarshalers.h"
 #include "gdkscreen.h"
@@ -60,6 +62,14 @@ static GdkWindow* singlehead_default_window_get_pointer (GdkWindow       *window
 static GdkWindow* singlehead_default_window_at_pointer  (GdkScreen       *screen,
 							 gint            *win_x,
 							 gint            *win_y);
+static GdkWindow *gdk_window_real_window_get_pointer     (GdkDisplay       *display,
+                                                          GdkWindow        *window,
+                                                          gint             *x,
+                                                          gint             *y,
+                                                          GdkModifierType  *mask);
+static GdkWindow *gdk_display_real_get_window_at_pointer (GdkDisplay       *display,
+                                                          gint             *win_x,
+                                                          gint             *win_y);
 
 static guint signals[LAST_SIGNAL] = { 0 };
 
@@ -67,8 +77,8 @@ static char *gdk_sm_client_id;
 
 static const GdkDisplayPointerHooks default_pointer_hooks = {
   _gdk_windowing_get_pointer,
-  _gdk_windowing_window_get_pointer,
-  _gdk_windowing_window_at_pointer
+  gdk_window_real_window_get_pointer,
+  gdk_display_real_get_window_at_pointer
 };
 
 static const GdkDisplayPointerHooks singlehead_pointer_hooks = {
@@ -471,6 +481,73 @@ gdk_display_get_pointer (GdkDisplay      *display,
     *mask = tmp_mask;
 }
 
+static GdkWindow *
+gdk_display_real_get_window_at_pointer (GdkDisplay *display,
+                                        gint       *win_x,
+                                        gint       *win_y)
+{
+  GdkWindow *window;
+
+  window = _gdk_windowing_window_at_pointer (display, win_x, win_y);
+
+  if (window && GDK_WINDOW_OBJECT (window)->offscreen_children)
+    {
+      GdkWindow *child;
+      gdouble child_x, child_y;
+
+      child =  _gdk_window_get_offscreen_window_child_at (window,
+                                                          *win_x, *win_y,
+                                                          &child_x, &child_y);
+      *win_x = floor (child_x + 0.5);
+      *win_y = floor (child_y + 0.5);
+
+      return child;
+    }
+
+  return window;
+}
+
+static GdkWindow *
+gdk_window_real_window_get_pointer (GdkDisplay       *display,
+                                    GdkWindow        *window,
+                                    gint             *x,
+                                    gint             *y,
+                                    GdkModifierType  *mask)
+{
+  GdkWindowObject *private;
+  GdkWindow *event_window;
+  GdkWindow *pointer_window;
+  gint tmpx, tmpy;
+
+  private = (GdkWindowObject *) window;
+
+  event_window = _gdk_window_get_offscreen_event_window (window);
+
+  pointer_window = _gdk_windowing_window_get_pointer (display,
+                                                      event_window,
+                                                      &tmpx, &tmpy,
+                                                      mask);
+
+  /* Convert to offscreen if needed */
+  _gdk_offscreen_window_transform_coords (event_window,
+                                          window,
+                                          &tmpx, &tmpy);
+  if (x)
+    *x = tmpx;
+  if (y)
+    *y = tmpy;
+
+  /* If the pointer is in a window with offscreen children, recalculate pointer
+   * window, as the pointer it might be in an offscreen child.
+   */
+  if (pointer_window != NULL &&
+      GDK_WINDOW_OBJECT (pointer_window)->offscreen_children)
+    pointer_window = gdk_display_real_get_window_at_pointer (display,
+                                                             &tmpx, &tmpy);
+
+  return pointer_window;
+}
+
 /**
  * gdk_display_get_window_at_pointer:
  * @display: a #GdkDisplay
@@ -582,8 +659,8 @@ singlehead_default_window_get_pointer (GdkWindow       *window,
 				       gint            *y,
 				       GdkModifierType *mask)
 {
-  return _gdk_windowing_window_get_pointer (gdk_drawable_get_display (window),
-					    window, x, y, mask);
+  return gdk_window_real_window_get_pointer (gdk_drawable_get_display (window),
+                                             window, x, y, mask);
 }
 
 static GdkWindow*
@@ -591,8 +668,8 @@ singlehead_default_window_at_pointer  (GdkScreen       *screen,
 				       gint            *win_x,
 				       gint            *win_y)
 {
-  return _gdk_windowing_window_at_pointer (gdk_screen_get_display (screen),
-					   win_x, win_y);
+  return gdk_display_real_get_window_at_pointer (gdk_screen_get_display (screen),
+                                                 win_x, win_y);
 }
 
 /**
diff --git a/gdk/gdkdraw.c b/gdk/gdkdraw.c
index 0876695..13efed9 100644
--- a/gdk/gdkdraw.c
+++ b/gdk/gdkdraw.c
@@ -48,6 +48,7 @@ static GdkDrawable* gdk_drawable_real_get_composite_drawable (GdkDrawable  *draw
 							      gint          height,
 							      gint         *composite_x_offset,
 							      gint         *composite_y_offset);
+static GdkDrawable *gdk_drawable_real_get_source_drawable    (GdkDrawable  *drawable);
 static GdkRegion *  gdk_drawable_real_get_visible_region     (GdkDrawable  *drawable);
 static void         gdk_drawable_real_draw_pixbuf            (GdkDrawable  *drawable,
 							      GdkGC        *gc,
@@ -70,6 +71,7 @@ gdk_drawable_class_init (GdkDrawableClass *klass)
 {
   klass->get_image = gdk_drawable_real_get_image;
   klass->get_composite_drawable = gdk_drawable_real_get_composite_drawable;
+  klass->get_source_drawable = gdk_drawable_real_get_source_drawable;
   /* Default implementation for clip and visible region is the same */
   klass->get_clip_region = gdk_drawable_real_get_visible_region;
   klass->get_visible_region = gdk_drawable_real_get_visible_region;
@@ -1187,7 +1189,13 @@ gdk_drawable_real_get_image (GdkDrawable     *drawable,
   return gdk_drawable_copy_to_image (drawable, NULL, x, y, 0, 0, width, height);
 }
 
-static GdkDrawable*
+static GdkDrawable *
+gdk_drawable_real_get_source_drawable (GdkDrawable *drawable)
+{
+  return drawable;
+}
+
+static GdkDrawable *
 gdk_drawable_real_get_composite_drawable (GdkDrawable *drawable,
                                           gint         x,
                                           gint         y,
@@ -1773,5 +1781,22 @@ _gdk_drawable_get_scratch_gc (GdkDrawable *drawable,
     }
 }
 
+/**
+ * _gdk_drawable_get_source_drawable:
+ * @drawable: a #GdkDrawable
+ *
+ * Returns a drawable for the passed @drawable that is guaranteed to be
+ * usable to create a pixmap (e.g.: not an offscreen window).
+ *
+ * Since: 2.16
+ */
+GdkDrawable *
+_gdk_drawable_get_source_drawable (GdkDrawable *drawable)
+{
+  g_return_val_if_fail (GDK_IS_DRAWABLE (drawable), NULL);
+
+  return GDK_DRAWABLE_GET_CLASS (drawable)->get_source_drawable (drawable);
+}
+
 #define __GDK_DRAW_C__
 #include "gdkaliasdef.c"
diff --git a/gdk/gdkdrawable.h b/gdk/gdkdrawable.h
index 508bf86..6cb0fc0 100644
--- a/gdk/gdkdrawable.h
+++ b/gdk/gdkdrawable.h
@@ -200,8 +200,9 @@ struct _GdkDrawableClass
 
   cairo_surface_t *(*ref_cairo_surface) (GdkDrawable *drawable);
 
+  GdkDrawable *(*get_source_drawable) (GdkDrawable *drawable);
+
   /* Padding for future expansion */
-  void         (*_gdk_reserved4)  (void);
   void         (*_gdk_reserved5)  (void);
   void         (*_gdk_reserved6)  (void);
   void         (*_gdk_reserved7)  (void);
diff --git a/gdk/gdkevents.c b/gdk/gdkevents.c
index fde9ab1..611a1d0 100644
--- a/gdk/gdkevents.c
+++ b/gdk/gdkevents.c
@@ -121,6 +121,34 @@ _gdk_event_queue_append (GdkDisplay *display,
 }
 
 /**
+ * _gdk_event_queue_insert_after:
+ * @display: a #GdkDisplay
+ * @sibling: Append after this event.
+ * @event: Event to append.
+ *
+ * Appends an event after the specified event, or if it isn't in
+ * the queue, onto the tail of the event queue.
+ *
+ * Returns: the newly appended list node.
+ *
+ * Since: 2.16
+ */
+GList*
+_gdk_event_queue_insert_after (GdkDisplay *display,
+                               GdkEvent   *sibling,
+                               GdkEvent   *event)
+{
+  GList *prev = g_list_find (display->queued_events, sibling);
+  if (prev && prev->next)
+    {
+      display->queued_events = g_list_insert_before (display->queued_events, prev->next, event);
+      return prev->next;
+    }
+  else
+    return _gdk_event_queue_append (display, event);
+}
+
+/**
  * _gdk_event_queue_remove_link:
  * @display: a #GdkDisplay
  * @node: node to remove
@@ -1069,13 +1097,18 @@ gdk_synthesize_click (GdkDisplay *display,
 		      gint	  nclicks)
 {
   GdkEvent temp_event;
+  GdkEvent *event_copy;
+  GList *link;
   
   g_return_if_fail (event != NULL);
   
   temp_event = *event;
   temp_event.type = (nclicks == 2) ? GDK_2BUTTON_PRESS : GDK_3BUTTON_PRESS;
-  
-  gdk_display_put_event (display, &temp_event);
+
+  event_copy = gdk_event_copy (&temp_event);
+  link = _gdk_event_queue_append (display, event_copy);
+
+  _gdk_event_rewrite_for_offscreen (display, link, event_copy);
 }
 
 void
diff --git a/gdk/gdkinternals.h b/gdk/gdkinternals.h
index 09ae12d..d04afcc 100644
--- a/gdk/gdkinternals.h
+++ b/gdk/gdkinternals.h
@@ -181,15 +181,18 @@ extern gchar     *_gdk_display_arg_name;
 void      _gdk_events_queue  (GdkDisplay *display);
 GdkEvent* _gdk_event_unqueue (GdkDisplay *display);
 
-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,
-				     GdkEvent   *event);
+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);
+GList* _gdk_event_queue_insert_after (GdkDisplay *display,
+                                      GdkEvent   *after_event,
+                                      GdkEvent   *event);
+void   _gdk_event_button_generate    (GdkDisplay *display,
+				      GdkEvent   *event);
 
 void _gdk_windowing_event_data_copy (const GdkEvent *src,
                                      GdkEvent       *dst);
@@ -226,6 +229,8 @@ GdkImage *_gdk_drawable_copy_to_image (GdkDrawable  *drawable,
 
 cairo_surface_t *_gdk_drawable_ref_cairo_surface (GdkDrawable *drawable);
 
+GdkDrawable *_gdk_drawable_get_source_drawable (GdkDrawable *drawable);
+
 /* GC caching */
 GdkGC *_gdk_drawable_get_scratch_gc (GdkDrawable *drawable,
 				     gboolean     graphics_exposures);
@@ -240,14 +245,30 @@ void _gdk_gc_update_context (GdkGC          *gc,
  * Interfaces used by windowing code *
  *************************************/
 
-GdkWindow *_gdk_window_new               (GdkWindow     *window,
-                                          GdkWindowAttr *attributes,
-                                          gint           attributes_mask);
-void       _gdk_window_destroy           (GdkWindow     *window,
-					  gboolean       foreign_destroy);
-void       _gdk_window_clear_update_area (GdkWindow     *window);
-
-void       _gdk_screen_close             (GdkScreen     *screen);
+GdkPixmap *_gdk_pixmap_new               (GdkDrawable    *drawable,
+                                          gint            width,
+                                          gint            height,
+                                          gint            depth);
+GdkPixmap *_gdk_pixmap_create_from_data  (GdkDrawable    *drawable,
+                                          const gchar    *data,
+                                          gint            width,
+                                          gint            height,
+                                          gint            depth,
+                                          const GdkColor *fg,
+                                          const GdkColor *bg);
+GdkPixmap *_gdk_bitmap_create_from_data  (GdkDrawable    *drawable,
+                                          const gchar    *data,
+                                          gint            width,
+                                          gint            height);
+
+GdkWindow *_gdk_window_new               (GdkWindow      *window,
+                                          GdkWindowAttr  *attributes,
+                                          gint            attributes_mask);
+void       _gdk_window_destroy           (GdkWindow      *window,
+                                          gboolean        foreign_destroy);
+void       _gdk_window_clear_update_area (GdkWindow      *window);
+
+void       _gdk_screen_close             (GdkScreen      *screen);
 
 const char *_gdk_get_sm_client_id (void);
 
@@ -305,10 +326,6 @@ GdkWindow* _gdk_windowing_window_at_pointer  (GdkDisplay       *display,
 gint _gdk_windowing_get_bits_for_depth (GdkDisplay *display,
 					gint        depth);
 
-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)
 
@@ -434,6 +451,55 @@ char *_gdk_windowing_get_startup_notify_id (GAppLaunchContext *context,
 void  _gdk_windowing_launch_failed         (GAppLaunchContext *context, 
 				            const char        *startup_notify_id);
 
+void _gdk_windowing_grab_broken          (GdkDisplay *display);
+void _gdk_windowing_update_implicit_grab (GdkWindow  *old_grab,
+                                          GdkWindow  *new_grab,
+                                          guint       event_mask,
+                                          guint32     time_);
+
+/*****************************
+ * offscreen window routines *
+ *****************************/
+
+GdkWindow* _gdk_window_new_offscreen                 (GdkWindow     *parent,
+                                                      GdkWindowAttr *attributes,
+                                                      gint           attributes_mask,
+                                                      gboolean       toplevel);
+void       _gdk_offscreen_window_destroy             (GdkWindow     *window,
+                                                      gboolean       recursing);
+void       _gdk_offscreen_window_set_pointer_grab    (GdkWindow     *old_grab_window,
+                                                      GdkWindow     *old_native_grab_window,
+                                                      GdkWindow     *grab_window,
+                                                      gboolean       owner_events,
+                                                      guint          event_mask,
+                                                      guint32        time_);
+void       _gdk_offscreen_window_unset_pointer_grab  (GdkWindow     *window,
+                                                      gboolean       regrab,
+                                                      gboolean       grab_one_button_release,
+                                                      guint32        time_);
+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);
+void       _gdk_event_rewrite_for_offscreen          (GdkDisplay    *display,
+                                                      GList         *event_node,
+                                                      GdkEvent      *event);
+void       _gdk_window_add_offscreen_child           (GdkWindow     *window);
+void       _gdk_window_remove_offscreen_child        (GdkWindow     *window);
+GdkWindow *_gdk_window_get_offscreen_window_child_at (GdkWindow     *parent,
+                                                      gdouble        x,
+                                                      gdouble        y,
+                                                      gdouble       *win_x_out,
+                                                      gdouble       *win_y_out);
+GdkWindow *_gdk_window_get_offscreen_event_window    (GdkWindow     *window);
+void       _gdk_offscreen_window_transform_coords    (GdkWindow     *event_window,
+                                                      GdkWindow     *relative_window,
+                                                      gint          *x,
+                                                      gint          *y);
+gboolean   _gdk_window_is_toplevel_offscreen         (GdkWindow     *child);
+
 
 /************************************
  * Initialization and exit routines *
diff --git a/gdk/gdkoffscreenwindow.c b/gdk/gdkoffscreenwindow.c
new file mode 100644
index 0000000..382f3a0
--- /dev/null
+++ b/gdk/gdkoffscreenwindow.c
@@ -0,0 +1,2615 @@
+/* GDK - The GIMP Drawing Kit
+ * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/*
+ * Modified by the GTK+ Team and others 1997-2005.  See the AUTHORS
+ * file for a list of people on the GTK+ Team.  See the ChangeLog
+ * files for a list of changes.  These files are distributed with
+ * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
+ */
+
+#include <config.h>
+#include <math.h>
+#include "gdk.h"
+#include "gdkwindow.h"
+#include "gdkinternals.h"
+#include "gdkwindowimpl.h"
+#include "gdkpixmap.h"
+#include "gdkdrawable.h"
+#include "gdktypes.h"
+#include "gdkscreen.h"
+#include "gdkgc.h"
+#include "gdkcolor.h"
+#include "gdkcursor.h"
+#include "gdkalias.h"
+
+/* LIMITATIONS:
+ *
+ * Offscreen windows must be child windows
+ * Offscreen windows can't be the child of a foreign window,
+ *   nor contain foreign windows
+ * All drawing must be within begin/end_paint
+ * The confine_window argument of pointer grabs doesn't work
+ * GDK_POINTER_MOTION_HINT_MASK isn't effective
+ * You can't reparent between offscreen windows and normal windows
+ */
+
+/* TODO:
+
+ * change expose order so that children are exposed before parent, to
+   avoid double drawing due to damage from children coming after parent expose.
+   Event with the right order we will expose twice, although both with an
+   uptodate pixmap.
+   Not sure how to fix this. Perhaps with a get_and_clear_damage() call?
+
+ * gtk_widget_reparent should unrealize/realize if the new parent GdkWindow is
+   offscreen and widget is not, or if the new parent is not offscreen and the
+   widget is. The problem is, how do you know what the new parent GdkWindow is
+   before gtk_container_add()?
+
+ * Unite the API for all 3 kinds of redirection? how?
+
+ * support cursors in offscreen windows
+
+ * XIM support (is this possible?)
+
+ * Introduce generic "this point in the window in root/other window coords"
+   operation in gtk
+
+ * dnd drop positions wrong with transformed windows. Needs above point
+   transformation.
+
+ * various menu popup positions wrong with transformed widgets.  Needs above
+   point transformation.
+
+ */
+
+typedef struct _GdkOffscreenWindow      GdkOffscreenWindow;
+typedef struct _GdkOffscreenWindowClass GdkOffscreenWindowClass;
+
+struct _GdkOffscreenWindow
+{
+  GdkDrawable parent_instance;
+
+  GdkWindow *wrapper;
+  gint width, height;
+  GdkCursor *cursor;
+  GdkColormap *colormap;
+  GdkScreen *screen;
+
+  /* same window if this is the toplevel */
+  GdkOffscreenWindow *toplevel_offscreen;
+
+  /* computed position in pixmap:
+   *   abs: position of top left corner of window in pixmap
+   *   llim: lower limit of visible part of window
+   *         this can be != abs if the window is outside its parent
+   *   lim: upper limit of visible part of window
+   */
+  gint abs_x, abs_y;
+  gint lim_x, lim_y;
+  gint llim_x, llim_y;
+};
+
+struct _GdkOffscreenWindowClass
+{
+  GdkDrawableClass parent_class;
+};
+
+#define GDK_TYPE_OFFSCREEN_WINDOW            (gdk_offscreen_window_get_type())
+#define GDK_OFFSCREEN_WINDOW(object)         (G_TYPE_CHECK_INSTANCE_CAST ((object), GDK_TYPE_OFFSCREEN_WINDOW, GdkOffscreenWindow))
+#define GDK_IS_OFFSCREEN_WINDOW(object)      (G_TYPE_CHECK_INSTANCE_TYPE ((object), GDK_TYPE_OFFSCREEN_WINDOW))
+#define GDK_OFFSCREEN_WINDOW_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), GDK_TYPE_OFFSCREEN_WINDOW, GdkOffscreenWindowClass))
+#define GDK_IS_OFFSCREEN_WINDOW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDK_TYPE_OFFSCREEN_WINDOW))
+#define GDK_OFFSCREEN_WINDOW_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), GDK_TYPE_OFFSCREEN_WINDOW, GdkOffscreenWindowClass))
+
+static void       gdk_offscreen_window_impl_iface_init    (GdkWindowImplIface         *iface);
+static void       send_crossing_events                    (GdkWindowOffscreenChildren *offscreen_children,
+							   GdkWindow                  *event_window,
+							   GdkWindow                  *src,
+							   GdkWindow                  *dest,
+							   GdkCrossingMode             mode,
+							   int                         event_x,
+							   int                         event_y,
+							   GdkModifierType             mask,
+							   guint32                     time_,
+							   gboolean                    do_first,
+							   gboolean                    do_last);
+static GdkWindow *gdk_offscreen_window_get_window_at      (GdkWindow                  *window,
+						           gdouble                     x,
+                                                           gdouble                     y,
+                                                           gdouble                    *win_x,
+                                                           gdouble                    *win_y);
+static void       gdk_offscreen_window_hide               (GdkWindow                  *window);
+static void       gdk_offscreen_window_clear_area         (GdkWindow                  *window,
+							   gint                        x,
+							   gint                        y,
+							   gint                        width,
+							   gint                        height,
+							   gboolean                    send_expose);
+static GdkRegion *gdk_offscreen_window_get_visible_region (GdkDrawable                *drawable);
+
+static void convert_event_coords_to_window (GdkWindow *event_window,
+					    GdkWindow *window,
+					    gdouble    event_x,
+                                            gdouble    event_y,
+					    gdouble   *window_x,
+                                            gdouble   *window_y);
+
+G_DEFINE_TYPE_WITH_CODE (GdkOffscreenWindow,
+                         gdk_offscreen_window,
+                         GDK_TYPE_DRAWABLE,
+			 G_IMPLEMENT_INTERFACE (GDK_TYPE_WINDOW_IMPL,
+						gdk_offscreen_window_impl_iface_init));
+
+
+static void
+gdk_offscreen_window_finalize (GObject *object)
+{
+  GdkOffscreenWindow *offscreen = GDK_OFFSCREEN_WINDOW (object);
+
+  if (offscreen->cursor)
+    gdk_cursor_unref (offscreen->cursor);
+
+  offscreen->cursor = NULL;
+
+  G_OBJECT_CLASS (gdk_offscreen_window_parent_class)->finalize (object);
+}
+
+static void
+gdk_offscreen_window_init (GdkOffscreenWindow *window)
+{
+}
+
+void
+_gdk_offscreen_window_destroy (GdkWindow *window,
+                               gboolean   recursing)
+{
+  GdkWindowObject *private = GDK_WINDOW_OBJECT (window);
+  GdkOffscreenWindow *offscreen;
+
+  offscreen = GDK_OFFSCREEN_WINDOW (private->impl);
+
+  if (offscreen->toplevel_offscreen == offscreen && private->parent)
+    _gdk_window_remove_offscreen_child (GDK_WINDOW (private->parent));
+
+  if (!recursing)
+    gdk_offscreen_window_hide (window);
+
+  g_object_unref (offscreen->colormap);
+  offscreen->colormap = NULL;
+}
+
+static gboolean
+is_parent_of (GdkWindow *parent,
+              GdkWindow *child)
+{
+  GdkWindow *w;
+
+  w = child;
+  while (w != NULL)
+    {
+      if (w == parent)
+	return TRUE;
+
+      w = gdk_window_get_parent (w);
+    }
+
+  return FALSE;
+}
+
+static GdkGC *
+gdk_offscreen_window_create_gc (GdkDrawable     *drawable,
+				GdkGCValues     *values,
+				GdkGCValuesMask  values_mask)
+{
+  GdkOffscreenWindow *offscreen = GDK_OFFSCREEN_WINDOW (drawable);
+  GdkWindowObject *private;
+
+  private = GDK_WINDOW_OBJECT (offscreen->wrapper);
+
+  return gdk_gc_new_with_values (private->redirect->pixmap, values, values_mask);
+}
+
+static GdkImage*
+gdk_offscreen_window_copy_to_image (GdkDrawable    *drawable,
+				    GdkImage       *image,
+				    gint            src_x,
+				    gint            src_y,
+				    gint            dest_x,
+				    gint            dest_y,
+				    gint            width,
+				    gint            height)
+{
+  GdkOffscreenWindow *offscreen = GDK_OFFSCREEN_WINDOW (drawable);
+  GdkWindowObject *private;
+  gint x_offset, y_offset;
+
+  private = GDK_WINDOW_OBJECT (offscreen->wrapper);
+
+  GDK_WINDOW_IMPL_GET_IFACE (offscreen)->get_offsets (drawable,
+                                                      &x_offset,
+                                                      &y_offset);
+
+  return gdk_drawable_copy_to_image (private->redirect->pixmap,
+                                     image,
+                                     src_x - x_offset,
+                                     src_y - y_offset,
+                                     dest_x, dest_y,
+                                     width, height);
+}
+
+static cairo_surface_t *
+gdk_offscreen_window_ref_cairo_surface (GdkDrawable *drawable)
+{
+  g_error ("Attempt to get a cairo surface directly for an offscreen "
+           "window. This is not supported, you must use "
+           "gdk_window_begin_paint_region().");
+
+  return NULL;
+}
+
+
+
+static void
+gdk_offscreen_window_empty_draw_op (GdkDrawable *drawable)
+{
+  /* All actual rendering is done in gdkwindow.c */
+}
+
+static GdkWindow *
+find_offscreen_child (GdkWindow *window,
+                      gdouble    x,
+                      gdouble    y)
+{
+  GdkWindowObject *private = (GdkWindowObject *) window;
+  GdkWindow *hit_window;
+
+  hit_window = NULL;
+
+  if (private->offscreen_hooks &&
+      private->offscreen_hooks->find_offscreen_child)
+    hit_window = private->offscreen_hooks->find_offscreen_child (window, x, y);
+
+  return hit_window;
+}
+
+
+static void
+parent_to_window (GdkWindow *parent,
+                  GdkWindow *window,
+                  gdouble    x,
+                  gdouble    y,
+		  gdouble   *window_x,
+                  gdouble   *window_y);
+
+static void
+window_to_parent (GdkWindow *window,
+                  GdkWindow *parent,
+                  gdouble    x,
+                  gdouble    y,
+		  gdouble   *parent_x,
+                  gdouble   *parent_y)
+{
+  GdkWindowObject *window_private = (GdkWindowObject *) window;
+  GdkWindowObject *parent_private = (GdkWindowObject *) parent;
+
+  /* Default value if no handler */
+  *parent_x = x + window_private->x;
+  *parent_y = y + window_private->y;
+
+  if (parent_private->offscreen_hooks &&
+      parent_private->offscreen_hooks->window_to_parent)
+    parent_private->offscreen_hooks->window_to_parent (window, parent,
+                                                       x, y,
+                                                       parent_x, parent_y);
+}
+
+static void
+parent_to_window (GdkWindow *parent,
+                  GdkWindow *window,
+                  gdouble    x,
+                  gdouble    y,
+		  gdouble   *window_x,
+                  gdouble   *window_y)
+{
+  GdkWindowObject *parent_private = (GdkWindowObject *) parent;
+  GdkWindowObject *window_private = (GdkWindowObject *) window;
+
+  /* Default value if no handler */
+  *window_x = x - window_private->x;
+  *window_y = y - window_private->y;
+
+  if (parent_private->offscreen_hooks &&
+      parent_private->offscreen_hooks->parent_to_window)
+    parent_private->offscreen_hooks->parent_to_window (parent, window,
+                                                       x, y,
+                                                       window_x, window_y);
+}
+
+
+static GdkColormap*
+gdk_offscreen_window_get_colormap (GdkDrawable *drawable)
+{
+  GdkOffscreenWindow *offscreen = GDK_OFFSCREEN_WINDOW (drawable);
+
+  return offscreen->colormap;
+}
+
+static void
+gdk_offscreen_window_set_colormap (GdkDrawable *drawable,
+				   GdkColormap*colormap)
+{
+  GdkOffscreenWindow *offscreen = GDK_OFFSCREEN_WINDOW (drawable);
+
+  if (colormap && GDK_WINDOW_DESTROYED (offscreen->wrapper))
+    return;
+
+  if (offscreen->colormap == colormap)
+    return;
+
+  if (offscreen->colormap)
+    g_object_unref (offscreen->colormap);
+
+  offscreen->colormap = colormap;
+  if (offscreen->colormap)
+    g_object_ref (offscreen->colormap);
+}
+
+
+static gint
+gdk_offscreen_window_get_depth (GdkDrawable *drawable)
+{
+  GdkOffscreenWindow *offscreen = GDK_OFFSCREEN_WINDOW (drawable);
+
+  return gdk_drawable_get_depth (offscreen->wrapper);
+}
+
+static void
+gdk_offscreen_window_get_size (GdkDrawable *drawable,
+			       gint         *width,
+			       gint         *height)
+{
+  GdkOffscreenWindow *offscreen = GDK_OFFSCREEN_WINDOW (drawable);
+
+  if (width)
+    *width = offscreen->width;
+  if (height)
+    *height = offscreen->height;
+}
+
+static GdkScreen*
+gdk_offscreen_window_get_screen (GdkDrawable *drawable)
+{
+  GdkOffscreenWindow *offscreen = GDK_OFFSCREEN_WINDOW (drawable);
+
+  return offscreen->screen;
+}
+
+static GdkVisual*
+gdk_offscreen_window_get_visual (GdkDrawable    *drawable)
+{
+  GdkOffscreenWindow *offscreen = GDK_OFFSCREEN_WINDOW (drawable);
+
+  return gdk_drawable_get_visual (offscreen->wrapper);
+}
+
+static void
+gdk_offscreen_window_class_init (GdkOffscreenWindowClass *klass)
+{
+  GdkDrawableClass *drawable_class = GDK_DRAWABLE_CLASS (klass);
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->finalize = gdk_offscreen_window_finalize;
+
+  drawable_class->create_gc = gdk_offscreen_window_create_gc;
+  drawable_class->_copy_to_image = gdk_offscreen_window_copy_to_image;
+  drawable_class->ref_cairo_surface = gdk_offscreen_window_ref_cairo_surface;
+  drawable_class->set_colormap = gdk_offscreen_window_set_colormap;
+  drawable_class->get_colormap = gdk_offscreen_window_get_colormap;
+  drawable_class->get_depth = gdk_offscreen_window_get_depth;
+  drawable_class->get_size = gdk_offscreen_window_get_size;
+  drawable_class->get_screen = gdk_offscreen_window_get_screen;
+  drawable_class->get_visual = gdk_offscreen_window_get_visual;
+  drawable_class->get_clip_region = gdk_offscreen_window_get_visible_region;
+  drawable_class->get_visible_region = gdk_offscreen_window_get_visible_region;
+
+  /* All actual rendering is done in gdkwindow.c with the redirect */
+  drawable_class->draw_drawable = (gpointer)gdk_offscreen_window_empty_draw_op;
+  drawable_class->draw_rectangle = (gpointer)gdk_offscreen_window_empty_draw_op;
+  drawable_class->draw_points = (gpointer)gdk_offscreen_window_empty_draw_op;
+  drawable_class->draw_arc = (gpointer)gdk_offscreen_window_empty_draw_op;
+  drawable_class->draw_polygon = (gpointer)gdk_offscreen_window_empty_draw_op;
+  drawable_class->draw_text = (gpointer)gdk_offscreen_window_empty_draw_op;
+  drawable_class->draw_text_wc = (gpointer)gdk_offscreen_window_empty_draw_op;
+  drawable_class->draw_segments = (gpointer)gdk_offscreen_window_empty_draw_op;
+  drawable_class->draw_lines = (gpointer)gdk_offscreen_window_empty_draw_op;
+  drawable_class->draw_image = (gpointer)gdk_offscreen_window_empty_draw_op;
+  drawable_class->draw_pixbuf = (gpointer)gdk_offscreen_window_empty_draw_op;
+}
+
+static void
+recompute_abs_positions (GdkWindow *window,
+			 gint       parent_x,
+                         gint       parent_y,
+			 gint       parent_llim_x,
+                         gint       parent_llim_y,
+			 gint       parent_lim_x,
+                         gint       parent_lim_y)
+{
+  GdkWindowObject *private = (GdkWindowObject *) window;
+  GdkOffscreenWindow *offscreen;
+  GList *l;
+  gint x, y;
+
+  if (!GDK_WINDOW_IS_MAPPED (window))
+    return;
+
+  offscreen = GDK_OFFSCREEN_WINDOW (private->impl);
+
+  offscreen->abs_x = parent_x + private->x;
+  offscreen->abs_y = parent_y + private->y;
+
+  x = MAX (parent_llim_x, offscreen->abs_x);
+  x = MIN (x, parent_lim_x);
+  offscreen->llim_x = x;
+
+  y = MAX (parent_llim_y, offscreen->abs_y);
+  y = MIN (y, parent_lim_y);
+  offscreen->llim_y = y;
+
+  x = MIN (parent_lim_x, offscreen->abs_x + offscreen->width);
+  x = MAX (x, offscreen->llim_x);
+  offscreen->lim_x = x;
+
+  y = MIN (parent_lim_y, offscreen->abs_y + offscreen->height);
+  y = MAX (y, offscreen->llim_y);
+  offscreen->lim_y = y;
+
+  g_assert (offscreen->llim_x <= offscreen->lim_x);
+  g_assert (offscreen->llim_y <= offscreen->lim_y);
+
+  for (l = private->children; l; l = l->next)
+    recompute_abs_positions (l->data,
+			     offscreen->abs_x, offscreen->abs_y,
+			     offscreen->llim_x, offscreen->llim_y,
+			     offscreen->lim_x, offscreen->lim_y);
+}
+
+
+static void
+recompute_window (GdkWindow *window)
+{
+  GdkWindowObject *private = (GdkWindowObject *)window;
+  GdkOffscreenWindow *offscreen, *parent_offscreen;
+  GdkWindowObject *parent_private;
+  GList *l;
+
+  offscreen = GDK_OFFSCREEN_WINDOW (private->impl);
+
+  if (offscreen->toplevel_offscreen == offscreen)
+    {
+      offscreen->abs_x = 0;
+      offscreen->abs_y = 0;
+      offscreen->llim_x = 0;
+      offscreen->llim_y = 0;
+      offscreen->lim_x = offscreen->width;
+      offscreen->lim_y = offscreen->height;
+
+      for (l = private->children; l; l = l->next)
+	recompute_abs_positions (l->data,
+				 offscreen->abs_x, offscreen->abs_y,
+				 offscreen->llim_x, offscreen->llim_y,
+				 offscreen->lim_x, offscreen->lim_y);
+    }
+  else
+    {
+      parent_private = private->parent;
+      parent_offscreen = GDK_OFFSCREEN_WINDOW (parent_private->impl);
+      recompute_abs_positions (window,
+			       parent_offscreen->abs_x,
+			       parent_offscreen->abs_y,
+			       parent_offscreen->llim_x,
+			       parent_offscreen->llim_y,
+			       parent_offscreen->lim_x,
+			       parent_offscreen->lim_y);
+    }
+}
+
+
+/**
+ * gdk_offscreen_window_new:
+ * @parent: a #GdkWindow
+ * @attributes: attributes of the new window
+ * @attributes_mask: mask indicating which fields in @attributes are valid
+ *
+ * Creates a new offscreen #GdkWindow child using the attributes from
+ * @attributes. See #GdkWindowAttr and #GdkWindowAttributesType for
+ * more details.
+
+ * Being offscreen means that the window will not automatically
+ * rendered to the parent window, instead it is drawn to a pixmap that
+ * is availible with gdk_window_get_offscreen_pixmap(). You can use
+ * this pixmap to render the window however you want. When the
+ * offscreen pixmap is drawn into, damage events are sent to the
+ * window, so the GtkWidget::damage_event signal handler can be used
+ * to make sure the parent window is updated.
+ *
+ * All children of an offscreen window are automatically turned into
+ * offscreen windows that share the offscreen pixmap with the window
+ * created using gdk_window_new_offscreen(). You can nest offscreen
+ * windows, but at least one ancestor must be a non-offscreen window.
+ *
+ * To correctly support offscreen children a parent window should
+ * catch the GdkWindow::find-offscreen-child signal on the parent
+ * window. Additionally, if you render the child transformed you need
+ * to implement GdkWindow::parent-to-window-coordinate and
+ * GdkWindow::window-to-parent-coordinate to get coordinates correct
+ * in e.g. pointer events.
+ *
+ * Offscreen windows have some limitations:
+ *
+ * <itemizedlist>
+ * <listitem><para>They can't be toplevel windows.</para></listitem>
+ * <listitem><para>They can't contain foreign windows, nor be children
+ * of foreign windows. This means #GtkPlug/#GtkSocket do not work
+ * with them.</para></listitem>
+ * <listitem><para>X specific features like XVideo and GLX doesn't
+ * work.</para></listitem>
+ * <listitem><para>All rendering must be done inside
+ * gdk_window_begin_paint_region() and gdk_window_end_paint()
+ * calls.</para></listitem>
+ * <listitem><para>The confine_window argument of pointer grabs
+ * does not work</para></listitem>
+ * <listitem><para>The %GDK_POINTER_MOTION_HINT_MASK mask is not
+ * effective</para></listitem>
+ * <listitem><para>You can't reparent between offscreen windows and
+ * normal windows</para></listitem>
+ * </itemizedlist>
+ *
+ * Return value: the new #GdkWindow
+ *
+ * Since: 2.14
+ */
+GdkWindow*
+gdk_offscreen_window_new (GdkWindow     *parent,
+			  GdkWindowAttr *attributes,
+			  gint           attributes_mask)
+{
+  return _gdk_window_new_offscreen (parent, attributes, attributes_mask, TRUE);
+}
+
+GdkWindow *
+_gdk_window_new_offscreen (GdkWindow     *parent,
+			   GdkWindowAttr *attributes,
+			   gint           attributes_mask,
+			   gboolean       toplevel)
+{
+  GdkWindow *window;
+  GdkWindowObject *parent_private;
+  GdkWindowObject *private;
+  GdkOffscreenWindow *offscreen;
+  int x, y;
+  GdkScreen *screen;
+  GdkVisual *visual;
+
+  g_return_val_if_fail (attributes != NULL, NULL);
+
+  if (parent == NULL ||
+      (GDK_WINDOW_TYPE (parent) == GDK_WINDOW_ROOT) ||
+      (GDK_WINDOW_TYPE (parent) == GDK_WINDOW_FOREIGN) ||
+      (attributes->window_type != GDK_WINDOW_CHILD) )
+    {
+      g_warning ("Only child offscreen windows allowed");
+      return NULL;
+    }
+
+  if (GDK_WINDOW_DESTROYED (parent))
+    return NULL;
+
+  parent_private = (GdkWindowObject*) parent;
+
+  window = g_object_new (GDK_TYPE_WINDOW, NULL);
+  private = (GdkWindowObject *)window;
+  private->impl = g_object_new (GDK_TYPE_OFFSCREEN_WINDOW, NULL);
+  offscreen = GDK_OFFSCREEN_WINDOW (private->impl);
+  offscreen->wrapper = window;
+
+  private->parent = (GdkWindowObject *)parent;
+
+  screen = gdk_drawable_get_screen (parent);
+
+  offscreen->screen = screen;
+
+  if (attributes_mask & GDK_WA_X)
+    x = attributes->x;
+  else
+    x = 0;
+
+  if (attributes_mask & GDK_WA_Y)
+    y = attributes->y;
+  else
+    y = 0;
+
+  private->x = x;
+  private->y = y;
+  offscreen->width = (attributes->width > 1) ? (attributes->width) : (1);
+  offscreen->height = (attributes->height > 1) ? (attributes->height) : (1);
+
+  private->window_type = GDK_WINDOW_CHILD;
+
+  if (attributes_mask & GDK_WA_VISUAL)
+    visual = attributes->visual;
+  else
+    visual = gdk_screen_get_system_visual (screen);
+
+  private->event_mask = attributes->event_mask;
+
+  if (attributes->wclass == GDK_INPUT_OUTPUT)
+    {
+      private->input_only = FALSE;
+      private->depth = visual->depth;
+
+      if (attributes_mask & GDK_WA_COLORMAP)
+        offscreen->colormap = g_object_ref (attributes->colormap);
+      else
+	{
+	  if (gdk_screen_get_system_visual (screen) == visual)
+            {
+	      offscreen->colormap = gdk_screen_get_system_colormap (screen);
+              g_object_ref (offscreen->colormap);
+            }
+	  else
+            offscreen->colormap = gdk_colormap_new (visual, FALSE);
+	}
+
+      private->bg_color.red = private->bg_color.green = private->bg_color.blue = 0;
+      private->bg_color.pixel = 0;
+      private->bg_pixmap = NULL;
+    }
+  else
+    {
+      private->depth = 0;
+      private->input_only = TRUE;
+      offscreen->colormap = gdk_screen_get_system_colormap (screen);
+      g_object_ref (offscreen->colormap);
+    }
+
+  if (private->parent)
+    {
+      private->parent->children = g_list_prepend (private->parent->children, window);
+
+      if (toplevel)
+	_gdk_window_add_offscreen_child (GDK_WINDOW (private->parent));
+    }
+
+  /* Inherit redirection from parent */
+  private->redirect = private->parent->redirect;
+
+  if (toplevel)
+    {
+      GdkPixmap *pixmap;
+
+      offscreen->toplevel_offscreen = offscreen;
+      offscreen->abs_x = 0;
+      offscreen->abs_y = 0;
+      offscreen->llim_x = 0;
+      offscreen->llim_y = 0;
+      offscreen->lim_x = offscreen->width;
+      offscreen->lim_y = offscreen->height;
+
+      pixmap = gdk_pixmap_new (parent,
+			       offscreen->width,
+			       offscreen->height,
+			       private->depth);
+      gdk_window_redirect_to_drawable (window,
+				       pixmap, 0, 0, 0, 0,
+				       offscreen->width,
+				       offscreen->height);
+      g_object_unref (pixmap);
+    }
+  else
+    {
+      GdkOffscreenWindow *parent_offscreen = GDK_OFFSCREEN_WINDOW (parent_private->impl);
+
+      offscreen->toplevel_offscreen = parent_offscreen->toplevel_offscreen;
+      recompute_abs_positions (window,
+			       parent_offscreen->abs_x, parent_offscreen->abs_y,
+			       parent_offscreen->llim_x, parent_offscreen->llim_y,
+			       parent_offscreen->lim_x, parent_offscreen->lim_y);
+    }
+
+
+  return window;
+}
+
+static gboolean
+gdk_offscreen_window_reparent (GdkWindow *window,
+			       GdkWindow *new_parent,
+			       gint       x,
+			       gint       y)
+{
+  GdkWindowObject *private = (GdkWindowObject *)window;
+  GdkWindowObject *new_parent_private = (GdkWindowObject *)new_parent;
+  GdkOffscreenWindow *offscreen;
+  gboolean was_mapped;
+
+  /* Can't reparent offscreen window to toplevel */
+  if (!new_parent)
+    return FALSE;
+
+  if (!gdk_window_is_offscreen (new_parent))
+    {
+      g_warning ("Can't reparent offscreen window to native parent");
+      return FALSE;
+    }
+
+  /* No input-output children of input-only windows */
+  if (new_parent_private->input_only && !private->input_only)
+    return FALSE;
+
+  /* Don't create loops in hierarchy */
+  if (is_parent_of (window, new_parent))
+    return FALSE;
+
+  offscreen = GDK_OFFSCREEN_WINDOW (private->impl);
+
+  was_mapped = GDK_WINDOW_IS_MAPPED (window);
+
+  gdk_window_hide (window);
+
+  if (private->parent)
+    {
+      private->parent->children = g_list_remove (private->parent->children, window);
+      if (offscreen->toplevel_offscreen == offscreen)
+	_gdk_window_remove_offscreen_child (GDK_WINDOW (private->parent));
+    }
+
+  private->parent = new_parent_private;
+  private->x = x;
+  private->y = y;
+
+  private->parent->children = g_list_prepend (private->parent->children, window);
+  if (offscreen->toplevel_offscreen == offscreen)
+    _gdk_window_add_offscreen_child (GDK_WINDOW (private->parent));
+  else
+    {
+      GdkOffscreenWindow *parent_offscreen = GDK_OFFSCREEN_WINDOW (new_parent_private->impl);
+
+      offscreen->toplevel_offscreen = parent_offscreen->toplevel_offscreen;
+      recompute_abs_positions (window,
+			       parent_offscreen->abs_x, parent_offscreen->abs_y,
+			       parent_offscreen->llim_x, parent_offscreen->llim_y,
+			       parent_offscreen->lim_x, parent_offscreen->lim_y);
+    }
+
+  return was_mapped;
+}
+
+/**
+ * gdk_window_is_offscreen:
+ * @window: a #GdkWindow
+ *
+ * Returns: %TRUE if the window is an offscreen window
+ **/
+gboolean
+gdk_window_is_offscreen (GdkWindow *window)
+{
+  GdkWindowObject *private = (GdkWindowObject *)window;
+
+  g_return_val_if_fail (GDK_IS_WINDOW (window), FALSE);
+
+  return GDK_IS_OFFSCREEN_WINDOW (private->impl);
+}
+
+gboolean
+_gdk_window_is_toplevel_offscreen (GdkWindow *window)
+{
+  GdkWindowObject *private = (GdkWindowObject *)window;
+  GdkOffscreenWindow *offscreen;
+
+  g_return_val_if_fail (GDK_IS_WINDOW (window), FALSE);
+
+  if (!GDK_IS_OFFSCREEN_WINDOW (private->impl))
+    return FALSE;
+
+  offscreen = GDK_OFFSCREEN_WINDOW (private->impl);
+
+  return offscreen->toplevel_offscreen == offscreen;
+}
+
+
+/**
+ * gdk_window_get_offscreen_pixmap:
+ * @window: a #GdkWindow
+ *
+ * Gets the offscreen pixmap that an offscreen window renders into. If
+ * you need to keep this around over window resizes, you need to add a
+ * reference to it.
+ *
+ * Returns: The offscreen pixmap, or NULL if not offscreen
+ **/
+GdkPixmap *
+gdk_window_get_offscreen_pixmap (GdkWindow *window)
+{
+  GdkWindowObject *private = (GdkWindowObject *)window;
+
+  g_return_val_if_fail (GDK_IS_WINDOW (window), FALSE);
+
+  if (!GDK_IS_OFFSCREEN_WINDOW (private->impl))
+    return NULL;
+
+  return private->redirect->pixmap;
+}
+
+static void
+offscreen_visible_rect (GdkOffscreenWindow *offscreen,
+                        GdkRectangle       *rect)
+{
+  rect->x = offscreen->llim_x - offscreen->abs_x;
+  rect->y = offscreen->llim_y - offscreen->abs_y;
+  rect->width = offscreen->lim_x - offscreen->llim_x;
+  rect->height = offscreen->lim_y - offscreen->llim_y;
+}
+
+static GdkRegion *
+gdk_offscreen_window_get_visible_region (GdkDrawable *drawable)
+{
+  GdkOffscreenWindow *offscreen = GDK_OFFSCREEN_WINDOW (drawable);
+  GdkRectangle rect;
+
+  offscreen_visible_rect (offscreen, &rect);
+
+  return gdk_region_rectangle (&rect);
+}
+
+static void
+gdk_offscreen_window_invalidate_visible (GdkWindow *window)
+{
+  GdkRectangle r;
+
+  offscreen_visible_rect (GDK_OFFSCREEN_WINDOW (window), &r);
+  gdk_window_invalidate_rect (window, &r, TRUE);
+}
+
+static void
+gdk_offscreen_window_raise (GdkWindow *window)
+{
+  /* gdk_window_raise already changed the stacking order */
+  gdk_offscreen_window_invalidate_visible (window);
+}
+
+static void
+gdk_offscreen_window_invalidate_in_parent (GdkWindowObject *private)
+{
+  GdkRectangle r, child;
+  GdkOffscreenWindow *offscreen;
+
+  offscreen = GDK_OFFSCREEN_WINDOW (private->impl);
+
+  /* get the visible rectangle of the parent */
+  r.x = r.y = 0;
+  gdk_drawable_get_size (GDK_DRAWABLE (private->parent), &r.width, &r.height);
+
+  child.x = private->x;
+  child.y = private->y;
+  child.width = offscreen->width;
+  child.height = offscreen->height;
+
+  gdk_rectangle_intersect (&r, &child, &r);
+
+  gdk_window_invalidate_rect (GDK_WINDOW (private->parent), &r, TRUE);
+}
+
+
+static void
+gdk_offscreen_window_lower (GdkWindow *window)
+{
+  GdkWindowObject *private = (GdkWindowObject *)window;
+  GdkOffscreenWindow *offscreen;
+
+  offscreen = GDK_OFFSCREEN_WINDOW (private->impl);
+
+  /* gdk_window_lower already changed the stacking order */
+
+  /* Invalidate the rect */
+  if (offscreen->toplevel_offscreen != offscreen)
+    gdk_offscreen_window_invalidate_in_parent (private);
+}
+
+static void
+send_pointer_events_for_geometry_change (GdkWindow *window)
+{
+  GdkWindow *mousewin, *event_window;
+  GdkWindowOffscreenChildren *offscreen_children;
+
+  event_window = _gdk_window_get_offscreen_event_window (window);
+  offscreen_children = GDK_WINDOW_OBJECT (event_window)->offscreen_children;
+  /* The window the pointer is in might have changed */
+  if (offscreen_children->window_under_pointer != NULL)
+    {
+      mousewin = _gdk_window_get_offscreen_window_child_at (event_window,
+							    offscreen_children->last_mouse_x,
+							    offscreen_children->last_mouse_y,
+							    NULL, NULL);
+      send_crossing_events (offscreen_children,
+			    event_window,
+			    offscreen_children->window_under_pointer, mousewin,
+			    GDK_CROSSING_NORMAL,
+			    offscreen_children->last_mouse_x, offscreen_children->last_mouse_y,
+			    offscreen_children->last_state, GDK_CURRENT_TIME,
+			    TRUE, TRUE);
+    }
+}
+
+static void
+gdk_offscreen_window_move_region (GdkWindow       *window,
+                                  const GdkRegion *region,
+                                  gint             dx,
+                                  gint             dy)
+{
+  GdkRegion *dest_region;
+
+  dest_region = gdk_region_copy (region);
+  gdk_region_offset (dest_region, dx, dy);
+
+  /* Really cheesy implementation that just exposes both regions */
+  gdk_window_invalidate_region (window, region, FALSE);
+  gdk_window_invalidate_region (window, dest_region, FALSE);
+
+}
+
+static void
+gdk_offscreen_window_scroll (GdkWindow *window,
+			     gint       dx,
+			     gint       dy)
+{
+  GdkWindowObject *obj = GDK_WINDOW_OBJECT (window);
+  GList *tmp_list;
+
+  /* Move the current invalid region */
+  if (obj->update_area)
+    gdk_region_offset (obj->update_area, dx, dy);
+
+/* We currently scroll in a pretty lame way, we move all
+   the subwindows, then re-expose the whole window */
+
+  tmp_list = obj->children;
+  while (tmp_list)
+    {
+      GdkWindow *child = GDK_WINDOW (tmp_list->data);
+      GdkWindowObject *child_obj = GDK_WINDOW_OBJECT (child);
+
+      child_obj->x += dx;
+      child_obj->y += dx;
+      recompute_window (child);
+
+      tmp_list = tmp_list->next;
+    }
+
+  gdk_window_invalidate_rect (window, NULL, TRUE);
+  send_pointer_events_for_geometry_change (window);
+}
+
+static void
+gdk_offscreen_window_move_resize_internal (GdkWindow *window,
+					   gint       x,
+					   gint       y,
+					   gint       width,
+					   gint       height,
+					   gboolean   send_expose_events)
+{
+  GdkWindowObject *private = (GdkWindowObject *)window;
+  GdkOffscreenWindow *offscreen, *toplevel;
+  gint dx, dy, dw, dh;
+  GdkRegion *old_region;
+
+  offscreen = GDK_OFFSCREEN_WINDOW (private->impl);
+  toplevel = offscreen->toplevel_offscreen;
+
+  if (width < 1)
+    width = 1;
+  if (height < 1)
+    height = 1;
+
+  if (private->destroyed)
+    return;
+
+  if (private->input_only)
+    send_expose_events = FALSE;
+
+  old_region = NULL;
+  if (GDK_WINDOW_IS_MAPPED (private) && send_expose_events)
+    {
+      old_region = _gdk_window_calculate_full_clip_region (window,
+							   toplevel->wrapper,
+							   NULL, FALSE,
+							   NULL, NULL);
+      /* Adjust region to parent window coords */
+      gdk_region_offset (old_region, private->x, private->y);
+    }
+
+  dx = x - private->x;
+  dy = y - private->y;
+  dw = width - offscreen->width;
+  dh = height - offscreen->height;
+
+  private->x = x;
+  private->y = y;
+  offscreen->width = width;
+  offscreen->height = height;
+
+  if (GDK_WINDOW_IS_MAPPED (private))
+    {
+      recompute_window (window);
+
+      if (send_expose_events)
+	{
+	  if (offscreen == toplevel)
+	    {
+	      if (dw != 0 || dh != 0)
+		{
+		  GdkGC *gc;
+		  GdkPixmap *old_pixmap;
+
+		  old_pixmap = private->redirect->pixmap;
+		  private->redirect->pixmap = gdk_pixmap_new (GDK_DRAWABLE (old_pixmap),
+							      offscreen->width,
+							      offscreen->height,
+							      private->depth);
+		  private->redirect->width = offscreen->width;
+		  private->redirect->height = offscreen->height;
+
+		  gc = _gdk_drawable_get_scratch_gc (private->redirect->pixmap, FALSE);
+		  gdk_draw_drawable (private->redirect->pixmap,
+				     gc,
+				     old_pixmap,
+				     0,0, 0, 0,
+				     -1, -1);
+		  g_object_unref (old_pixmap);
+
+		}
+	      gdk_window_invalidate_rect (window, NULL, TRUE);
+	    }
+	  else
+	    {
+	      GdkRegion *new_region;
+
+	      new_region = _gdk_window_calculate_full_clip_region (window,
+								   toplevel->wrapper,
+								   NULL, FALSE,
+								   NULL, NULL);
+	      /* Adjust region to parent window coords */
+	      gdk_region_offset (new_region, private->x, private->y);
+	      gdk_region_union (new_region, old_region);
+
+	      /* Here we could optimize by copying areas that overlaps both
+	       * new_region and old_region, but we just expose everything instead */
+
+	      /* Invalidate regions in new_region */
+	      gdk_window_invalidate_region (GDK_WINDOW (private->parent), new_region, TRUE);
+
+	      gdk_region_destroy (new_region);
+	    }
+	}
+
+      send_pointer_events_for_geometry_change (window);
+    }
+
+  if (old_region != NULL)
+    gdk_region_destroy (old_region);
+}
+
+static void
+gdk_offscreen_window_move_resize (GdkWindow *window,
+                                  gboolean   with_move,
+				  gint       x,
+                                  gint       y,
+				  gint       width,
+                                  gint       height)
+{
+  GdkWindowObject *private = (GdkWindowObject *)window;
+  GdkOffscreenWindow *offscreen;
+
+  offscreen = GDK_OFFSCREEN_WINDOW (private->impl);
+
+  if (!with_move)
+    {
+      x = private->x;
+      y = private->y;
+    }
+
+  if (width < 0)
+    width = offscreen->width;
+
+  if (height < 0)
+    height = offscreen->height;
+
+  gdk_offscreen_window_move_resize_internal (window, x, y,
+					     width, height,
+					     TRUE);
+}
+
+static gboolean
+all_parents_shown (GdkWindowObject *private)
+{
+  private = (GdkWindowObject *)private->parent;
+
+  while (private != NULL &&
+	 GDK_WINDOW_IS_MAPPED (private) &&
+	 GDK_IS_OFFSCREEN_WINDOW (private->impl))
+    {
+      private = (GdkWindowObject *)private->parent;
+    }
+
+  return private == NULL || !GDK_IS_OFFSCREEN_WINDOW (private->impl);
+}
+
+static const guint type_masks[] = {
+  GDK_SUBSTRUCTURE_MASK, /* GDK_DELETE                 = 0  */
+  GDK_STRUCTURE_MASK, /* GDK_DESTROY                   = 1  */
+  GDK_EXPOSURE_MASK, /* GDK_EXPOSE                     = 2  */
+  GDK_POINTER_MOTION_MASK, /* GDK_MOTION_NOTIFY        = 3  */
+  GDK_BUTTON_PRESS_MASK, /* GDK_BUTTON_PRESS           = 4  */
+  GDK_BUTTON_PRESS_MASK, /* GDK_2BUTTON_PRESS          = 5  */
+  GDK_BUTTON_PRESS_MASK, /* GDK_3BUTTON_PRESS          = 6  */
+  GDK_BUTTON_RELEASE_MASK, /* GDK_BUTTON_RELEASE       = 7  */
+  GDK_KEY_PRESS_MASK, /* GDK_KEY_PRESS                 = 8  */
+  GDK_KEY_RELEASE_MASK, /* GDK_KEY_RELEASE             = 9  */
+  GDK_ENTER_NOTIFY_MASK, /* GDK_ENTER_NOTIFY           = 10 */
+  GDK_LEAVE_NOTIFY_MASK, /* GDK_LEAVE_NOTIFY           = 11 */
+  GDK_FOCUS_CHANGE_MASK, /* GDK_FOCUS_CHANGE           = 12 */
+  GDK_STRUCTURE_MASK, /* GDK_CONFIGURE                 = 13 */
+  GDK_VISIBILITY_NOTIFY_MASK, /* GDK_MAP               = 14 */
+  GDK_VISIBILITY_NOTIFY_MASK, /* GDK_UNMAP             = 15 */
+  GDK_PROPERTY_CHANGE_MASK, /* GDK_PROPERTY_NOTIFY     = 16 */
+  GDK_PROPERTY_CHANGE_MASK, /* GDK_SELECTION_CLEAR     = 17 */
+  GDK_PROPERTY_CHANGE_MASK, /* GDK_SELECTION_REQUEST   = 18 */
+  GDK_PROPERTY_CHANGE_MASK, /* GDK_SELECTION_NOTIFY    = 19 */
+  GDK_PROXIMITY_IN_MASK, /* GDK_PROXIMITY_IN           = 20 */
+  GDK_PROXIMITY_OUT_MASK, /* GDK_PROXIMITY_OUT         = 21 */
+  GDK_ALL_EVENTS_MASK, /* GDK_DRAG_ENTER               = 22 */
+  GDK_ALL_EVENTS_MASK, /* GDK_DRAG_LEAVE               = 23 */
+  GDK_ALL_EVENTS_MASK, /* GDK_DRAG_MOTION              = 24 */
+  GDK_ALL_EVENTS_MASK, /* GDK_DRAG_STATUS              = 25 */
+  GDK_ALL_EVENTS_MASK, /* GDK_DROP_START               = 26 */
+  GDK_ALL_EVENTS_MASK, /* GDK_DROP_FINISHED            = 27 */
+  GDK_ALL_EVENTS_MASK, /* GDK_CLIENT_EVENT	       = 28 */
+  GDK_VISIBILITY_NOTIFY_MASK, /* GDK_VISIBILITY_NOTIFY = 29 */
+  GDK_EXPOSURE_MASK, /* GDK_NO_EXPOSE                  = 30 */
+  GDK_SCROLL_MASK | GDK_BUTTON_PRESS_MASK /* GDK_SCROLL= 31 */
+  /* Later additions: */
+  /* GDK_WINDOW_STATE      = 32 */
+  /* GDK_SETTING           = 33 */
+  /* GDK_OWNER_CHANGE      = 34 */
+  /* GDK_GRAB_BROKEN       = 35 */
+};
+
+static GdkWindow *
+get_target_window_for_event (GdkWindow    *window,
+			     GdkEventType  type,
+			     gboolean      recurse)
+{
+  guint32 evmask;
+  GdkWindow *w;
+  gboolean was_offscreen;
+
+  w = window;
+  while (w != NULL && GDK_WINDOW_OBJECT (w)->window_type != GDK_WINDOW_ROOT)
+    {
+      evmask = GDK_WINDOW_OBJECT (w)->event_mask;
+
+      if (evmask & type_masks[type])
+	return w;
+
+      if (!recurse)
+	break;
+
+      /* skip over parent of toplevel, to avoid event recursion */
+      was_offscreen = gdk_window_is_offscreen (w);
+      w = gdk_window_get_parent (w);
+      if (was_offscreen && !gdk_window_is_offscreen (w))
+	w = gdk_window_get_parent (w);
+    }
+
+  return NULL;
+}
+
+/* send motion events if the right buttons are down */
+static guint
+update_evmask_for_button_motion (guint           evmask,
+                                 GdkModifierType mask)
+{
+  if ((evmask & GDK_BUTTON_MOTION_MASK) &&
+      (mask & (GDK_BUTTON1_MASK |
+	       GDK_BUTTON2_MASK |
+	       GDK_BUTTON3_MASK |
+	       GDK_BUTTON4_MASK |
+	       GDK_BUTTON5_MASK)))
+    evmask |= GDK_POINTER_MOTION_MASK;
+
+  if (((evmask & GDK_BUTTON1_MOTION_MASK) && (mask & GDK_BUTTON1_MASK)) ||
+      ((evmask & GDK_BUTTON2_MOTION_MASK) && (mask & GDK_BUTTON2_MASK)) ||
+      ((evmask & GDK_BUTTON3_MOTION_MASK) && (mask & GDK_BUTTON3_MASK)))
+    evmask |= GDK_POINTER_MOTION_MASK;
+
+  return evmask;
+}
+
+static GdkWindow *
+get_target_window_for_pointer_event (GdkWindowOffscreenChildren *offscreen_children,
+                                     GdkWindow                  *window,
+                                     GdkEventType                type,
+                                     GdkModifierType             mask)
+{
+  guint evmask;
+  gboolean crossing_event;
+  GdkWindow *w, *grab_window;
+
+  /* crossing events don't propagate up like other types of events */
+  crossing_event = (type == GDK_ENTER_NOTIFY || type == GDK_LEAVE_NOTIFY);
+
+  if ((offscreen_children->pointer_grab_window != NULL &&
+       !offscreen_children->pointer_grab_owner_events) ||
+      (type == GDK_BUTTON_RELEASE &&
+       offscreen_children->grab_one_pointer_release_event))
+    {
+      evmask = offscreen_children->pointer_grab_event_mask;
+      evmask = update_evmask_for_button_motion (evmask, mask);
+
+      if (type == GDK_BUTTON_RELEASE &&
+	  offscreen_children->grab_one_pointer_release_event)
+	{
+	  grab_window = offscreen_children->grab_one_pointer_release_event;
+	  offscreen_children->grab_one_pointer_release_event = NULL;
+	}
+      else
+	grab_window = offscreen_children->pointer_grab_window;
+
+      if ((evmask & type_masks[type]) &&
+          (!crossing_event || window == grab_window))
+	return grab_window;
+      else
+	return NULL;
+    }
+
+  w = window;
+  while (w != NULL)
+    {
+      evmask = GDK_WINDOW_OBJECT(window)->event_mask;
+      evmask = update_evmask_for_button_motion (evmask, mask);
+
+      if (evmask & type_masks[type])
+	return w;
+
+      if (crossing_event)
+	break;
+
+      w = gdk_window_get_parent (w);
+    }
+
+  if (offscreen_children->pointer_grab_window != NULL &&
+      offscreen_children->pointer_grab_owner_events)
+    {
+      evmask = offscreen_children->pointer_grab_event_mask;
+      evmask = update_evmask_for_button_motion (evmask, mask);
+
+      if ((evmask & type_masks[type]) &&
+	  (!crossing_event || window == offscreen_children->pointer_grab_window))
+	return offscreen_children->pointer_grab_window;
+      else
+	return NULL;
+    }
+
+  return NULL;
+}
+
+static GdkEvent *
+make_gdk_event (GdkWindow    *window,
+		GdkEventType  type,
+		GdkEvent     *append_after)
+{
+  GdkEvent *event = gdk_event_new (type);
+  guint32 the_time;
+  GdkModifierType the_state;
+
+  the_time = gdk_event_get_time (append_after);
+  gdk_event_get_state (append_after, &the_state);
+
+  event->any.window = g_object_ref (window);
+  event->any.send_event = FALSE;
+
+  switch (type)
+    {
+    case GDK_MOTION_NOTIFY:
+      event->motion.time = the_time;
+      event->motion.axes = NULL;
+      event->motion.state = the_state;
+      break;
+
+    case GDK_BUTTON_PRESS:
+    case GDK_2BUTTON_PRESS:
+    case GDK_3BUTTON_PRESS:
+    case GDK_BUTTON_RELEASE:
+      event->button.time = the_time;
+      event->button.axes = NULL;
+      event->button.state = the_state;
+      break;
+
+    case GDK_SCROLL:
+      event->scroll.time = the_time;
+      event->scroll.state = the_state;
+      break;
+
+    case GDK_KEY_PRESS:
+    case GDK_KEY_RELEASE:
+      event->key.time = the_time;
+      event->key.state = the_state;
+      break;
+
+    case GDK_ENTER_NOTIFY:
+    case GDK_LEAVE_NOTIFY:
+      event->crossing.time = the_time;
+      event->crossing.state = the_state;
+      break;
+
+    case GDK_PROPERTY_NOTIFY:
+      event->property.time = the_time;
+      event->property.state = the_state;
+      break;
+
+    case GDK_SELECTION_CLEAR:
+    case GDK_SELECTION_REQUEST:
+    case GDK_SELECTION_NOTIFY:
+      event->selection.time = the_time;
+      break;
+
+    case GDK_PROXIMITY_IN:
+    case GDK_PROXIMITY_OUT:
+      event->proximity.time = the_time;
+      break;
+
+    case GDK_DRAG_ENTER:
+    case GDK_DRAG_LEAVE:
+    case GDK_DRAG_MOTION:
+    case GDK_DRAG_STATUS:
+    case GDK_DROP_START:
+    case GDK_DROP_FINISHED:
+      event->dnd.time = the_time;
+      break;
+
+    case GDK_FOCUS_CHANGE:
+    case GDK_CONFIGURE:
+    case GDK_MAP:
+    case GDK_UNMAP:
+    case GDK_CLIENT_EVENT:
+    case GDK_VISIBILITY_NOTIFY:
+    case GDK_NO_EXPOSE:
+    case GDK_DELETE:
+    case GDK_DESTROY:
+    case GDK_EXPOSE:
+    default:
+      break;
+    }
+
+  if (append_after)
+    _gdk_event_queue_insert_after (gdk_drawable_get_display (window), append_after, event);
+  else
+    _gdk_event_queue_append (gdk_drawable_get_display (window), event);
+
+  return event;
+}
+
+static void
+send_map_events (GdkWindowObject *private)
+{
+  GList *l;
+  GdkWindowObject *parent_private = private->parent;
+  GdkWindow *event_win;
+  GdkOffscreenWindow *offscreen, *parent_offscreen;
+
+  if (!GDK_WINDOW_IS_MAPPED (private))
+    return;
+
+  event_win = get_target_window_for_event ((GdkWindow *)private, GDK_MAP, FALSE);
+  if (event_win)
+    make_gdk_event (event_win, GDK_MAP, NULL);
+
+  if (private->input_only)
+    return;
+
+  if (!parent_private)
+    parent_private = private;
+
+  offscreen = GDK_OFFSCREEN_WINDOW (private->impl);
+  if (GDK_IS_OFFSCREEN_WINDOW (parent_private->impl))
+    {
+      parent_offscreen = GDK_OFFSCREEN_WINDOW (parent_private->impl);
+
+      if (((offscreen->abs_x > parent_offscreen->lim_x) ||
+	   (offscreen->abs_y > parent_offscreen->lim_y) ||
+	   (offscreen->lim_x < parent_offscreen->llim_x) ||
+	   (offscreen->lim_y < parent_offscreen->llim_y)))
+	return;
+    }
+
+  gdk_window_clear ((GdkWindow *)private);
+
+  for (l = private->children; l; l = l->next)
+    send_map_events (l->data);
+}
+
+static void
+gdk_offscreen_window_show (GdkWindow *window, gboolean raise)
+{
+  GdkWindowObject *private = (GdkWindowObject *)window;
+  GdkOffscreenWindow *offscreen;
+
+  if (GDK_WINDOW_IS_MAPPED (window))
+    return;
+
+  offscreen = GDK_OFFSCREEN_WINDOW (private->impl);
+
+  private->state = 0;
+
+  /* gdk_window_show already changed the stacking order if needed */
+
+  if (all_parents_shown (private))
+    {
+      recompute_window (window);
+
+      send_map_events (private);
+      send_pointer_events_for_geometry_change (window);
+
+      if (private->input_only)
+	return;
+
+      gdk_window_clear_area_e (window, 0, 0,
+			       offscreen->width, offscreen->height);
+    }
+}
+
+
+static void
+gdk_offscreen_window_hide (GdkWindow *window)
+{
+  GdkWindowObject *private;
+  GdkWindow *event_win;
+  GdkOffscreenWindow *offscreen;
+  GdkWindow *grab_window;
+
+  g_return_if_fail (window != NULL);
+
+  private = (GdkWindowObject*) window;
+  offscreen = GDK_OFFSCREEN_WINDOW (private->impl);
+
+  if (GDK_WINDOW_IS_MAPPED (private))
+    {
+      GdkEvent *event;
+      GdkRectangle r;
+      GdkDisplay *display;
+
+      /* May need to break grabs on children */
+      display = gdk_drawable_get_display (window);
+      if (gdk_pointer_grab_info_libgtk_only (display, &grab_window, NULL))
+	{
+	  if (is_parent_of (window, grab_window))
+	    {
+	      _gdk_offscreen_window_unset_pointer_grab (grab_window,
+                                                        FALSE, FALSE,
+                                                        GDK_CURRENT_TIME);
+	      gdk_display_pointer_ungrab (display, GDK_CURRENT_TIME);
+	      _gdk_windowing_grab_broken (display);
+	    }
+	}
+
+      event_win = get_target_window_for_event (window, GDK_UNMAP, FALSE);
+      if (event_win)
+	event = make_gdk_event (event_win, GDK_UNMAP, NULL);
+
+      r.x = offscreen->llim_x;
+      r.y = offscreen->llim_y;
+      r.width = offscreen->lim_x - r.x;
+      r.height = offscreen->lim_y - r.y;
+
+      private->state = GDK_WINDOW_STATE_WITHDRAWN;
+
+      send_pointer_events_for_geometry_change (window);
+
+      /* Invalidate the rect */
+      if (offscreen->toplevel_offscreen != offscreen)
+	gdk_offscreen_window_invalidate_in_parent (private);
+    }
+}
+
+static void
+gdk_offscreen_window_withdraw (GdkWindow *window)
+{
+}
+
+static GdkEventMask
+gdk_offscreen_window_get_events (GdkWindow *window)
+{
+  return GDK_WINDOW_OBJECT (window)->event_mask;
+}
+
+static void
+gdk_offscreen_window_set_events (GdkWindow       *window,
+				 GdkEventMask     event_mask)
+{
+  GDK_WINDOW_OBJECT (window)->event_mask = event_mask;
+}
+
+
+static void
+gdk_offscreen_window_clear_area (GdkWindow *window,
+				 gint       x,
+				 gint       y,
+				 gint       width,
+				 gint       height,
+				 gboolean   send_expose)
+{
+  if (GDK_WINDOW_DESTROYED (window))
+    return;
+
+  /* Actual drawing is done by gdkwindow.c */
+
+  if (send_expose)
+    {
+      GdkRectangle visible, rect;
+
+      visible.x = visible.y = 0;
+      gdk_drawable_get_size (GDK_DRAWABLE (window), &visible.width, &visible.height);
+
+      rect.x = x;
+      rect.y = x;
+      rect.width = width;
+      rect.height = height;
+
+      gdk_rectangle_intersect (&rect, &visible, &rect);
+
+      gdk_window_invalidate_rect (window, &rect, TRUE);
+    }
+}
+
+static void
+gdk_offscreen_window_set_background (GdkWindow      *window,
+				     const GdkColor *color)
+{
+  GdkWindowObject *private = (GdkWindowObject *)window;
+  GdkColormap *colormap = gdk_drawable_get_colormap (window);
+
+  private->bg_color = *color;
+  gdk_colormap_query_color (colormap, private->bg_color.pixel, &private->bg_color);
+
+  if (private->bg_pixmap &&
+      private->bg_pixmap != GDK_PARENT_RELATIVE_BG &&
+      private->bg_pixmap != GDK_NO_BG)
+    g_object_unref (private->bg_pixmap);
+
+  private->bg_pixmap = NULL;
+}
+
+static void
+gdk_offscreen_window_set_back_pixmap (GdkWindow *window,
+				      GdkPixmap *pixmap,
+				      gboolean   parent_relative)
+{
+  GdkWindowObject *private = (GdkWindowObject *)window;
+
+  g_return_if_fail (GDK_IS_WINDOW (window));
+  g_return_if_fail (pixmap == NULL || !parent_relative);
+  g_return_if_fail (pixmap == NULL || gdk_drawable_get_depth (window) == gdk_drawable_get_depth (pixmap));
+
+  if (pixmap && !gdk_drawable_get_colormap (pixmap))
+    {
+      g_warning ("gdk_window_set_back_pixmap(): pixmap must have a colormap");
+      return;
+    }
+
+  if (private->bg_pixmap &&
+      private->bg_pixmap != GDK_PARENT_RELATIVE_BG &&
+      private->bg_pixmap != GDK_NO_BG)
+    g_object_unref (private->bg_pixmap);
+
+  if (parent_relative)
+    private->bg_pixmap = GDK_PARENT_RELATIVE_BG;
+  else
+    {
+      if (pixmap)
+	{
+	  g_object_ref (pixmap);
+	  private->bg_pixmap = pixmap;
+	}
+      else
+	private->bg_pixmap = GDK_NO_BG;
+    }
+}
+
+static void
+gdk_offscreen_window_get_offsets (GdkWindow *window,
+				  gint      *x_offset,
+				  gint      *y_offset)
+{
+  *x_offset = 0;
+  *y_offset = 0;
+}
+
+static GdkWindow *
+gdk_offscreen_window_get_window_at (GdkWindow *window,
+				    gdouble    x,
+                                    gdouble    y,
+				    gdouble   *win_x,
+                                    gdouble   *win_y)
+{
+  GdkWindowObject *private = (GdkWindowObject *)window;
+  GdkOffscreenWindow *offscreen;
+  GdkWindow *return_val;
+
+  offscreen = GDK_OFFSCREEN_WINDOW (private->impl);
+
+  return_val = NULL;
+
+  if ((x >= 0) && (x < offscreen->width) &&
+      (y >= 0) && (y < offscreen->height))
+    {
+      GdkWindowObject *sub;
+      GdkOffscreenWindow *sub_offscreen;
+      gdouble subx = x, suby = y;
+
+      for (sub = private; sub; private = sub)
+	{
+	  GList *ltmp;
+
+	  /* This assumes children is ordered in reverse stack order */
+	  for (ltmp = private->children; ltmp; ltmp = ltmp->next)
+	    {
+	      sub = ltmp->data;
+
+              if (!GDK_WINDOW_IS_MAPPED (sub))
+                continue;
+
+	      sub_offscreen = GDK_OFFSCREEN_WINDOW (sub->impl);
+
+	      if (subx >= sub->x &&
+		  (subx < (sub_offscreen->width + sub->x)) &&
+		  (suby >= sub->y) &&
+		  (suby < (sub_offscreen->height + sub->y)))
+		{
+		  subx -= sub->x;
+		  suby -= sub->y;
+		  break;
+		}
+	    }
+
+	  if (!ltmp)
+	    {
+	      sub = NULL;
+	      break;
+	    }
+	}
+
+      if (win_x)
+	*win_x = subx;
+      if (win_y)
+	*win_y = suby;
+
+      return_val = (GdkWindow *)private;
+    }
+
+  return return_val;
+}
+
+GdkWindow *
+_gdk_window_get_offscreen_window_child_at (GdkWindow *parent,
+					   gdouble    x,
+					   gdouble    y,
+					   gdouble   *win_x_out,
+					   gdouble   *win_y_out)
+{
+  GdkWindow *target;
+  GdkWindow *child;
+  GdkWindowObject *parent_private;
+  gdouble win_x, win_y;
+
+  parent_private = GDK_WINDOW_OBJECT (parent);
+  if (parent_private && parent_private->offscreen_children)
+    {
+      child = find_offscreen_child (parent, x, y);
+      if (child)
+	{
+	  parent_to_window (parent, child, x, y, &win_x, &win_y);
+	  target = gdk_offscreen_window_get_window_at (child,
+                                                       win_x, win_y,
+                                                       &win_x, &win_y);
+	  return _gdk_window_get_offscreen_window_child_at (target,
+                                                            win_x, win_y,
+                                                            win_x_out, win_y_out);
+	}
+    }
+
+  if (win_x_out)
+    *win_x_out = x;
+  if (win_y_out)
+    *win_y_out = y;
+
+  return parent;
+}
+
+static GdkWindow *
+find_common_ancestor (GdkWindow *win1,
+		      GdkWindow *win2)
+{
+  GdkWindowObject *tmp;
+  GList *path1 = NULL, *path2 = NULL;
+  GList *list1, *list2;
+
+  tmp = GDK_WINDOW_OBJECT (win1);
+  while (tmp)
+    {
+      path1 = g_list_prepend (path1, tmp);
+      tmp = tmp->parent;
+    }
+
+  tmp = GDK_WINDOW_OBJECT (win2);
+  while (tmp)
+    {
+      path2 = g_list_prepend (path2, tmp);
+      tmp = tmp->parent;
+    }
+
+  list1 = path1;
+  list2 = path2;
+  tmp = NULL;
+  while (list1 && list2 && (list1->data == list2->data))
+    {
+      tmp = (GdkWindowObject *)list1->data;
+      list1 = g_list_next (list1);
+      list2 = g_list_next (list2);
+    }
+  g_list_free (path1);
+  g_list_free (path2);
+
+  return GDK_WINDOW (tmp);
+}
+
+
+/* x, y are in toplevel coords */
+static void
+send_crossing_events (GdkWindowOffscreenChildren *offscreen_children,
+                      GdkWindow                  *event_window,
+                      GdkWindow                  *src,
+                      GdkWindow                  *dest,
+                      GdkCrossingMode             mode,
+                      gint                        event_x,
+                      gint                        event_y,
+                      GdkModifierType             mask,
+                      guint32                     time_,
+                      gboolean                    do_first,
+                      gboolean                    do_last)
+{
+  GdkWindow *c;
+  GdkWindow *win, *last, *next;
+  GdkEvent *event;
+  GList *path, *list;
+  gboolean non_linear;
+  GdkWindow *a;
+  GdkWindow *b;
+  GdkWindow *event_win;
+
+  if ((mode == GDK_CROSSING_NORMAL) &&
+      (dest == offscreen_children->window_under_pointer))
+    return;
+
+  if (src)
+    a = src;
+  else
+    a = event_window;
+
+  if (dest)
+    b = dest;
+  else
+    b = event_window;
+
+  if (a == b)
+    return;
+
+  c = find_common_ancestor (a, b);
+  if (c == NULL)
+    return;
+
+  non_linear = (c != a) && (c != b);
+
+  event_win = get_target_window_for_pointer_event (offscreen_children, a, GDK_LEAVE_NOTIFY, mask);
+  if (do_first && event_win)
+    {
+      event = make_gdk_event (event_win, GDK_LEAVE_NOTIFY, NULL);
+      event->crossing.time = time_;
+      event->crossing.subwindow = NULL;
+      convert_event_coords_to_window (event_window,
+				      event_win, event_x, event_y,
+				      &event->crossing.x, &event->crossing.y);
+      event->crossing.x_root = event_x;
+      event->crossing.y_root = event_y;
+      event->crossing.mode = mode;
+      if (non_linear)
+	event->crossing.detail = GDK_NOTIFY_NONLINEAR;
+      else if (c == a)
+	event->crossing.detail = GDK_NOTIFY_INFERIOR;
+      else
+	event->crossing.detail = GDK_NOTIFY_ANCESTOR;
+      event->crossing.focus = FALSE;
+      event->crossing.state = mask;
+    }
+
+  /* Traverse up from a to (excluding) c */
+  if (c != a)
+    {
+      last = a;
+      win = GDK_WINDOW (GDK_WINDOW_OBJECT (a)->parent);
+      while (win != c)
+	{
+	  event_win = get_target_window_for_pointer_event (offscreen_children, win, GDK_LEAVE_NOTIFY, mask);
+	  if (event_win)
+	    {
+	      event = make_gdk_event (event_win, GDK_LEAVE_NOTIFY, NULL);
+	      event->crossing.time = time_;
+	      event->crossing.subwindow = g_object_ref (last);
+	      convert_event_coords_to_window (event_window,
+					      event_win, event_x, event_y,
+					      &event->crossing.x, &event->crossing.y);
+	      event->crossing.x_root = event_x;
+	      event->crossing.y_root = event_y;
+	      event->crossing.mode = mode;
+	      if (non_linear)
+		event->crossing.detail = GDK_NOTIFY_NONLINEAR_VIRTUAL;
+	      else
+		event->crossing.detail = GDK_NOTIFY_VIRTUAL;
+	      event->crossing.focus = FALSE;
+	      event->crossing.state = mask;
+	    }
+	  last = win;
+	  win = GDK_WINDOW (GDK_WINDOW_OBJECT (win)->parent);
+	}
+    }
+
+  /* Traverse down from c to b */
+  if (c != b)
+    {
+      path = NULL;
+      win = GDK_WINDOW (GDK_WINDOW_OBJECT (b)->parent);
+      while (win != c)
+	{
+	  path = g_list_prepend (path, win);
+	  win = GDK_WINDOW( GDK_WINDOW_OBJECT (win)->parent);
+	}
+
+      list = path;
+      while (list)
+	{
+	  win = (GdkWindow *)list->data;
+	  list = g_list_next (list);
+	  if (list)
+	    next = (GdkWindow *)list->data;
+	  else
+	    next = b;
+
+	  event_win = get_target_window_for_pointer_event (offscreen_children, win, GDK_ENTER_NOTIFY, mask);
+	  if (event_win)
+	    {
+	      event = make_gdk_event (event_win, GDK_ENTER_NOTIFY, NULL);
+	      event->crossing.time = time_;
+	      event->crossing.subwindow = g_object_ref (next);
+	      convert_event_coords_to_window (event_window,
+					      event_win, event_x, event_y,
+					      &event->crossing.x, &event->crossing.y);
+	      event->crossing.x_root = event_x;
+	      event->crossing.y_root = event_y;
+	      event->crossing.mode = mode;
+	      if (non_linear)
+		event->crossing.detail = GDK_NOTIFY_NONLINEAR_VIRTUAL;
+	      else
+		event->crossing.detail = GDK_NOTIFY_VIRTUAL;
+	      event->crossing.focus = FALSE;
+	      event->crossing.state = mask;
+	    }
+	}
+      g_list_free (path);
+    }
+
+  event_win = get_target_window_for_pointer_event (offscreen_children, b, GDK_ENTER_NOTIFY, mask);
+  if (do_last && event_win)
+    {
+      event = make_gdk_event (event_win, GDK_ENTER_NOTIFY, NULL);
+      event->crossing.time = time_;
+      event->crossing.subwindow = NULL;
+      convert_event_coords_to_window (event_window,
+				      event_win, event_x, event_y,
+				      &event->crossing.x, &event->crossing.y);
+      event->crossing.x_root = event_x;
+      event->crossing.y_root = event_y;
+      event->crossing.mode = mode;
+      if (non_linear)
+	event->crossing.detail = GDK_NOTIFY_NONLINEAR;
+      else if (c == a)
+	event->crossing.detail = GDK_NOTIFY_ANCESTOR;
+      else
+	event->crossing.detail = GDK_NOTIFY_INFERIOR;
+      event->crossing.focus = FALSE;
+      event->crossing.state = mask;
+    }
+
+  if (mode != GDK_CROSSING_GRAB &&
+      mode != GDK_CROSSING_UNGRAB &&
+      dest != offscreen_children->window_under_pointer)
+    {
+      if (offscreen_children->window_under_pointer)
+	g_object_unref (offscreen_children->window_under_pointer);
+      offscreen_children->window_under_pointer = NULL;
+      if (dest)
+	offscreen_children->window_under_pointer = g_object_ref (dest);
+    }
+}
+
+/* Find the first enclosing non-offscreen window.
+ * This window is the one used to get events to the
+ * offscreen windows
+ */
+GdkWindow *
+_gdk_window_get_offscreen_event_window (GdkWindow *window)
+{
+  GdkWindowObject *private;
+  GdkOffscreenWindow *offscreen;
+  GdkWindow *toplevel;
+
+  while (window != NULL && gdk_window_is_offscreen (window))
+    {
+      private = (GdkWindowObject *)window;
+      offscreen = GDK_OFFSCREEN_WINDOW (private->impl);
+      toplevel = offscreen->toplevel_offscreen->wrapper;
+      window = GDK_WINDOW (GDK_WINDOW_OBJECT (toplevel)->parent);
+    }
+
+  return window;
+}
+
+static void
+convert_event_coords_to_window (GdkWindow *event_window,
+				GdkWindow *window,
+				gdouble    event_x,
+                                gdouble    event_y,
+				gdouble   *window_x,
+                                gdouble   *window_y)
+{
+  if (event_window != window)
+    {
+      GdkWindow *parent = GDK_WINDOW (GDK_WINDOW_OBJECT (window)->parent);
+
+      if (parent != event_window)
+        convert_event_coords_to_window (event_window, parent,
+                                        event_x, event_y,
+                                        &event_x, &event_y);
+
+      parent_to_window (parent, window,
+                        event_x, event_y,
+                        &event_x, &event_y);
+    }
+
+  *window_x = event_x;
+  *window_y = event_y;
+}
+
+static void
+convert_window_coords_to_event (GdkWindow *event_window,
+				GdkWindow *window,
+				gdouble    window_x,
+                                gdouble    window_y,
+				gdouble   *event_x,
+                                gdouble   *event_y)
+{
+  if (event_window != window)
+    {
+      GdkWindow *parent = GDK_WINDOW (GDK_WINDOW_OBJECT (window)->parent);
+
+      window_to_parent (window, parent,
+                        window_x, window_y,
+                        &window_x, &window_y);
+
+      if (parent != event_window)
+        convert_window_coords_to_event (event_window, parent,
+                                        window_x, window_y,
+                                        &window_x, &window_y);
+
+    }
+
+  *event_x = window_x;
+  *event_y = window_y;
+}
+
+
+static gboolean
+proxy_pointer_event (GdkWindow                  *pointer_window,
+		     gdouble                     event_x,
+                     gdouble                     event_y,
+		     GdkEvent                   *source_event,
+		     GdkWindowOffscreenChildren *offscreen_children)
+{
+  GdkWindow *event_win, *cursor_window;
+  gboolean crossing_event;
+  gboolean sent_motion;
+  GdkEvent *event;
+  guint state;
+  guint32 time_;
+
+  gdk_event_get_state (source_event, &state);
+  time_ = gdk_event_get_time (source_event);
+
+  crossing_event =
+    source_event->type == GDK_ENTER_NOTIFY ||
+    source_event->type == GDK_LEAVE_NOTIFY;
+
+  if (crossing_event)
+    {
+      GdkEventCrossing *crossing = &source_event->crossing;
+
+      if (crossing->mode == GDK_CROSSING_GRAB)
+	{
+	  if (crossing->type == GDK_LEAVE_NOTIFY &&
+	      offscreen_children->window_under_pointer != NULL)
+	    {
+	      send_crossing_events (offscreen_children, source_event->any.window,
+				    offscreen_children->window_under_pointer,
+				    NULL,
+				    GDK_CROSSING_GRAB,
+				    event_x, event_y, state, time_,
+				    TRUE, FALSE);
+	    }
+
+	  if (crossing->type == GDK_ENTER_NOTIFY &&
+	      offscreen_children->pointer_grab_window != NULL)
+	    {
+	      send_crossing_events (offscreen_children, source_event->any.window,
+				    NULL, offscreen_children->pointer_grab_window,
+				    GDK_CROSSING_GRAB,
+				    event_x, event_y, state, time_,
+				    FALSE, TRUE);
+	    }
+	}
+
+      if (crossing->mode == GDK_CROSSING_UNGRAB)
+	{
+	  if (crossing->type == GDK_LEAVE_NOTIFY &&
+	      offscreen_children->pointer_grab_window != NULL)
+	    {
+	      send_crossing_events (offscreen_children, source_event->any.window,
+				    offscreen_children->pointer_grab_window,
+				    NULL,
+				    GDK_CROSSING_UNGRAB,
+                                    event_x, event_y, state, time_,
+				    TRUE, FALSE);
+	    }
+
+	  if (crossing->type == GDK_ENTER_NOTIFY &&
+	      offscreen_children->window_under_pointer != NULL)
+	    {
+	      send_crossing_events (offscreen_children, source_event->any.window,
+				    NULL,
+				    offscreen_children->window_under_pointer,
+				    GDK_CROSSING_UNGRAB,
+				    event_x, event_y, state, time_,
+				    FALSE, TRUE);
+	    }
+	}
+    }
+
+  cursor_window = pointer_window;
+  if (offscreen_children->pointer_grab_window &&
+      (pointer_window == NULL ||
+       !is_parent_of (offscreen_children->pointer_grab_window, pointer_window)))
+    cursor_window = offscreen_children->pointer_grab_window;
+
+  /* TODO: set cursor from cursor_window, or grab cursor */
+
+  sent_motion = FALSE;
+  if (!crossing_event &&
+      (offscreen_children->window_under_pointer == pointer_window ||
+       (offscreen_children->pointer_grab_window != NULL &&
+	!offscreen_children->pointer_grab_owner_events)))
+    {
+      /* send motion events */
+      event_win = get_target_window_for_pointer_event (offscreen_children, pointer_window, GDK_MOTION_NOTIFY, state);
+
+      if (event_win)
+	{
+	  sent_motion = TRUE;
+	  event = make_gdk_event (event_win, GDK_MOTION_NOTIFY, source_event);
+	  event->motion.time = time_;
+	  convert_event_coords_to_window (source_event->any.window,
+					  event_win, event_x, event_y,
+					  &event->motion.x, &event->motion.y);
+	  event->motion.x_root = event_x; /* TODO: right? */
+	  event->motion.y_root = event_y;
+	  event->motion.state = state;
+	  event->motion.is_hint = FALSE;
+	  event->motion.device = NULL;
+	  if (source_event && source_event->type == GDK_MOTION_NOTIFY)
+	    event->motion.device = source_event->motion.device;
+	}
+    }
+
+  send_crossing_events (offscreen_children,
+			source_event->any.window,
+			offscreen_children->window_under_pointer,
+			pointer_window,
+			GDK_CROSSING_NORMAL,
+			event_x, event_y,
+			state, time_,
+			TRUE, TRUE);
+
+
+  /* unlink move event from parent if we sent a motion event */
+  return source_event->type == GDK_MOTION_NOTIFY && sent_motion;
+}
+
+static gboolean
+is_button_type (GdkEventType type)
+{
+  return type == GDK_BUTTON_PRESS ||
+         type == GDK_2BUTTON_PRESS ||
+         type == GDK_3BUTTON_PRESS ||
+         type == GDK_BUTTON_RELEASE ||
+         type == GDK_SCROLL;
+}
+
+static gboolean
+is_motion_type (GdkEventType type)
+{
+  return type == GDK_MOTION_NOTIFY ||
+         type == GDK_ENTER_NOTIFY ||
+         type == GDK_LEAVE_NOTIFY;
+}
+
+static gboolean
+proxy_button_event (GdkWindow                  *pointer_window,
+		    gdouble                     event_x,
+                    gdouble                     event_y,
+		    GdkEvent                   *source_event,
+		    GdkWindowOffscreenChildren *offscreen_children)
+{
+  GdkWindow *event_win;
+  GdkEvent *event;
+  guint state;
+  guint32 time_;
+  GdkEventType type;
+
+  type = source_event->any.type;
+  gdk_event_get_state (source_event, &state);
+  time_ = gdk_event_get_time (source_event);
+
+  if (type == GDK_BUTTON_PRESS && pointer_window != NULL)
+    _gdk_windowing_update_implicit_grab (source_event->any.window,
+                                         pointer_window,
+                                         gdk_window_get_events (pointer_window),
+                                         time_);
+
+  event_win = get_target_window_for_pointer_event (offscreen_children,
+                                                   pointer_window,
+                                                   type,
+                                                   state);
+
+  if (event_win && event_win != source_event->any.window)
+    {
+      event = make_gdk_event (event_win, type, source_event);
+
+      switch (type)
+        {
+        case GDK_BUTTON_PRESS:
+        case GDK_2BUTTON_PRESS:
+        case GDK_3BUTTON_PRESS:
+        case GDK_BUTTON_RELEASE:
+          event->button.time = time_;
+          event->button.button = source_event->button.button;
+          convert_event_coords_to_window (source_event->any.window,
+                                          event_win,
+                                          event_x, event_y,
+                                          &event->button.x, &event->button.y);
+          event->button.x_root = event_x;
+          event->button.y_root = event_y;
+          event->button.state = state;
+          event->button.device = source_event->button.device;
+          return TRUE;
+
+        case GDK_SCROLL:
+          event->scroll.time = time_;
+          event->scroll.direction = source_event->scroll.direction;
+          convert_event_coords_to_window (source_event->any.window,
+                                          event_win,
+                                          event_x, event_y,
+                                          &event->scroll.x, &event->scroll.y);
+          event->scroll.x_root = event_x;
+          event->scroll.y_root = event_y;
+          event->scroll.state = state;
+          event->scroll.device = source_event->scroll.device;
+          return TRUE;
+
+        default:
+          return FALSE;
+        }
+    }
+
+  return FALSE;
+}
+
+static gint
+gdk_offscreen_window_get_origin (GdkWindow *window,
+				 gint      *x,
+				 gint      *y)
+{
+  GdkWindow *event_window;
+  int parent_x, parent_y;
+  gdouble event_x, event_y;
+
+  event_window = _gdk_window_get_offscreen_event_window (window);
+
+  convert_window_coords_to_event (event_window, window,
+				  0.0, 0.0,
+                                  &event_x, &event_y);
+
+  gdk_window_get_origin (event_window, &parent_x, &parent_y);
+
+  if (x)
+    *x = floor (event_x + 0.5) + parent_x;
+  if (y)
+    *y = floor (event_y + 0.5) + parent_y;
+
+  return TRUE;
+}
+
+static void
+gdk_offscreen_window_shape_combine_mask (GdkWindow *window,
+                                         GdkBitmap *mask,
+                                         gint       x,
+                                         gint       y)
+{
+}
+
+static void
+gdk_offscreen_window_shape_combine_region (GdkWindow       *window,
+                                           const GdkRegion *shape_region,
+                                           gint             offset_x,
+                                           gint             offset_y)
+{
+}
+
+static void
+gdk_offscreen_window_set_child_shapes (GdkWindow *window)
+{
+}
+
+static void
+gdk_offscreen_window_merge_child_shapes (GdkWindow *window)
+{
+}
+
+static gboolean
+gdk_offscreen_window_set_static_gravities (GdkWindow *window,
+					   gboolean   use_static)
+{
+  return TRUE;
+}
+
+static void
+gdk_offscreen_window_set_cursor (GdkWindow *window,
+				 GdkCursor *cursor)
+{
+  GdkWindowObject *private = (GdkWindowObject *)window;
+  GdkOffscreenWindow *offscreen;
+
+  offscreen = GDK_OFFSCREEN_WINDOW (private->impl);
+
+  if (offscreen->cursor)
+    {
+      gdk_cursor_unref (offscreen->cursor);
+      offscreen->cursor = NULL;
+    }
+
+  if (cursor)
+    offscreen->cursor = gdk_cursor_ref (cursor);
+
+  /* TODO: The cursor is never actually used... */
+}
+
+static void
+gdk_offscreen_window_get_geometry (GdkWindow *window,
+				   gint      *x,
+				   gint      *y,
+				   gint      *width,
+				   gint      *height,
+				   gint      *depth)
+{
+  GdkWindowObject *private = (GdkWindowObject *)window;
+  GdkOffscreenWindow *offscreen;
+
+  offscreen = GDK_OFFSCREEN_WINDOW (private->impl);
+
+  g_return_if_fail (window == NULL || GDK_IS_WINDOW (window));
+
+  if (!GDK_WINDOW_DESTROYED (window))
+    {
+      if (x)
+	*x = private->x;
+      if (y)
+	*y = private->y;
+      if (width)
+	*width = offscreen->width;
+      if (height)
+	*height = offscreen->width;
+      if (depth)
+	*depth = private->depth;
+    }
+}
+
+void
+_gdk_offscreen_window_transform_coords (GdkWindow       *event_window,
+					GdkWindow       *relative_window,
+					gint            *x,
+					gint            *y)
+{
+  gdouble window_x, window_y;
+
+  if (relative_window != event_window)
+    {
+      /* The relative window is an offscreen window */
+      convert_event_coords_to_window (event_window, relative_window,
+				      *x, *y,
+				      &window_x, &window_y);
+      *x = floor (window_x + 0.5);
+      *y = floor (window_y + 0.5);
+    }
+}
+
+void
+_gdk_offscreen_window_set_pointer_grab (GdkWindow *old_grab_window,
+					GdkWindow *old_native_grab_window,
+					GdkWindow *grab_window,
+					gboolean owner_events,
+					guint event_mask,
+					guint32 time_)
+{
+  GdkDisplay *display;
+  GdkWindow *crossing_start, *event_window;
+  GdkWindowOffscreenChildren *offscreen_children;
+  gint wx, wy;
+
+  display = gdk_drawable_get_display (grab_window);
+  event_window = _gdk_window_get_offscreen_event_window (grab_window);
+  offscreen_children = GDK_WINDOW_OBJECT (event_window)->offscreen_children;
+
+  /* Normal GRAB events are sent by listening for enter and leave
+   * events on the parent of the toplevel, which is then proxied
+   * into the offscreen where we create them. However, there are two
+   * cases where X will not send these events:
+   * * When there is already a grab on the parent of the toplevel
+   *   offscreen grab window
+   * * When there is no grab, but the pointer is already in the
+   *   parent of the toplevel offscreen grab window
+   * In these cases we need to generate our own grab crossing events.
+   */
+  crossing_start = NULL;
+  if (old_grab_window != NULL && old_native_grab_window == event_window)
+    crossing_start = old_grab_window;
+
+  if (old_grab_window == NULL &&
+      _gdk_windowing_window_at_pointer (display, &wx, &wy) == event_window)
+    crossing_start = offscreen_children->window_under_pointer;
+
+  if (crossing_start != NULL)
+    {
+      /* TODO: Hmmm, is this totally right if start is in another toplevel? */
+      send_crossing_events (offscreen_children,
+			    event_window,
+			    crossing_start,
+			    grab_window,
+			    GDK_CROSSING_GRAB,
+			    /* These may be stale... */
+			    offscreen_children->last_mouse_x,
+			    offscreen_children->last_mouse_y,
+			    offscreen_children->last_state,
+			    time_, TRUE, TRUE);
+    }
+
+
+  /* TODO: What about the case of grabbing the toplevel parent, when
+     an offscreen child of it is grabbed or has the pointer? */
+
+  if (offscreen_children->pointer_grab_window != NULL)
+    g_object_unref (offscreen_children->pointer_grab_window);
+
+  offscreen_children->pointer_grab_window = g_object_ref (grab_window);
+  offscreen_children->pointer_grab_owner_events = owner_events;
+  offscreen_children->pointer_grab_event_mask = event_mask;
+}
+
+void
+_gdk_offscreen_window_unset_pointer_grab (GdkWindow *window,
+					  gboolean regrab,
+					  gboolean grab_one_pointer_release_event,
+					  guint32 time_)
+{
+  GdkWindow *event_window;
+  GdkWindowOffscreenChildren *offscreen_children;
+  GdkDisplay *display;
+  gint wx, wy;
+
+  display = gdk_drawable_get_display (window);
+  event_window = _gdk_window_get_offscreen_event_window (window);
+  offscreen_children = GDK_WINDOW_OBJECT (event_window)->offscreen_children;
+
+  g_assert (offscreen_children->pointer_grab_window != NULL);
+
+  if (grab_one_pointer_release_event)
+    offscreen_children->grab_one_pointer_release_event = offscreen_children->pointer_grab_window;
+
+  g_object_unref (offscreen_children->pointer_grab_window);
+  offscreen_children->pointer_grab_window = NULL;
+
+  /* Normal UNGRAB events are sent by listening for enter and leave
+   * events on the parent of the toplevel, which is then proxied
+   * into the offscreen where we create them. However, there are two
+   * cases where X will not send these events:
+   * * When this ungrab is due to a new grab on the parent of the toplevel
+   *   offscreen grab window.
+   * * When there is no new grab, and the pointer is already in the
+   *   parent of the toplevel offscreen grab window
+   * In the first case we send the right GRAB events from the grab, but
+   * in the second case we need to generate our own UNGRAB crossing events.
+   */
+  if (!regrab &&
+      _gdk_windowing_window_at_pointer (display, &wx, &wy) == event_window)
+    {
+      GdkWindow *crossing_end = offscreen_children->window_under_pointer;
+
+      send_crossing_events (offscreen_children, event_window,
+			    window,
+			    crossing_end,
+			    GDK_CROSSING_UNGRAB,
+			    /* These may be stale... */
+			    offscreen_children->last_mouse_x,
+			    offscreen_children->last_mouse_y,
+			    offscreen_children->last_state,
+			    time_, TRUE, TRUE);
+    }
+}
+
+void
+_gdk_event_rewrite_for_offscreen (GdkDisplay *display,
+				  GList      *event_link,
+				  GdkEvent   *event)
+{
+  GdkWindow *event_window;
+  GdkWindow *pointer_window;
+  GdkWindowObject *event_private;
+  GdkWindowOffscreenChildren *offscreen_children;
+  gboolean is_grab;
+  gdouble x, y;
+  gboolean unlink_event;
+
+  event_window = event->any.window;
+  event_private = GDK_WINDOW_OBJECT (event_window);
+  if (!event_window || !event_private->offscreen_children)
+    return;
+
+  if (!(is_button_type (event->type) || is_motion_type (event->type)))
+    return;
+
+  offscreen_children = event_private->offscreen_children;
+
+  gdk_event_get_coords (event, &x, &y);
+  offscreen_children->last_mouse_x = x;
+  offscreen_children->last_mouse_y = y;
+
+  gdk_event_get_state (event, &offscreen_children->last_state);
+
+  pointer_window = _gdk_window_get_offscreen_window_child_at (event_window,
+                                                              x, y,
+                                                              NULL, NULL);
+  if (pointer_window == event_window)
+    pointer_window = NULL;
+
+  is_grab = FALSE;
+  if (offscreen_children->pointer_grab_window)
+    is_grab = TRUE;
+
+  unlink_event = FALSE;
+  if (is_motion_type (event->type))
+    unlink_event = proxy_pointer_event (pointer_window, x, y,
+                                        event,
+                                        offscreen_children);
+
+  if (is_button_type (event->type))
+    unlink_event = proxy_button_event (pointer_window, x, y,
+                                       event,
+                                       offscreen_children);
+
+  if (unlink_event)
+    {
+      _gdk_event_queue_remove_link (display, event_link);
+      g_list_free_1 (event_link);
+      gdk_event_free (event);
+    }
+}
+
+/**
+ * gdk_window_set_offscreen_hooks:
+ * @window: a #GdkWindow
+ * @hooks: a table of pointers to functions for handling offscreen
+ *   window coordinates translations
+ *
+ * Sets the parent-to-window and window-to-parent coordinates
+ * translation functions for offscreen windows.
+ *
+ * This function is useful for complex widgets employing
+ * offscreen windows.
+ *
+ * Since: 2.16
+ */
+void
+gdk_window_set_offscreen_hooks (GdkWindow               *window,
+                                const GdkOffscreenHooks *hooks)
+{
+  GdkWindowObject *private;
+
+  g_return_if_fail (GDK_IS_WINDOW (window));
+  g_return_if_fail (hooks != NULL);
+
+  private = (GdkWindowObject *) window;
+
+  private->offscreen_hooks = hooks;
+}
+
+static void
+gdk_offscreen_window_impl_iface_init (GdkWindowImplIface *iface)
+{
+  iface->show = gdk_offscreen_window_show;
+  iface->hide = gdk_offscreen_window_hide;
+  iface->withdraw = gdk_offscreen_window_withdraw;
+  iface->raise = gdk_offscreen_window_raise;
+  iface->lower = gdk_offscreen_window_lower;
+  iface->move_resize = gdk_offscreen_window_move_resize;
+  iface->scroll = gdk_offscreen_window_scroll;
+  iface->move_region = gdk_offscreen_window_move_region;
+  iface->set_background = gdk_offscreen_window_set_background;
+  iface->set_back_pixmap = gdk_offscreen_window_set_back_pixmap;
+  iface->get_events = gdk_offscreen_window_get_events;
+  iface->set_events = gdk_offscreen_window_set_events;
+  iface->clear_area = gdk_offscreen_window_clear_area;
+  iface->reparent = gdk_offscreen_window_reparent;
+  iface->set_cursor = gdk_offscreen_window_set_cursor;
+  iface->get_geometry = gdk_offscreen_window_get_geometry;
+  iface->get_origin = gdk_offscreen_window_get_origin;
+  iface->shape_combine_mask = gdk_offscreen_window_shape_combine_mask;
+  iface->shape_combine_region = gdk_offscreen_window_shape_combine_region;
+  iface->set_child_shapes = gdk_offscreen_window_set_child_shapes;
+  iface->merge_child_shapes = gdk_offscreen_window_merge_child_shapes;
+  iface->set_static_gravities = gdk_offscreen_window_set_static_gravities;
+  iface->get_offsets = gdk_offscreen_window_get_offsets;
+}
+
+#define __GDK_OFFSCREEN_WINDOW_C__
+#include "gdkaliasdef.c"
diff --git a/gdk/gdkpixmap.c b/gdk/gdkpixmap.c
index c717b9a..806a5ca 100644
--- a/gdk/gdkpixmap.c
+++ b/gdk/gdkpixmap.c
@@ -229,6 +229,48 @@ gdk_pixmap_finalize (GObject *object)
   G_OBJECT_CLASS (parent_class)->finalize (object);
 }
 
+GdkPixmap *
+gdk_pixmap_new (GdkDrawable *drawable,
+                gint         width,
+                gint         height,
+                gint         depth)
+{
+  GdkDrawable *source_drawable;
+
+  source_drawable = _gdk_drawable_get_source_drawable (drawable);
+  return _gdk_pixmap_new (source_drawable, width, height, depth);
+}
+
+GdkPixmap *
+gdk_bitmap_create_from_data (GdkDrawable *drawable,
+                             const gchar *data,
+                             gint         width,
+                             gint         height)
+{
+  GdkDrawable *source_drawable;
+
+  source_drawable = _gdk_drawable_get_source_drawable (drawable);
+  return _gdk_bitmap_create_from_data (source_drawable, data, width, height);
+}
+
+GdkPixmap*
+gdk_pixmap_create_from_data (GdkDrawable    *drawable,
+                             const gchar    *data,
+                             gint            width,
+                             gint            height,
+                             gint            depth,
+                             const GdkColor *fg,
+                             const GdkColor *bg)
+{
+  GdkDrawable *source_drawable;
+
+  source_drawable = _gdk_drawable_get_source_drawable (drawable);
+  return _gdk_pixmap_create_from_data (source_drawable,
+                                       data, width, height,
+                                       depth, fg,bg);
+}
+
+
 static GdkGC *
 gdk_pixmap_create_gc (GdkDrawable     *drawable,
                       GdkGCValues     *values,
diff --git a/gdk/gdkwindow.c b/gdk/gdkwindow.c
index 699d701..4a050e0 100644
--- a/gdk/gdkwindow.c
+++ b/gdk/gdkwindow.c
@@ -56,18 +56,6 @@ typedef struct {
   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);
@@ -192,6 +180,7 @@ static void         gdk_window_real_set_colormap (GdkDrawable *drawable,
 						  GdkColormap *cmap);
 static GdkColormap* gdk_window_real_get_colormap (GdkDrawable *drawable);
 
+static GdkDrawable* gdk_window_get_source_drawable    (GdkDrawable *drawable);
 static GdkDrawable* gdk_window_get_composite_drawable (GdkDrawable *drawable,
 						       gint         x,
 						       gint         y,
@@ -223,12 +212,6 @@ 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;
 
@@ -319,6 +302,7 @@ gdk_window_class_init (GdkWindowObjectClass *klass)
   drawable_class->get_clip_region = gdk_window_get_clip_region;
   drawable_class->get_visible_region = gdk_window_get_visible_region;
   drawable_class->get_composite_drawable = gdk_window_get_composite_drawable;
+  drawable_class->get_source_drawable = gdk_window_get_source_drawable;
 }
 
 static void
@@ -371,7 +355,10 @@ gdk_window_new (GdkWindow     *parent,
   
   g_return_val_if_fail (attributes != NULL, NULL);
 
-  window = _gdk_window_new (parent, attributes, attributes_mask);
+  if (parent && gdk_window_is_offscreen (parent))
+    window = _gdk_window_new_offscreen (parent, attributes, attributes_mask, FALSE);
+  else
+    window = _gdk_window_new (parent, attributes, attributes_mask);
 
   /* Inherit redirection from parent */
   if (parent != NULL)
@@ -563,8 +550,12 @@ _gdk_window_destroy_hierarchy (GdkWindow *window,
 	      
 	      g_list_free (children);
 	    }
-	  
-	  _gdk_windowing_window_destroy (window, recursing, foreign_destroy);
+
+          if (gdk_window_is_offscreen (window))
+            _gdk_offscreen_window_destroy (window, recursing);
+          else
+            _gdk_windowing_window_destroy (window, recursing, foreign_destroy);
+
 	  private->parent = NULL;
 	  private->destroyed = TRUE;
 
@@ -1568,7 +1559,18 @@ gdk_window_draw_text_wc (GdkDrawable    *drawable,
   RESTORE_GC (gc);
 }
 
-static GdkDrawable*
+static GdkDrawable *
+gdk_window_get_source_drawable (GdkDrawable *drawable)
+{
+  GdkWindow *window = GDK_WINDOW (drawable);
+
+  if (gdk_window_is_offscreen (window))
+    return gdk_window_get_offscreen_pixmap (window);
+
+  return drawable;
+}
+
+static GdkDrawable *
 gdk_window_get_composite_drawable (GdkDrawable *drawable,
                                    gint         x,
                                    gint         y,
@@ -1583,13 +1585,16 @@ gdk_window_get_composite_drawable (GdkDrawable *drawable,
   GdkRectangle rect;
   GdkGC *tmp_gc;
   gboolean overlap_buffer;
+  gboolean is_offscreen;
 
   GDK_WINDOW_IMPL_GET_IFACE (private->impl)->get_offsets (GDK_WINDOW (drawable),
                                                           composite_x_offset,
                                                           composite_y_offset);
-  
-  if ((GDK_IS_WINDOW (drawable) && GDK_WINDOW_DESTROYED (drawable))
-      || private->paint_stack == NULL)
+
+  is_offscreen = gdk_window_is_offscreen (GDK_WINDOW (drawable));
+
+  if ((GDK_IS_WINDOW (drawable) && GDK_WINDOW_DESTROYED (drawable)) ||
+      (!is_offscreen && private->paint_stack == NULL))
     {
       /* No backing store */
       return g_object_ref (drawable);
@@ -1626,7 +1631,7 @@ gdk_window_get_composite_drawable (GdkDrawable *drawable,
 	}
     }
 
-  if (!overlap_buffer)
+  if (!overlap_buffer && !is_offscreen)
     return g_object_ref (drawable);
 
   tmp_pixmap = gdk_pixmap_new (drawable, width, height, -1);
@@ -2502,6 +2507,102 @@ static GSList *update_windows = NULL;
 static guint update_idle = 0;
 static gboolean debug_updates = FALSE;
 
+static inline gboolean
+gdk_window_is_ancestor (GdkWindow *window,
+                        GdkWindow *ancestor)
+{
+  while (window)
+    {
+      GdkWindow *parent = (GdkWindow*) ((GdkWindowObject*) window)->parent;
+
+      if (parent == ancestor)
+        return TRUE;
+
+      window = parent;
+    }
+
+  return FALSE;
+}
+
+static void
+gdk_window_add_update_window (GdkWindow *window)
+{
+  GSList *tmp;
+  GSList *prev = NULL;
+  gboolean has_ancestor_in_list = FALSE;
+
+  for (tmp = update_windows; tmp; tmp = tmp->next)
+    {
+      GdkWindowObject *parent = GDK_WINDOW_OBJECT (window)->parent;
+
+      /*  check if tmp is an ancestor of "window"; if it is, set a
+       *  flag indicating that all following windows are either
+       *  children of "window" or from a differen hierarchy
+       */
+      if (!has_ancestor_in_list && gdk_window_is_ancestor (window, tmp->data))
+        has_ancestor_in_list = TRUE;
+
+      /* insert in reverse stacking order when adding around siblings,
+       * so processing updates properly paints over lower stacked windows
+       */
+      if (parent == GDK_WINDOW_OBJECT (tmp->data)->parent)
+        {
+          gint index = g_list_index (parent->children, window);
+          for (; tmp && parent == GDK_WINDOW_OBJECT (tmp->data)->parent; tmp = tmp->next)
+            {
+              gint sibling_index = g_list_index (parent->children, tmp->data);
+              if (index > sibling_index)
+                break;
+              prev = tmp;
+            }
+          /* here, tmp got advanced past all lower stacked siblings */
+          tmp = g_slist_prepend (tmp, window);
+          if (prev)
+            prev->next = tmp;
+          else
+            update_windows = tmp;
+          return;
+        }
+
+      /*  if "window" has an ancestor in the list and tmp is one of
+       *  "window's" children, insert "window" before tmp
+       */
+      if (has_ancestor_in_list && gdk_window_is_ancestor (tmp->data, window))
+        {
+          tmp = g_slist_prepend (tmp, window);
+
+          if (prev)
+            prev->next = tmp;
+          else
+            update_windows = tmp;
+          return;
+        }
+
+      /*  if we're at the end of the list and had an ancestor it it,
+       *  append to the list
+       */
+      if (! tmp->next && has_ancestor_in_list)
+        {
+          tmp = g_slist_append (tmp, window);
+          return;
+        }
+
+      prev = tmp;
+    }
+
+  /*  if all above checks failed ("window" is from a different
+   *  hierarchy than what is already in the list) or the list is
+   *  empty, prepend
+   */
+  update_windows = g_slist_prepend (update_windows, window);
+}
+
+static void
+gdk_window_remove_update_window (GdkWindow *window)
+{
+  update_windows = g_slist_remove (update_windows, window);
+}
+
 static gboolean
 gdk_window_update_idle (gpointer data)
 {
@@ -2563,8 +2664,9 @@ gdk_window_process_updates_internal (GdkWindow *window)
               gdk_display_sync (gdk_drawable_get_display (window));
               g_usleep (70000);
             }
-          
-	  save_region = _gdk_windowing_window_queue_antiexpose (window, update_area);
+
+          if (!gdk_window_is_offscreen (window))
+            save_region = _gdk_windowing_window_queue_antiexpose (window, update_area);
 
 	  if (save_region)
 	    expose_region = gdk_region_copy (update_area);
@@ -2659,7 +2761,7 @@ gdk_window_process_all_updates (void)
         {
 	  if (private->update_freeze_count ||
 	      gdk_window_is_toplevel_frozen (tmp_list->data))
-	    update_windows = g_slist_prepend (update_windows, private);
+	    gdk_window_add_update_window ((GdkWindow *) private);
 	  else
 	    gdk_window_process_updates_internal (tmp_list->data);
 	}
@@ -2696,7 +2798,7 @@ gdk_window_process_updates (GdkWindow *window,
 
   g_return_if_fail (window != NULL);
   g_return_if_fail (GDK_IS_WINDOW (window));
-  
+
   if (GDK_IS_PAINTABLE (private->impl))
     {
       GdkPaintableIface *iface = GDK_PAINTABLE_GET_IFACE (private->impl);
@@ -2712,17 +2814,17 @@ gdk_window_process_updates (GdkWindow *window,
       !gdk_window_is_toplevel_frozen (window))
     {      
       gdk_window_process_updates_internal (window);
-      update_windows = g_slist_remove (update_windows, window);
+      gdk_window_remove_update_window (window);
     }
 
   if (update_children)
     {
-      GList *tmp_list = private->children;
-      while (tmp_list)
-	{
-	  gdk_window_process_updates (tmp_list->data, TRUE);
-	  tmp_list = tmp_list->next;
-	}
+      /* process updates in reverse stacking order so composition or
+       * painting over achieves the desired effect for offscreen windows
+       */
+      GList *node;
+      for (node = g_list_last (private->children); node; node = node->prev)
+        gdk_window_process_updates (node->data, TRUE);
     }
 }
 
@@ -2882,7 +2984,8 @@ gdk_window_invalidate_maybe_recurse (GdkWindow       *window,
 	  
 	  /* remove child area from the invalid area of the parent */
 	  if (GDK_WINDOW_IS_MAPPED (child) && !child->shaped &&
-	      !child->composited)
+	      !child->composited &&
+              !_gdk_window_is_toplevel_offscreen (GDK_WINDOW (child)))
 	    gdk_region_subtract (visible_region, child_region);
 	  
 	  if (child_func && (*child_func) ((GdkWindow *)child, user_data))
@@ -2916,7 +3019,7 @@ gdk_window_invalidate_maybe_recurse (GdkWindow       *window,
 	}
       else
 	{
-	  update_windows = g_slist_prepend (update_windows, window);
+	  gdk_window_add_update_window (window);
 	  private->update_area = gdk_region_copy (visible_region);
 	  
 	  gdk_window_schedule_update (window);
@@ -2995,8 +3098,8 @@ gdk_window_get_update_area (GdkWindow *window)
       tmp_region = private->update_area;
       private->update_area = NULL;
 
-      update_windows = g_slist_remove (update_windows, window);
-      
+      gdk_window_remove_update_window (window);
+
       return tmp_region;
     }
   else
@@ -3020,8 +3123,8 @@ _gdk_window_clear_update_area (GdkWindow *window)
 
   if (private->update_area)
     {
-      update_windows = g_slist_remove (update_windows, window);
-      
+      gdk_window_remove_update_window (window);
+
       gdk_region_destroy (private->update_area);
       private->update_area = NULL;
     }
@@ -4336,18 +4439,19 @@ window_get_size_rectangle (GdkWindow    *window,
 /* 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 *
+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)
+					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;
+  gboolean clip_offsprings = TRUE;
   GdkWindowObject *parentwin, *lastwin;
 
   if (base_x_offset)
@@ -4360,12 +4464,12 @@ _gdk_window_calculate_full_clip_region (GdkWindow *window,
 
   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.
+  /* redraws to the base redirection window must not be clipped by offspring
+   * areas or invisible ancestor regions, retrieving pixel content for those
+   * areas is what offscreen rendering is normally used for.
    */
   if (private->redirect && private->redirect->redirected == private)
-    return gdk_region_rectangle (&visible_rect);
+    clip_offsprings = FALSE;
 
   /* real_clip_region is in window coordinates */
   real_clip_region = gdk_region_rectangle (&visible_rect);
@@ -4379,27 +4483,37 @@ _gdk_window_calculate_full_clip_region (GdkWindow *window,
     parentwin = lastwin->parent;
   
   /* Remove the areas of all overlapping windows above parentwin in the hiearachy */
-  for (; parentwin != NULL && (parentwin == private || lastwin != (GdkWindowObject *)base_window);
+  for (; clip_offsprings && parentwin != NULL &&
+         (parentwin == private || lastwin != (GdkWindowObject*) base_window);
        lastwin = parentwin, parentwin = lastwin->parent)
     {
       GList *cur;
       GdkRectangle real_clip_rect;
-      
+      gboolean is_offscreen;
+
       if (parentwin != private)
 	{
 	  x_offset += GDK_WINDOW_OBJECT (lastwin)->x;
 	  y_offset += GDK_WINDOW_OBJECT (lastwin)->y;
 	}
-      
+
+      is_offscreen = gdk_window_is_offscreen (GDK_WINDOW (parentwin));
+
       /* children is ordered in reverse stack order */
-      for (cur = GDK_WINDOW_OBJECT (parentwin)->children; cur && cur->data != lastwin; cur = cur->next)
+      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;
 
+          /* Ignore toplevel offscreen children */
+          if (_gdk_window_is_toplevel_offscreen (child))
+            continue;
+
           window_get_size_rectangle (child, &visible_rect);
 
 	  /* Convert rect to "window" coords */
@@ -4539,5 +4653,39 @@ gdk_window_redirect_free (GdkWindowRedirect *redirect)
   g_free (redirect);
 }
 
+void
+_gdk_window_add_offscreen_child (GdkWindow *window)
+{
+  GdkWindowObject *obj = (GdkWindowObject*) window;
+
+  if (obj->offscreen_children)
+    obj->offscreen_children->n_offscreen_children++;
+  else
+    {
+      obj->offscreen_children = g_slice_new0 (GdkWindowOffscreenChildren);
+      obj->offscreen_children->n_offscreen_children = 1;
+    }
+}
+
+void
+_gdk_window_remove_offscreen_child (GdkWindow *window)
+{
+  GdkWindowObject *obj = (GdkWindowObject*) window;
+
+  g_return_if_fail (obj->offscreen_children != NULL);
+
+  if (obj->offscreen_children)
+    {
+      g_return_if_fail (obj->offscreen_children->n_offscreen_children > 0);
+
+      obj->offscreen_children->n_offscreen_children--;
+      if (obj->offscreen_children->n_offscreen_children == 0)
+        {
+          g_slice_free (GdkWindowOffscreenChildren, obj->offscreen_children);
+          obj->offscreen_children = NULL;
+        }
+    }
+}
+
 #define __GDK_WINDOW_C__
 #include "gdkaliasdef.c"
diff --git a/gdk/gdkwindow.h b/gdk/gdkwindow.h
index 06ee018..72d9ccc 100644
--- a/gdk/gdkwindow.h
+++ b/gdk/gdkwindow.h
@@ -37,10 +37,12 @@
 
 G_BEGIN_DECLS
 
-typedef struct _GdkGeometry           GdkGeometry;
-typedef struct _GdkWindowAttr	      GdkWindowAttr;
-typedef struct _GdkPointerHooks	      GdkPointerHooks;
-typedef struct _GdkWindowRedirect     GdkWindowRedirect;
+typedef struct _GdkGeometry                GdkGeometry;
+typedef struct _GdkWindowAttr              GdkWindowAttr;
+typedef struct _GdkPointerHooks            GdkPointerHooks;
+typedef struct _GdkOffscreenHooks          GdkOffscreenHooks;
+typedef struct _GdkWindowRedirect          GdkWindowRedirect;
+typedef struct _GdkWindowOffscreenChildren GdkWindowOffscreenChildren;
 
 /* Classes of windows.
  *   InputOutput: Almost every window should be of this type. Such windows
@@ -248,9 +250,64 @@ struct _GdkPointerHooks
                                    gint            *win_y);
 };
 
+struct _GdkOffscreenHooks
+{
+  void       (*parent_to_window)     (GdkWindow *parent,
+                                      GdkWindow *window,
+                                      gdouble    parent_x,
+                                      gdouble    parent_y,
+                                      gdouble   *win_x,
+                                      gdouble   *win_y);
+  void       (*window_to_parent)     (GdkWindow *window,
+                                      GdkWindow *parent,
+                                      gdouble    win_x,
+                                      gdouble    win_y,
+                                      gdouble   *parent_x,
+                                      gdouble   *parent_y);
+  GdkWindow* (*find_offscreen_child) (GdkWindow *window,
+                                      gdouble    x,
+                                      gdouble    y);
+};
+
+struct _GdkWindowOffscreenChildren
+{
+  gint n_offscreen_children;
+
+  /* The window with the mouse in it, or NULL if the pointer is not inside
+   *  an offscreen child. A pointer grab doesn't affect this.
+   */
+  GdkWindow *window_under_pointer;
+
+  gdouble last_mouse_x, last_mouse_y;
+  guint32 last_state;
+
+  GdkWindow *pointer_grab_window;
+  gboolean pointer_grab_owner_events;
+
+  GdkWindow *grab_one_pointer_release_event;
+  guint pointer_grab_event_mask;
+};
+
 typedef struct _GdkWindowObject GdkWindowObject;
 typedef struct _GdkWindowObjectClass GdkWindowObjectClass;
 
+struct _GdkWindowRedirect
+{
+  GdkWindowObject *redirected;
+  GdkDrawable *pixmap;
+
+  gint src_x;
+  gint src_y;
+  gint dest_x;
+  gint dest_y;
+  gint width;
+  gint height;
+
+  GdkRegion *damage;
+  guint damage_idle;
+};
+
+
 #define GDK_TYPE_WINDOW              (gdk_window_object_get_type ())
 #define GDK_WINDOW(object)           (G_TYPE_CHECK_INSTANCE_CAST ((object), GDK_TYPE_WINDOW, GdkWindow))
 #define GDK_WINDOW_CLASS(klass)      (G_TYPE_CHECK_CLASS_CAST ((klass), GDK_TYPE_WINDOW, GdkWindowObjectClass))
@@ -307,6 +364,8 @@ struct _GdkWindowObject
   guint update_and_descendants_freeze_count;
 
   GdkWindowRedirect *redirect;
+  GdkWindowOffscreenChildren *offscreen_children;
+  const GdkOffscreenHooks *offscreen_hooks;
 };
 
 struct _GdkWindowObjectClass
@@ -645,12 +704,30 @@ GdkPointerHooks *gdk_set_pointer_hooks (const GdkPointerHooks *new_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);
+/* Offscreen redirection */
+#ifdef  GDK_COMPILATION
+GType gdk_offscreen_window_get_type (void);
+#endif
+
+GdkWindow *gdk_offscreen_window_new          (GdkWindow     *parent,
+                                              GdkWindowAttr *attributes,
+                                              gint           attributes_mask);
+
+gboolean   gdk_window_is_offscreen           (GdkWindow     *window);
+GdkPixmap *gdk_window_get_offscreen_pixmap   (GdkWindow     *window);
+
+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);
+
+void       gdk_window_set_offscreen_hooks    (GdkWindow               *window,
+                                              const GdkOffscreenHooks *hooks);
 
 #ifndef GDK_DISABLE_DEPRECATED
 #define GDK_ROOT_PARENT()             (gdk_get_default_root_window ())
diff --git a/gdk/quartz/gdkpixmap-quartz.c b/gdk/quartz/gdkpixmap-quartz.c
index e55ff9d..a743480 100644
--- a/gdk/quartz/gdkpixmap-quartz.c
+++ b/gdk/quartz/gdkpixmap-quartz.c
@@ -137,10 +137,10 @@ data_provider_release (void *info, const void *data, size_t size)
 }
 
 GdkPixmap*
-gdk_pixmap_new (GdkDrawable *drawable,
-		gint         width,
-		gint         height,
-		gint         depth)
+_gdk_pixmap_new (GdkDrawable *drawable,
+                 gint         width,
+                 gint         height,
+                 gint         depth)
 {
   GdkPixmap *pixmap;
   GdkDrawableImplQuartz *draw_impl;
@@ -224,10 +224,10 @@ gdk_pixmap_new (GdkDrawable *drawable,
 }
 
 GdkPixmap *
-gdk_bitmap_create_from_data (GdkDrawable *window,
-			     const gchar *data,
-			     gint         width,
-			     gint         height)
+_gdk_bitmap_create_from_data (GdkDrawable *window,
+                              const gchar *data,
+                              gint         width,
+                              gint         height)
 {
   GdkPixmap *pixmap;
   GdkPixmapImplQuartz *impl;
@@ -264,13 +264,13 @@ gdk_bitmap_create_from_data (GdkDrawable *window,
 }
 
 GdkPixmap*
-gdk_pixmap_create_from_data (GdkDrawable    *drawable,
-			     const gchar    *data,
-			     gint            width,
-			     gint            height,
-			     gint            depth,
-			     const GdkColor *fg,
-			     const GdkColor *bg)
+_gdk_pixmap_create_from_data (GdkDrawable    *drawable,
+                              const gchar    *data,
+                              gint            width,
+                              gint            height,
+                              gint            depth,
+                              const GdkColor *fg,
+                              const GdkColor *bg)
 {	
   /* FIXME: Implement */
   return NULL;
diff --git a/gdk/x11/gdkdisplay-x11.c b/gdk/x11/gdkdisplay-x11.c
index 08b77da..b3beacb 100644
--- a/gdk/x11/gdkdisplay-x11.c
+++ b/gdk/x11/gdkdisplay-x11.c
@@ -576,7 +576,7 @@ _gdk_x11_display_is_root_window (GdkDisplay *display,
  */
 void
 gdk_display_pointer_ungrab (GdkDisplay *display,
-			    guint32     time)
+			    guint32     time_)
 {
   Display *xdisplay;
   GdkDisplayX11 *display_x11;
@@ -586,14 +586,21 @@ gdk_display_pointer_ungrab (GdkDisplay *display,
   display_x11 = GDK_DISPLAY_X11 (display);
   xdisplay = GDK_DISPLAY_XDISPLAY (display);
   
-  _gdk_input_ungrab_pointer (display, time);
-  XUngrabPointer (xdisplay, time);
+  _gdk_input_ungrab_pointer (display, time_);
+  XUngrabPointer (xdisplay, time_);
   XFlush (xdisplay);
 
-  if (time == GDK_CURRENT_TIME || 
+  if (time_ == GDK_CURRENT_TIME ||
       display_x11->pointer_xgrab_time == GDK_CURRENT_TIME ||
-      !XSERVER_TIME_IS_LATER (display_x11->pointer_xgrab_time, time))
-    display_x11->pointer_xgrab_window = NULL;
+      !XSERVER_TIME_IS_LATER (display_x11->pointer_xgrab_time, time_))
+    {
+      GdkWindow *xgrab_window = GDK_WINDOW (display_x11->pointer_xgrab_window);
+
+      if (gdk_window_is_offscreen (xgrab_window))
+        _gdk_offscreen_window_unset_pointer_grab (xgrab_window, FALSE, FALSE, time_);
+
+      display_x11->pointer_xgrab_window = NULL;
+    }
 }
 
 /**
@@ -1308,7 +1315,9 @@ gdk_display_store_clipboard (GdkDisplay    *display,
 {
   GdkDisplayX11 *display_x11 = GDK_DISPLAY_X11 (display);
   Atom clipboard_manager, save_targets;
-  
+
+  g_return_if_fail (!gdk_window_is_offscreen (clipboard_window));
+
   clipboard_manager = gdk_x11_get_xatom_by_name_for_display (display, "CLIPBOARD_MANAGER");
   save_targets = gdk_x11_get_xatom_by_name_for_display (display, "SAVE_TARGETS");
 
diff --git a/gdk/x11/gdkdisplay-x11.h b/gdk/x11/gdkdisplay-x11.h
index ffd0461..d515a74 100644
--- a/gdk/x11/gdkdisplay-x11.h
+++ b/gdk/x11/gdkdisplay-x11.h
@@ -98,12 +98,14 @@ struct _GdkDisplayX11
    * window is NULL, then the other associated fields are ignored
    */
   GdkWindowObject *pointer_xgrab_window;
+  GdkWindowObject *pointer_xgrab_native_window;
   gulong pointer_xgrab_serial;
   gboolean pointer_xgrab_owner_events;
   gboolean pointer_xgrab_implicit;
   guint32 pointer_xgrab_time;
 
   GdkWindowObject *keyboard_xgrab_window;
+  GdkWindowObject *keyboard_xgrab_native_window;
   gulong keyboard_xgrab_serial;
   gboolean keyboard_xgrab_owner_events;
   guint32 keyboard_xgrab_time;
@@ -169,6 +171,9 @@ struct _GdkDisplayX11
 
   /* Alpha mask picture format */
   XRenderPictFormat *mask_format;
+
+  /* The offscreen window that has the pointer in it (if any) */
+  GdkWindow *active_offscreen_window;
 };
 
 struct _GdkDisplayX11Class
diff --git a/gdk/x11/gdkdnd-x11.c b/gdk/x11/gdkdnd-x11.c
index 4ff2276..f57b7fc 100644
--- a/gdk/x11/gdkdnd-x11.c
+++ b/gdk/x11/gdkdnd-x11.c
@@ -2987,6 +2987,7 @@ gdk_drag_begin (GdkWindow     *window,
   GdkDragContext *new_context;
   
   g_return_val_if_fail (window != NULL, NULL);
+  g_return_val_if_fail (!gdk_window_is_offscreen (window), NULL);
 
   new_context = gdk_drag_context_new ();
   new_context->is_source = TRUE;
@@ -3187,6 +3188,12 @@ gdk_drag_find_window_for_screen (GdkDragContext  *context,
 
   g_return_if_fail (context != NULL);
 
+  if (gdk_window_is_offscreen (drag_window))
+    {
+      *dest_window = NULL;
+      return;
+    }
+
   display = GDK_WINDOW_DISPLAY (context->source_window);
 
   window_cache = drag_context_find_window_cache (context, screen);
@@ -3262,6 +3269,7 @@ gdk_drag_motion (GdkDragContext *context,
   GdkDragContextPrivateX11 *private = PRIVATE_DATA (context);
 
   g_return_val_if_fail (context != NULL, FALSE);
+  g_return_val_if_fail (dest_window == NULL || !gdk_window_is_offscreen (dest_window), FALSE);
 
   private->old_actions = context->actions;
   context->actions = possible_actions;
diff --git a/gdk/x11/gdkdrawable-x11.c b/gdk/x11/gdkdrawable-x11.c
index a19c7a2..6ec0c97 100644
--- a/gdk/x11/gdkdrawable-x11.c
+++ b/gdk/x11/gdkdrawable-x11.c
@@ -870,7 +870,15 @@ gdk_x11_drawable_get_xid (GdkDrawable *drawable)
   GdkDrawable *impl;
   
   if (GDK_IS_WINDOW (drawable))
-    impl = ((GdkPixmapObject *)drawable)->impl;
+    {
+      if (gdk_window_is_offscreen (GDK_WINDOW (drawable)))
+        {
+          g_warning (G_STRLOC " drawable is an offscreen window");
+          return None;
+        }
+
+      impl = ((GdkPixmapObject *)drawable)->impl;
+    }
   else if (GDK_IS_PIXMAP (drawable))
     impl = ((GdkPixmapObject *)drawable)->impl;
   else
diff --git a/gdk/x11/gdkevents-x11.c b/gdk/x11/gdkevents-x11.c
index 29efc7e..56bafc0 100644
--- a/gdk/x11/gdkevents-x11.c
+++ b/gdk/x11/gdkevents-x11.c
@@ -313,7 +313,9 @@ gdk_event_get_graphics_expose (GdkWindow *window)
   GdkEvent *event;
   
   g_return_val_if_fail (window != NULL, NULL);
-  
+
+  window = _gdk_window_get_offscreen_event_window (window);
+
   XIfEvent (GDK_WINDOW_XDISPLAY (window), &xevent, 
 	    graphics_expose_predicate, (XPointer) window);
   
@@ -942,6 +944,14 @@ gdk_event_translate (GdkDisplay *display,
     
   if (window != NULL)
     {
+      /* Rewrite keyboard grabs to offscreen windows */
+      if ((xevent->type == KeyPress || xevent->type == KeyRelease) &&
+          window_private == display_x11->keyboard_xgrab_native_window)
+        {
+          window_private = display_x11->keyboard_xgrab_window;
+          window = (GdkWindow *) window_private;
+        }
+
       window_impl = GDK_WINDOW_IMPL_X11 (window_private->impl);
       
       /* Move key events on focus window to the real toplevel, and
@@ -2299,6 +2309,7 @@ _gdk_events_queue (GdkDisplay *display)
       if (gdk_event_translate (display, event, &xevent, FALSE))
 	{
 	  ((GdkEventPrivate *)event)->flags &= ~GDK_EVENT_PENDING;
+          _gdk_event_rewrite_for_offscreen (display, node, event);
 	}
       else
 	{
diff --git a/gdk/x11/gdkgeometry-x11.c b/gdk/x11/gdkgeometry-x11.c
index 9271f09..f986e30 100644
--- a/gdk/x11/gdkgeometry-x11.c
+++ b/gdk/x11/gdkgeometry-x11.c
@@ -855,6 +855,9 @@ gdk_window_premove (GdkWindow          *window,
   gint d_xoffset, d_yoffset;
   GdkWindowParentPos this_pos;
 
+  if (gdk_window_is_offscreen (window))
+    return;
+
   obj = (GdkWindowObject *) window;
   impl = GDK_WINDOW_IMPL_X11 (obj->impl);
   
@@ -895,6 +898,9 @@ gdk_window_postmove (GdkWindow          *window,
   gint d_xoffset, d_yoffset;
   GdkWindowParentPos this_pos;
 
+  if (gdk_window_is_offscreen (window))
+    return;
+
   obj = (GdkWindowObject *) window;
   impl = GDK_WINDOW_IMPL_X11 (obj->impl);
   
diff --git a/gdk/x11/gdkinput.c b/gdk/x11/gdkinput.c
index 98840bb..b4a7ba4 100644
--- a/gdk/x11/gdkinput.c
+++ b/gdk/x11/gdkinput.c
@@ -207,7 +207,7 @@ gdk_device_get_history  (GdkDevice         *device,
   int tmp_n_events = 0;
   int i;
 
-  g_return_val_if_fail (GDK_IS_WINDOW (window), FALSE);
+  g_return_val_if_fail (GDK_IS_ONSCREEN_WINDOW (window), FALSE);
 
   if (GDK_WINDOW_DESTROYED (window))
     /* Nothing */ ;
@@ -303,7 +303,7 @@ gdk_input_set_extension_events (GdkWindow *window, gint mask,
   GdkDisplayX11 *display_x11;
 
   g_return_if_fail (window != NULL);
-  g_return_if_fail (GDK_IS_WINDOW (window));
+  g_return_if_fail (GDK_IS_ONSCREEN_WINDOW (window));
 
   window_private = (GdkWindowObject*) window;
   display_x11 = GDK_DISPLAY_X11 (GDK_WINDOW_DISPLAY (window));
diff --git a/gdk/x11/gdkmain-x11.c b/gdk/x11/gdkmain-x11.c
index 7bede22..37eb385 100644
--- a/gdk/x11/gdkmain-x11.c
+++ b/gdk/x11/gdkmain-x11.c
@@ -191,6 +191,7 @@ gdk_pointer_grab (GdkWindow *	  window,
 {
   gint return_val;
   GdkCursorPrivate *cursor_private;
+  GdkWindow *native;
   GdkDisplayX11 *display_x11;
   guint xevent_mask;
   Window xwindow;
@@ -202,13 +203,18 @@ gdk_pointer_grab (GdkWindow *	  window,
   g_return_val_if_fail (window != NULL, 0);
   g_return_val_if_fail (GDK_IS_WINDOW (window), 0);
   g_return_val_if_fail (confine_to == NULL || GDK_IS_WINDOW (confine_to), 0);
-  
-  display_x11 = GDK_DISPLAY_X11 (GDK_WINDOW_DISPLAY (window));
+
+  native = _gdk_window_get_offscreen_event_window (window);
+
+  if (confine_to)
+    confine_to = _gdk_window_get_offscreen_event_window (confine_to);
+
+  display_x11 = GDK_DISPLAY_X11 (GDK_WINDOW_DISPLAY (native));
 
   cursor_private = (GdkCursorPrivate*) cursor;
   
-  xwindow = GDK_WINDOW_XID (window);
-  serial = NextRequest (GDK_WINDOW_XDISPLAY (window));
+  xwindow = GDK_WINDOW_XID (native);
+  serial = NextRequest (GDK_WINDOW_XDISPLAY (native));
   
   if (!confine_to || GDK_WINDOW_DESTROYED (confine_to))
     xconfine_to = None;
@@ -230,7 +236,7 @@ gdk_pointer_grab (GdkWindow *	  window,
 	xevent_mask |= _gdk_event_mask_table[i];
     }
   
-  return_val = _gdk_input_grab_pointer (window,
+  return_val = _gdk_input_grab_pointer (native,
 					owner_events,
 					event_mask,
 					confine_to,
@@ -239,14 +245,14 @@ gdk_pointer_grab (GdkWindow *	  window,
   if (return_val == GrabSuccess || 
       G_UNLIKELY (!display_x11->trusted_client && return_val == AlreadyGrabbed))
     {
-      if (!GDK_WINDOW_DESTROYED (window))
+      if (!GDK_WINDOW_DESTROYED (native))
 	{
 #ifdef G_ENABLE_DEBUG
 	  if (_gdk_debug_flags & GDK_DEBUG_NOGRABS)
 	    return_val = GrabSuccess;
 	  else
 #endif
-	    return_val = XGrabPointer (GDK_WINDOW_XDISPLAY (window),
+	    return_val = XGrabPointer (GDK_WINDOW_XDISPLAY (native),
 				       xwindow,
 				       owner_events,
 				       xevent_mask,
@@ -263,11 +269,25 @@ gdk_pointer_grab (GdkWindow *	  window,
     {
       if (display_x11->pointer_xgrab_window != NULL &&
 	  display_x11->pointer_xgrab_window != (GdkWindowObject *)window)
-	generate_grab_broken_event (GDK_WINDOW (display_x11->pointer_xgrab_window),
-				    FALSE, display_x11->pointer_xgrab_implicit,
-				    window);
+        {
+          if (gdk_window_is_offscreen (GDK_WINDOW (display_x11->pointer_xgrab_window)))
+            _gdk_offscreen_window_unset_pointer_grab (GDK_WINDOW (display_x11->pointer_xgrab_window),
+                                                      TRUE, FALSE, time);
+
+	  generate_grab_broken_event (GDK_WINDOW (display_x11->pointer_xgrab_window),
+                                      FALSE, display_x11->pointer_xgrab_implicit,
+                                      window);
+        }
+
+      if (gdk_window_is_offscreen (window))
+        _gdk_offscreen_window_set_pointer_grab (GDK_WINDOW (display_x11->pointer_xgrab_window),
+                                                GDK_WINDOW (display_x11->pointer_xgrab_native_window),
+                                                window,
+                                                owner_events, event_mask,
+                                                time);
 
       display_x11->pointer_xgrab_window = (GdkWindowObject *)window;
+      display_x11->pointer_xgrab_native_window = (GdkWindowObject *)native;
       display_x11->pointer_xgrab_serial = serial;
       display_x11->pointer_xgrab_owner_events = owner_events;
       display_x11->pointer_xgrab_time = time;
@@ -342,23 +362,26 @@ gdk_keyboard_grab (GdkWindow *	   window,
   gint return_val;
   unsigned long serial;
   GdkDisplayX11 *display_x11;
-  
+  GdkWindow *native;
+
   g_return_val_if_fail (window != NULL, 0);
   g_return_val_if_fail (GDK_IS_WINDOW (window), 0);
-  
-  display_x11 = GDK_DISPLAY_X11 (GDK_WINDOW_DISPLAY (window));
 
-  serial = NextRequest (GDK_WINDOW_XDISPLAY (window));
+  native = _gdk_window_get_offscreen_event_window (window);
 
-  if (!GDK_WINDOW_DESTROYED (window))
+  display_x11 = GDK_DISPLAY_X11 (GDK_WINDOW_DISPLAY (native));
+
+  serial = NextRequest (GDK_WINDOW_XDISPLAY (native));
+
+  if (!GDK_WINDOW_DESTROYED (native))
     {
 #ifdef G_ENABLE_DEBUG
       if (_gdk_debug_flags & GDK_DEBUG_NOGRABS)
 	return_val = GrabSuccess;
       else
 #endif
-	return_val = XGrabKeyboard (GDK_WINDOW_XDISPLAY (window),
-				    GDK_WINDOW_XID (window),
+	return_val = XGrabKeyboard (GDK_WINDOW_XDISPLAY (native),
+				    GDK_WINDOW_XID (native),
 				    owner_events,
 				    GrabModeAsync, GrabModeAsync,
 				    time);
@@ -378,6 +401,7 @@ gdk_keyboard_grab (GdkWindow *	   window,
 				    TRUE, FALSE, window);
       
       display_x11->keyboard_xgrab_window = (GdkWindowObject *)window;
+      display_x11->keyboard_xgrab_native_window = (GdkWindowObject *)native;
       display_x11->keyboard_xgrab_serial = serial;
       display_x11->keyboard_xgrab_owner_events = owner_events;
       display_x11->keyboard_xgrab_time = time;      
@@ -386,6 +410,19 @@ gdk_keyboard_grab (GdkWindow *	   window,
   return gdk_x11_convert_grab_status (return_val);
 }
 
+void
+_gdk_windowing_grab_broken (GdkDisplay *display)
+{
+  GdkDisplayX11 *display_x11 = GDK_DISPLAY_X11 (display);
+
+  generate_grab_broken_event (GDK_WINDOW (display_x11->pointer_xgrab_window),
+                              FALSE,
+                              display_x11->pointer_xgrab_implicit,
+                              NULL);
+
+  display_x11->pointer_xgrab_window = NULL;
+}
+
 /**
  * gdk_keyboard_grab_info_libgtk_only:
  * @display: the display for which to get the grab information
@@ -443,13 +480,18 @@ _gdk_xgrab_check_unmap (GdkWindow *window,
       serial >= display_x11->pointer_xgrab_serial)
     {
       GdkWindowObject *private = GDK_WINDOW_OBJECT (window);
-      GdkWindowObject *tmp = display_x11->pointer_xgrab_window;
+      GdkWindowObject *tmp = display_x11->pointer_xgrab_native_window;
 
       while (tmp && tmp != private)
 	tmp = tmp->parent;
 
       if (tmp)
-	{	  
+        {
+          if (gdk_window_is_offscreen (GDK_WINDOW (display_x11->pointer_xgrab_window)))
+            _gdk_offscreen_window_unset_pointer_grab (GDK_WINDOW (display_x11->pointer_xgrab_window),
+                                                      FALSE, FALSE,
+                                                      GDK_CURRENT_TIME);
+
 	  generate_grab_broken_event (GDK_WINDOW (display_x11->pointer_xgrab_window),
 				      FALSE, display_x11->pointer_xgrab_implicit, 
 				      NULL);
@@ -488,15 +530,22 @@ _gdk_xgrab_check_destroy (GdkWindow *window)
 {
   GdkDisplayX11 *display_x11 = GDK_DISPLAY_X11 (gdk_drawable_get_display (window));
   
-  if ((GdkWindowObject *)window == display_x11->pointer_xgrab_window)
+  if ((GdkWindowObject *)window == display_x11->pointer_xgrab_native_window &&
+      display_x11->pointer_xgrab_window != NULL)
     {
+      if (gdk_window_is_offscreen (GDK_WINDOW (display_x11->pointer_xgrab_window)))
+        _gdk_offscreen_window_unset_pointer_grab (GDK_WINDOW (display_x11->pointer_xgrab_window),
+                                                  FALSE, FALSE,
+                                                  GDK_CURRENT_TIME);
+
       generate_grab_broken_event (GDK_WINDOW (display_x11->pointer_xgrab_window),
 				  FALSE, display_x11->pointer_xgrab_implicit,
 				  NULL);
       display_x11->pointer_xgrab_window = NULL;
     }
 
-  if ((GdkWindowObject *)window ==  display_x11->keyboard_xgrab_window)
+  if ((GdkWindowObject *)window == display_x11->keyboard_xgrab_native_window &&
+      display_x11->keyboard_xgrab_window != NULL)
     {
       generate_grab_broken_event (GDK_WINDOW (display_x11->keyboard_xgrab_window),
 				  TRUE, FALSE, NULL);
@@ -530,6 +579,7 @@ _gdk_xgrab_check_button_event (GdkWindow *window,
       if (!display_x11->pointer_xgrab_window)
 	{
 	  display_x11->pointer_xgrab_window = (GdkWindowObject *)window;
+          display_x11->pointer_xgrab_native_window = (GdkWindowObject *)window;
 	  display_x11->pointer_xgrab_serial = xevent->xany.serial;
 	  display_x11->pointer_xgrab_owner_events = FALSE;
 	  display_x11->pointer_xgrab_time = xevent->xbutton.time;
@@ -541,6 +591,11 @@ _gdk_xgrab_check_button_event (GdkWindow *window,
 	  display_x11->pointer_xgrab_implicit &&
 	  (xevent->xbutton.state & GDK_ANY_BUTTON_MASK & ~(GDK_BUTTON1_MASK << (xevent->xbutton.button - 1))) == 0)
 	{
+          if (gdk_window_is_offscreen (GDK_WINDOW (display_x11->pointer_xgrab_window)))
+            _gdk_offscreen_window_unset_pointer_grab (GDK_WINDOW (display_x11->pointer_xgrab_window),
+                                                      FALSE, TRUE,
+                                                      xevent->xbutton.time);
+
 	  display_x11->pointer_xgrab_window = NULL;
 	}
       break;
@@ -550,6 +605,27 @@ _gdk_xgrab_check_button_event (GdkWindow *window,
 }
 
 void
+_gdk_windowing_update_implicit_grab (GdkWindow *old_grab,
+                                     GdkWindow *new_grab,
+                                     guint      event_mask,
+                                     guint32    time)
+{
+  GdkDisplayX11 *display_x11 = GDK_DISPLAY_X11 (gdk_drawable_get_display (old_grab));
+
+  if (display_x11->pointer_xgrab_window == GDK_WINDOW_OBJECT (old_grab) &&
+      display_x11->pointer_xgrab_implicit)
+    {
+      if (gdk_window_is_offscreen (new_grab))
+        _gdk_offscreen_window_set_pointer_grab (GDK_WINDOW (display_x11->pointer_xgrab_window),
+                                                GDK_WINDOW (display_x11->pointer_xgrab_native_window),
+                                                new_grab, FALSE,
+                                                event_mask, time);
+
+     display_x11->pointer_xgrab_window = GDK_WINDOW_OBJECT (new_grab);
+   }
+}
+
+void
 _gdk_windowing_display_set_sm_client_id (GdkDisplay  *display,
 					 const gchar *sm_client_id)
 {
diff --git a/gdk/x11/gdkpixmap-x11.c b/gdk/x11/gdkpixmap-x11.c
index 7984b5a..1fcaf5a 100644
--- a/gdk/x11/gdkpixmap-x11.c
+++ b/gdk/x11/gdkpixmap-x11.c
@@ -137,10 +137,10 @@ gdk_pixmap_impl_x11_get_size   (GdkDrawable *drawable,
 }
 
 GdkPixmap*
-gdk_pixmap_new (GdkDrawable *drawable,
-		gint         width,
-		gint         height,
-		gint         depth)
+_gdk_pixmap_new (GdkDrawable *drawable,
+                 gint         width,
+                 gint         height,
+                 gint         depth)
 {
   GdkPixmap *pixmap;
   GdkDrawableImplX11 *draw_impl;
@@ -194,10 +194,10 @@ gdk_pixmap_new (GdkDrawable *drawable,
 }
 
 GdkPixmap *
-gdk_bitmap_create_from_data (GdkDrawable *drawable,
-			     const gchar *data,
-			     gint         width,
-			     gint         height)
+_gdk_bitmap_create_from_data (GdkDrawable *drawable,
+                              const gchar *data,
+                              gint         width,
+                              gint         height)
 {
   GdkPixmap *pixmap;
   GdkDrawableImplX11 *draw_impl;
@@ -238,13 +238,13 @@ gdk_bitmap_create_from_data (GdkDrawable *drawable,
 }
 
 GdkPixmap*
-gdk_pixmap_create_from_data (GdkDrawable    *drawable,
-			     const gchar    *data,
-			     gint            width,
-			     gint            height,
-			     gint            depth,
-			     const GdkColor *fg,
-			     const GdkColor *bg)
+_gdk_pixmap_create_from_data (GdkDrawable    *drawable,
+			      const gchar    *data,
+			      gint            width,
+			      gint            height,
+			      gint            depth,
+			      const GdkColor *fg,
+			      const GdkColor *bg)
 {
   GdkPixmap *pixmap;
   GdkDrawableImplX11 *draw_impl;
diff --git a/gdk/x11/gdkprivate-x11.h b/gdk/x11/gdkprivate-x11.h
index 1ae97c1..f79a1e8 100644
--- a/gdk/x11/gdkprivate-x11.h
+++ b/gdk/x11/gdkprivate-x11.h
@@ -215,5 +215,6 @@ extern gboolean          _gdk_synchronize;
 #define GDK_WINDOW_DISPLAY(win)       (GDK_SCREEN_X11 (GDK_WINDOW_SCREEN (win))->display)
 #define GDK_WINDOW_XROOTWIN(win)      (GDK_SCREEN_X11 (GDK_WINDOW_SCREEN (win))->xroot_window)
 #define GDK_GC_DISPLAY(gc)            (GDK_SCREEN_DISPLAY (GDK_GC_X11(gc)->screen))
+#define GDK_IS_ONSCREEN_WINDOW(win)   (GDK_IS_WINDOW (win) && !gdk_window_is_offscreen ((win)))
 
 #endif /* __GDK_PRIVATE_X11_H__ */
diff --git a/gdk/x11/gdkproperty-x11.c b/gdk/x11/gdkproperty-x11.c
index 36b8e9f..05f934b 100644
--- a/gdk/x11/gdkproperty-x11.c
+++ b/gdk/x11/gdkproperty-x11.c
@@ -541,7 +541,7 @@ gdk_property_get (GdkWindow   *window,
   Atom xtype;
   int res;
 
-  g_return_val_if_fail (!window || GDK_IS_WINDOW (window), FALSE);
+  g_return_val_if_fail (!window || GDK_IS_ONSCREEN_WINDOW (window), FALSE);
 
   if (!window)
     {
@@ -550,6 +550,8 @@ gdk_property_get (GdkWindow   *window,
       
       GDK_NOTE (MULTIHEAD, g_message ("gdk_property_get(): window is NULL\n"));
     }
+  else if (gdk_window_is_offscreen (window))
+    return FALSE;
 
   if (GDK_WINDOW_DESTROYED (window))
     return FALSE;
@@ -675,7 +677,7 @@ gdk_property_change (GdkWindow    *window,
   Atom xproperty;
   Atom xtype;
 
-  g_return_if_fail (!window || GDK_IS_WINDOW (window));
+  g_return_if_fail (!window || GDK_IS_ONSCREEN_WINDOW (window));
 
   if (!window)
     {
@@ -686,7 +688,8 @@ gdk_property_change (GdkWindow    *window,
       
       GDK_NOTE (MULTIHEAD, g_message ("gdk_property_change(): window is NULL\n"));
     }
-
+  else if (gdk_window_is_offscreen (window))
+    return;
 
   if (GDK_WINDOW_DESTROYED (window))
     return;
@@ -726,7 +729,7 @@ void
 gdk_property_delete (GdkWindow *window,
 		     GdkAtom    property)
 {
-  g_return_if_fail (!window || GDK_IS_WINDOW (window));
+  g_return_if_fail (!window || GDK_IS_ONSCREEN_WINDOW (window));
 
   if (!window)
     {
@@ -736,6 +739,8 @@ gdk_property_delete (GdkWindow *window,
       GDK_NOTE (MULTIHEAD, 
 		g_message ("gdk_property_delete(): window is NULL\n"));
     }
+  else if (gdk_window_is_offscreen (window))
+    return;
 
   if (GDK_WINDOW_DESTROYED (window))
     return;
diff --git a/gdk/x11/gdkselection-x11.c b/gdk/x11/gdkselection-x11.c
index 76ec3ee..d2159ac 100644
--- a/gdk/x11/gdkselection-x11.c
+++ b/gdk/x11/gdkselection-x11.c
@@ -141,7 +141,7 @@ gdk_selection_owner_set_for_display (GdkDisplay *display,
 
   if (owner) 
     {
-      if (GDK_WINDOW_DESTROYED (owner))
+      if (GDK_WINDOW_DESTROYED (owner) || gdk_window_is_offscreen (owner))
 	return FALSE;
       
       xdisplay = GDK_WINDOW_XDISPLAY (owner);
@@ -231,7 +231,7 @@ gdk_selection_convert (GdkWindow *requestor,
 
   g_return_if_fail (selection != GDK_NONE);
   
-  if (GDK_WINDOW_DESTROYED (requestor))
+  if (GDK_WINDOW_DESTROYED (requestor) || gdk_window_is_offscreen (requestor))
     return;
 
   display = GDK_WINDOW_DISPLAY (requestor);
@@ -279,10 +279,11 @@ gdk_selection_property_get (GdkWindow  *requestor,
 
   g_return_val_if_fail (requestor != NULL, 0);
   g_return_val_if_fail (GDK_IS_WINDOW (requestor), 0);
+  g_return_val_if_fail (GDK_IS_ONSCREEN_WINDOW (requestor), 0);
   
   display = GDK_WINDOW_DISPLAY (requestor);
 
-  if (GDK_WINDOW_DESTROYED (requestor))
+  if (GDK_WINDOW_DESTROYED (requestor) || gdk_window_is_offscreen (requestor))
     goto err;
 
   t = NULL;
diff --git a/gdk/x11/gdkwindow-x11.c b/gdk/x11/gdkwindow-x11.c
index eb2ae6c..32ceacd 100644
--- a/gdk/x11/gdkwindow-x11.c
+++ b/gdk/x11/gdkwindow-x11.c
@@ -300,7 +300,8 @@ _gdk_x11_window_tmp_unset_bg (GdkWindow *window,
 
   if (private->input_only || private->destroyed ||
       (private->window_type != GDK_WINDOW_ROOT &&
-       !GDK_WINDOW_IS_MAPPED (window)))
+       !GDK_WINDOW_IS_MAPPED (window)) ||
+       gdk_window_is_offscreen (window))
     {
       return;
     }
@@ -332,7 +333,8 @@ _gdk_x11_window_tmp_reset_bg (GdkWindow *window,
 
   if (private->input_only || private->destroyed ||
       (private->window_type != GDK_WINDOW_ROOT &&
-       !GDK_WINDOW_IS_MAPPED (window)))
+       !GDK_WINDOW_IS_MAPPED (window)) ||
+       gdk_window_is_offscreen (window))
     {
       return;
     }
@@ -1215,7 +1217,7 @@ gdk_window_destroy_notify (GdkWindow *window)
 {
   GdkWindowImplX11 *window_impl;
 
-  g_return_if_fail (GDK_IS_WINDOW (window));
+  g_return_if_fail (GDK_IS_ONSCREEN_WINDOW (window));
   
   window_impl = GDK_WINDOW_IMPL_X11 (((GdkWindowObject *)window)->impl);
 
@@ -1730,7 +1732,9 @@ gdk_window_x11_move_resize (GdkWindow *window,
                             gint       height)
 {
   if (with_move && (width < 0 && height < 0))
-    window_x11_move (window, x, y);
+    {
+      window_x11_move (window, x, y);
+    }
   else
     {
       if (with_move)
@@ -1752,6 +1756,12 @@ gdk_window_x11_reparent (GdkWindow *window,
   GdkWindowImplX11 *impl;
   gboolean was_toplevel;
 
+  if (new_parent && gdk_window_is_offscreen (new_parent))
+    {
+      g_warning ("Can't reparent a X11 window to an offscreen parent");
+      return FALSE;
+    }
+
   if (!new_parent)
     new_parent = gdk_screen_get_root_window (GDK_WINDOW_SCREEN (window));
 
@@ -1960,9 +1970,9 @@ gdk_window_focus (GdkWindow *window,
 {
   GdkDisplay *display;
   
-  g_return_if_fail (GDK_IS_WINDOW (window));
+  g_return_if_fail (GDK_IS_ONSCREEN_WINDOW (window));
 
-  if (GDK_WINDOW_DESTROYED (window))
+  if (GDK_WINDOW_DESTROYED (window) || gdk_window_is_offscreen (window))
     return;
 
   display = GDK_WINDOW_DISPLAY (window);
@@ -2032,9 +2042,9 @@ gdk_window_set_hints (GdkWindow *window,
 {
   XSizeHints size_hints;
   
-  g_return_if_fail (GDK_IS_WINDOW (window));
+  g_return_if_fail (GDK_IS_ONSCREEN_WINDOW (window));
   
-  if (GDK_WINDOW_DESTROYED (window))
+  if (GDK_WINDOW_DESTROYED (window) || gdk_window_is_offscreen (window))
     return;
   
   size_hints.flags = 0;
@@ -2087,9 +2097,9 @@ gdk_window_set_type_hint (GdkWindow        *window,
   GdkDisplay *display;
   Atom atom;
   
-  g_return_if_fail (GDK_IS_WINDOW (window));
+  g_return_if_fail (GDK_IS_ONSCREEN_WINDOW (window));
   
-  if (GDK_WINDOW_DESTROYED (window))
+  if (GDK_WINDOW_DESTROYED (window) || gdk_window_is_offscreen (window))
     return;
 
   display = gdk_drawable_get_display (window);
@@ -2273,9 +2283,9 @@ gdk_window_set_modal_hint (GdkWindow *window,
 {
   GdkWindowObject *private;
 
-  g_return_if_fail (GDK_IS_WINDOW (window));
+  g_return_if_fail (GDK_IS_ONSCREEN_WINDOW (window));
   
-  if (GDK_WINDOW_DESTROYED (window))
+  if (GDK_WINDOW_DESTROYED (window) || gdk_window_is_offscreen (window))
     return;
 
   private = (GdkWindowObject*) window;
@@ -2308,10 +2318,10 @@ gdk_window_set_skip_taskbar_hint (GdkWindow *window,
 {
   GdkToplevelX11 *toplevel;
   
-  g_return_if_fail (GDK_IS_WINDOW (window));
+  g_return_if_fail (GDK_IS_ONSCREEN_WINDOW (window));
   g_return_if_fail (GDK_WINDOW_TYPE (window) != GDK_WINDOW_CHILD);
   
-  if (GDK_WINDOW_DESTROYED (window))
+  if (GDK_WINDOW_DESTROYED (window) || gdk_window_is_offscreen (window))
     return;
 
   toplevel = _gdk_x11_window_get_toplevel (window);
@@ -2345,10 +2355,10 @@ gdk_window_set_skip_pager_hint (GdkWindow *window,
 {
   GdkToplevelX11 *toplevel;
     
-  g_return_if_fail (GDK_IS_WINDOW (window));
+  g_return_if_fail (GDK_IS_ONSCREEN_WINDOW (window));
   g_return_if_fail (GDK_WINDOW_TYPE (window) != GDK_WINDOW_CHILD);
   
-  if (GDK_WINDOW_DESTROYED (window))
+  if (GDK_WINDOW_DESTROYED (window) || gdk_window_is_offscreen (window))
     return;
 
   toplevel = _gdk_x11_window_get_toplevel (window);
@@ -2376,10 +2386,10 @@ gdk_window_set_urgency_hint (GdkWindow *window,
 {
   GdkToplevelX11 *toplevel;
     
-  g_return_if_fail (GDK_IS_WINDOW (window));
+  g_return_if_fail (GDK_IS_ONSCREEN_WINDOW (window));
   g_return_if_fail (GDK_WINDOW_TYPE (window) != GDK_WINDOW_CHILD);
   
-  if (GDK_WINDOW_DESTROYED (window))
+  if (GDK_WINDOW_DESTROYED (window) || gdk_window_is_offscreen (window))
     return;
 
   toplevel = _gdk_x11_window_get_toplevel (window);
@@ -2424,9 +2434,9 @@ gdk_window_set_geometry_hints (GdkWindow         *window,
 {
   XSizeHints size_hints;
   
-  g_return_if_fail (GDK_IS_WINDOW (window));
+  g_return_if_fail (GDK_IS_ONSCREEN_WINDOW (window));
   
-  if (GDK_WINDOW_DESTROYED (window))
+  if (GDK_WINDOW_DESTROYED (window) || gdk_window_is_offscreen (window))
     return;
   
   size_hints.flags = 0;
@@ -2688,10 +2698,10 @@ gdk_window_set_title (GdkWindow   *window,
   Display *xdisplay;
   Window xwindow;
   
-  g_return_if_fail (GDK_IS_WINDOW (window));
+  g_return_if_fail (GDK_IS_ONSCREEN_WINDOW (window));
   g_return_if_fail (title != NULL);
 
-  if (GDK_WINDOW_DESTROYED (window))
+  if (GDK_WINDOW_DESTROYED (window) || gdk_window_is_offscreen (window))
     return;
   
   display = gdk_drawable_get_display (window);
@@ -2738,7 +2748,7 @@ gdk_window_set_role (GdkWindow   *window,
 {
   GdkDisplay *display;
   
-  g_return_if_fail (GDK_IS_WINDOW (window));
+  g_return_if_fail (GDK_IS_ONSCREEN_WINDOW (window));
 
   display = gdk_drawable_get_display (window);
 
@@ -2806,7 +2816,7 @@ void
 gdk_window_set_transient_for (GdkWindow *window, 
 			      GdkWindow *parent)
 {
-  g_return_if_fail (GDK_IS_WINDOW (window));
+  g_return_if_fail (GDK_IS_ONSCREEN_WINDOW (window));
   
   if (!GDK_WINDOW_DESTROYED (window) && !GDK_WINDOW_DESTROYED (parent))
     XSetTransientForHint (GDK_WINDOW_XDISPLAY (window), 
@@ -3026,7 +3036,7 @@ gdk_window_get_deskrelative_origin (GdkWindow *window,
   gulong number_return, bytes_after_return;
   guchar *data_return;
   
-  g_return_val_if_fail (GDK_IS_WINDOW (window), FALSE);
+  g_return_val_if_fail (GDK_IS_ONSCREEN_WINDOW (window), FALSE);
   
   if (!GDK_WINDOW_DESTROYED (window))
     {
@@ -3092,7 +3102,7 @@ gdk_window_get_root_origin (GdkWindow *window,
 {
   GdkRectangle rect;
 
-  g_return_if_fail (GDK_IS_WINDOW (window));
+  g_return_if_fail (GDK_IS_ONSCREEN_WINDOW (window));
 
   gdk_window_get_frame_extents (window, &rect);
 
@@ -3138,7 +3148,7 @@ gdk_window_get_frame_extents (GdkWindow    *window,
   gint wx, wy;
   gboolean got_frame_extents = FALSE;
   
-  g_return_if_fail (GDK_IS_WINDOW (window));
+  g_return_if_fail (GDK_IS_ONSCREEN_WINDOW (window));
   g_return_if_fail (rect != NULL);
   
   private = (GdkWindowObject*) window;
@@ -3148,7 +3158,7 @@ gdk_window_get_frame_extents (GdkWindow    *window,
   rect->width = 1;
   rect->height = 1;
   
-  if (GDK_WINDOW_DESTROYED (window))
+  if (GDK_WINDOW_DESTROYED (window) || gdk_window_is_offscreen (window))
     return;
   
   while (private->parent && ((GdkWindowObject*) private->parent)->parent)
@@ -3850,7 +3860,7 @@ gdk_window_set_override_redirect (GdkWindow *window,
 {
   XSetWindowAttributes attr;
   
-  g_return_if_fail (GDK_IS_WINDOW (window));
+  g_return_if_fail (GDK_IS_ONSCREEN_WINDOW (window));
 
   if (!GDK_WINDOW_DESTROYED (window))
     {
@@ -3886,7 +3896,7 @@ gdk_window_set_accept_focus (GdkWindow *window,
 {
   GdkWindowObject *private;
 
-  g_return_if_fail (GDK_IS_WINDOW (window));
+  g_return_if_fail (GDK_IS_ONSCREEN_WINDOW (window));
 
   private = (GdkWindowObject *)window;  
   
@@ -3923,7 +3933,7 @@ gdk_window_set_focus_on_map (GdkWindow *window,
 {
   GdkWindowObject *private;
 
-  g_return_if_fail (GDK_IS_WINDOW (window));
+  g_return_if_fail (GDK_IS_ONSCREEN_WINDOW (window));
 
   private = (GdkWindowObject *)window;  
   
@@ -3968,9 +3978,9 @@ gdk_x11_window_set_user_time (GdkWindow *window,
   glong timestamp_long = (glong)timestamp;
   Window xid;
 
-  g_return_if_fail (GDK_IS_WINDOW (window));
+  g_return_if_fail (GDK_IS_ONSCREEN_WINDOW (window));
 
-  if (GDK_WINDOW_DESTROYED (window))
+  if (GDK_WINDOW_DESTROYED (window) || gdk_window_is_offscreen (window))
     return;
 
   display = gdk_drawable_get_display (window);
@@ -4038,9 +4048,9 @@ gdk_window_set_icon_list (GdkWindow *window,
   GdkDisplay *display;
   gint n;
   
-  g_return_if_fail (GDK_IS_WINDOW (window));
+  g_return_if_fail (GDK_IS_ONSCREEN_WINDOW (window));
 
-  if (GDK_WINDOW_DESTROYED (window))
+  if (GDK_WINDOW_DESTROYED (window) || gdk_window_is_offscreen (window))
     return;
 
   display = gdk_drawable_get_display (window);
@@ -4150,10 +4160,10 @@ gdk_window_set_icon (GdkWindow *window,
 {
   GdkToplevelX11 *toplevel;
 
-  g_return_if_fail (GDK_IS_WINDOW (window));
+  g_return_if_fail (GDK_IS_ONSCREEN_WINDOW (window));
   g_return_if_fail (GDK_WINDOW_TYPE (window) != GDK_WINDOW_CHILD);
   
-  if (GDK_WINDOW_DESTROYED (window))
+  if (GDK_WINDOW_DESTROYED (window) || gdk_window_is_offscreen (window))
     return;
 
   toplevel = _gdk_x11_window_get_toplevel (window);
@@ -4210,9 +4220,9 @@ gdk_window_set_icon_name (GdkWindow   *window,
 {
   GdkDisplay *display;
   
-  g_return_if_fail (GDK_IS_WINDOW (window));
+  g_return_if_fail (GDK_IS_ONSCREEN_WINDOW (window));
 
-  if (GDK_WINDOW_DESTROYED (window))
+  if (GDK_WINDOW_DESTROYED (window) || gdk_window_is_offscreen (window))
     return;
 
   display = gdk_drawable_get_display (window);
@@ -4245,9 +4255,9 @@ gdk_window_set_icon_name (GdkWindow   *window,
 void
 gdk_window_iconify (GdkWindow *window)
 {
-  g_return_if_fail (GDK_IS_WINDOW (window));
+  g_return_if_fail (GDK_IS_ONSCREEN_WINDOW (window));
 
-  if (GDK_WINDOW_DESTROYED (window))
+  if (GDK_WINDOW_DESTROYED (window) || gdk_window_is_offscreen (window))
     return;
 
   if (GDK_WINDOW_IS_MAPPED (window))
@@ -4279,9 +4289,9 @@ gdk_window_iconify (GdkWindow *window)
 void
 gdk_window_deiconify (GdkWindow *window)
 {
-  g_return_if_fail (GDK_IS_WINDOW (window));
+  g_return_if_fail (GDK_IS_ONSCREEN_WINDOW (window));
 
-  if (GDK_WINDOW_DESTROYED (window))
+  if (GDK_WINDOW_DESTROYED (window) || gdk_window_is_offscreen (window))
     return;
 
   if (GDK_WINDOW_IS_MAPPED (window))
@@ -4315,9 +4325,9 @@ gdk_window_deiconify (GdkWindow *window)
 void
 gdk_window_stick (GdkWindow *window)
 {
-  g_return_if_fail (GDK_IS_WINDOW (window));
+  g_return_if_fail (GDK_IS_ONSCREEN_WINDOW (window));
 
-  if (GDK_WINDOW_DESTROYED (window))
+  if (GDK_WINDOW_DESTROYED (window) || gdk_window_is_offscreen (window))
     return;
 
   if (GDK_WINDOW_IS_MAPPED (window))
@@ -4372,9 +4382,9 @@ gdk_window_stick (GdkWindow *window)
 void
 gdk_window_unstick (GdkWindow *window)
 {
-  g_return_if_fail (GDK_IS_WINDOW (window));
+  g_return_if_fail (GDK_IS_ONSCREEN_WINDOW (window));
 
-  if (GDK_WINDOW_DESTROYED (window))
+  if (GDK_WINDOW_DESTROYED (window) || gdk_window_is_offscreen (window))
     return;
 
   if (GDK_WINDOW_IS_MAPPED (window))
@@ -4416,9 +4426,9 @@ gdk_window_unstick (GdkWindow *window)
 void
 gdk_window_maximize (GdkWindow *window)
 {
-  g_return_if_fail (GDK_IS_WINDOW (window));
+  g_return_if_fail (GDK_IS_ONSCREEN_WINDOW (window));
 
-  if (GDK_WINDOW_DESTROYED (window))
+  if (GDK_WINDOW_DESTROYED (window) || gdk_window_is_offscreen (window))
     return;
 
   if (GDK_WINDOW_IS_MAPPED (window))
@@ -4451,9 +4461,9 @@ gdk_window_maximize (GdkWindow *window)
 void
 gdk_window_unmaximize (GdkWindow *window)
 {
-  g_return_if_fail (GDK_IS_WINDOW (window));
+  g_return_if_fail (GDK_IS_ONSCREEN_WINDOW (window));
 
-  if (GDK_WINDOW_DESTROYED (window))
+  if (GDK_WINDOW_DESTROYED (window) || gdk_window_is_offscreen (window))
     return;
 
   if (GDK_WINDOW_IS_MAPPED (window))
@@ -4489,9 +4499,9 @@ gdk_window_unmaximize (GdkWindow *window)
 void
 gdk_window_fullscreen (GdkWindow *window)
 {
-  g_return_if_fail (GDK_IS_WINDOW (window));
+  g_return_if_fail (GDK_IS_ONSCREEN_WINDOW (window));
 
-  if (GDK_WINDOW_DESTROYED (window))
+  if (GDK_WINDOW_DESTROYED (window) || gdk_window_is_offscreen (window))
     return;
 
   if (GDK_WINDOW_IS_MAPPED (window))
@@ -4525,9 +4535,9 @@ gdk_window_fullscreen (GdkWindow *window)
 void
 gdk_window_unfullscreen (GdkWindow *window)
 {
-  g_return_if_fail (GDK_IS_WINDOW (window));
+  g_return_if_fail (GDK_IS_ONSCREEN_WINDOW (window));
 
-  if (GDK_WINDOW_DESTROYED (window))
+  if (GDK_WINDOW_DESTROYED (window) || gdk_window_is_offscreen (window))
     return;
 
   if (GDK_WINDOW_IS_MAPPED (window))
@@ -4639,10 +4649,10 @@ gdk_window_get_group (GdkWindow *window)
 {
   GdkToplevelX11 *toplevel;
   
-  g_return_val_if_fail (GDK_IS_WINDOW (window), NULL);
+  g_return_val_if_fail (GDK_IS_ONSCREEN_WINDOW (window), NULL);
   g_return_val_if_fail (GDK_WINDOW_TYPE (window) != GDK_WINDOW_CHILD, NULL);
 
-  if (GDK_WINDOW_DESTROYED (window))
+  if (GDK_WINDOW_DESTROYED (window) || gdk_window_is_offscreen (window))
     return NULL;
   
   toplevel = _gdk_x11_window_get_toplevel (window);
@@ -4672,9 +4682,9 @@ gdk_window_set_group (GdkWindow *window,
 {
   GdkToplevelX11 *toplevel;
   
-  g_return_if_fail (GDK_IS_WINDOW (window));
+  g_return_if_fail (GDK_IS_ONSCREEN_WINDOW (window));
   g_return_if_fail (GDK_WINDOW_TYPE (window) != GDK_WINDOW_CHILD);
-  g_return_if_fail (leader == NULL || GDK_IS_WINDOW (leader));
+  g_return_if_fail (leader == NULL || GDK_IS_ONSCREEN_WINDOW (leader));
 
   if (GDK_WINDOW_DESTROYED (window) || (leader != NULL && GDK_WINDOW_DESTROYED (leader)))
     return;
@@ -4802,7 +4812,7 @@ gdk_window_set_decorations (GdkWindow      *window,
 {
   MotifWmHints hints;
   
-  g_return_if_fail (GDK_IS_WINDOW (window));
+  g_return_if_fail (GDK_IS_ONSCREEN_WINDOW (window));
   
   /* initialize to zero to avoid writing uninitialized data to socket */
   memset(&hints, 0, sizeof(hints));
@@ -4827,7 +4837,7 @@ gdk_window_get_decorations(GdkWindow       *window,
   MotifWmHints *hints;
   gboolean result = FALSE;
 
-  g_return_val_if_fail (GDK_IS_WINDOW (window), FALSE);
+  g_return_val_if_fail (GDK_IS_ONSCREEN_WINDOW (window), FALSE);
 
   hints = gdk_window_get_mwm_hints (window);
   
@@ -5342,7 +5352,8 @@ gdk_window_x11_set_static_gravities (GdkWindow *window,
       tmp_list = private->children;
       while (tmp_list)
 	{
-	  gdk_window_set_static_win_gravity (tmp_list->data, use_static);
+          if (!gdk_window_is_offscreen (tmp_list->data))
+            gdk_window_set_static_win_gravity (tmp_list->data, use_static);
 	  
 	  tmp_list = tmp_list->next;
 	}
@@ -5917,9 +5928,9 @@ gdk_window_begin_resize_drag (GdkWindow     *window,
                               gint           root_y,
                               guint32        timestamp)
 {
-  g_return_if_fail (GDK_IS_WINDOW (window));
+  g_return_if_fail (GDK_IS_ONSCREEN_WINDOW (window));
 
-  if (GDK_WINDOW_DESTROYED (window))
+  if (GDK_WINDOW_DESTROYED (window) || gdk_window_is_offscreen (window))
     return;
 
   if (gdk_x11_screen_supports_net_wm_hint (GDK_WINDOW_SCREEN (window),
@@ -5952,9 +5963,9 @@ gdk_window_begin_move_drag (GdkWindow *window,
                             gint       root_y,
                             guint32    timestamp)
 {
-  g_return_if_fail (GDK_IS_WINDOW (window));
+  g_return_if_fail (GDK_IS_ONSCREEN_WINDOW (window));
 
-  if (GDK_WINDOW_DESTROYED (window))
+  if (GDK_WINDOW_DESTROYED (window) || gdk_window_is_offscreen (window))
     return;
 
   if (gdk_x11_screen_supports_net_wm_hint (GDK_WINDOW_SCREEN (window),
@@ -5988,7 +5999,7 @@ gdk_window_enable_synchronized_configure (GdkWindow *window)
   GdkWindowObject *private = (GdkWindowObject *)window;
   GdkWindowImplX11 *impl;
 
-  g_return_if_fail (GDK_IS_WINDOW (window));
+  g_return_if_fail (GDK_IS_ONSCREEN_WINDOW (window));
 
   impl = GDK_WINDOW_IMPL_X11 (private->impl);
 	  
@@ -6019,7 +6030,7 @@ gdk_window_configure_finished (GdkWindow *window)
 {
   GdkWindowImplX11 *impl;
 
-  g_return_if_fail (GDK_IS_WINDOW (window));
+  g_return_if_fail (GDK_IS_ONSCREEN_WINDOW (window));
   
   impl = GDK_WINDOW_IMPL_X11 (((GdkWindowObject *)window)->impl);
   if (!impl->use_synchronized_configure)
@@ -6060,6 +6071,11 @@ gdk_window_beep (GdkWindow *window)
 {
   GdkDisplay *display;
 
+  g_return_if_fail (GDK_IS_WINDOW (window));
+
+  if (GDK_WINDOW_DESTROYED (window) || gdk_window_is_offscreen (window))
+    return;
+
   display = GDK_WINDOW_DISPLAY (window);
 
 #ifdef HAVE_XKB
diff --git a/tests/Makefile.am b/tests/Makefile.am
index bd88483..7ade8f9 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -53,6 +53,7 @@ noinst_PROGRAMS =  $(TEST_PROGS)	\
 	testmultiscreen			\
 	testnotebookdnd			\
 	testnouiprint			\
+	testoffscreen			\
 	testprint			\
 	testrgb				\
 	testrecentchooser 		\
@@ -128,6 +129,7 @@ testmultidisplay_DEPENDENCIES = $(TEST_DEPS)
 testmultiscreen_DEPENDENCIES = $(TEST_DEPS)
 testnotebookdnd_DEPENDENCIES = $(TEST_DEPS)
 testnouiprint_DEPENDENCIES = $(TEST_DEPS)
+testoffscreen_DEPENDENCIES = $(TEST_DEPS)
 testprint_DEPENDENCIES = $(TEST_DEPS)
 testrecentchooser_DEPENDENCIES = $(TEST_DEPS)
 testrecentchoosermenu_DEPENDENCIES = $(TEST_DEPS)
@@ -183,6 +185,7 @@ testmultidisplay_LDADD = $(LDADDS)
 testmultiscreen_LDADD = $(LDADDS)
 testnotebookdnd_LDADD = $(LDADDS)
 testnouiprint_LDADD = $(LDADDS)
+testoffscreen_LDADD = $(LDADDS)
 testprint_LDADD = $(LDADDS)
 testrecentchooser_LDADD = $(LDADDS)
 testrecentchoosermenu_LDADD = $(LDADDS)
@@ -304,6 +307,11 @@ testrecentchoosermenu_SOURCES =	\
 testvolumebutton_SOURCES =	\
 	testvolumebutton.c
 
+testoffscreen_SOURCES = 	\
+	gtkoffscreenbox.c	\
+	gtkoffscreenbox.h	\
+	testoffscreen.c
+
 EXTRA_DIST += 			\
 	prop-editor.h		\
 	testgtk.1 		\
diff --git a/tests/gtkoffscreenbox.c b/tests/gtkoffscreenbox.c
new file mode 100644
index 0000000..742f5ab
--- /dev/null
+++ b/tests/gtkoffscreenbox.c
@@ -0,0 +1,649 @@
+#include <config.h>
+#include <cairo.h>
+#include <math.h>
+#include <gtk/gtk.h>
+
+#include "gtkoffscreenbox.h"
+
+static void       gtk_offscreen_box_class_init            (GtkOffscreenBoxClass *klass);
+static void       gtk_offscreen_box_init                  (GtkOffscreenBox      *offscreen_box);
+static void       gtk_offscreen_box_realize               (GtkWidget            *widget);
+static void       gtk_offscreen_box_unrealize             (GtkWidget            *widget);
+static void       gtk_offscreen_box_map                   (GtkWidget            *widget);
+static void       gtk_offscreen_box_unmap                 (GtkWidget            *widget);
+static void       gtk_offscreen_box_size_request          (GtkWidget            *widget,
+							   GtkRequisition       *requisition);
+static void       gtk_offscreen_box_size_allocate         (GtkWidget            *widget,
+							   GtkAllocation        *allocation);
+static gint       gtk_offscreen_box_expose                (GtkWidget            *widget,
+							   GdkEventExpose       *offscreen);
+static void       gtk_offscreen_box_add                   (GtkContainer         *container,
+							   GtkWidget            *child);
+static void       gtk_offscreen_box_remove                (GtkContainer         *container,
+							   GtkWidget            *widget);
+static void       gtk_offscreen_box_forall                (GtkContainer         *container,
+							   gboolean              include_internals,
+							   GtkCallback           callback,
+							   gpointer              callback_data);
+static GType      gtk_offscreen_box_child_type            (GtkContainer         *container);
+static gboolean   gtk_offscreen_box_damage_event          (GtkWidget            *widget,
+							   GdkEventExpose       *event);
+static void       window_to_parent_coordinate             (GdkWindow            *window,
+                                                           GdkWindow            *parent,
+							   double                x,
+							   double                y,
+							   double               *parent_x,
+							   double               *parent_y);
+static void       parent_to_window_coordinate             (GdkWindow            *parent,
+                                                           GdkWindow            *window,
+							   double                x,
+							   double                y,
+							   double               *win_x,
+							   double               *win_y);
+static GdkWindow *find_offscreen_child                    (GdkWindow            *window,
+							   double                x,
+							   double                y);
+
+static const GdkOffscreenHooks offscreen_hooks = {
+  parent_to_window_coordinate,
+  window_to_parent_coordinate,
+  find_offscreen_child
+};
+
+#define CHILD1_SIZE_SCALE 1.0
+#define CHILD2_SIZE_SCALE 1.0
+
+G_DEFINE_TYPE (GtkOffscreenBox, gtk_offscreen_box, GTK_TYPE_CONTAINER);
+
+static void
+gtk_offscreen_box_class_init (GtkOffscreenBoxClass *klass)
+{
+  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+  GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
+
+  widget_class->realize = gtk_offscreen_box_realize;
+  widget_class->unrealize = gtk_offscreen_box_unrealize;
+  widget_class->map = gtk_offscreen_box_map;
+  widget_class->unmap = gtk_offscreen_box_unmap;
+  widget_class->size_request = gtk_offscreen_box_size_request;
+  widget_class->size_allocate = gtk_offscreen_box_size_allocate;
+  widget_class->expose_event = gtk_offscreen_box_expose;
+
+  g_signal_override_class_closure (g_signal_lookup ("damage-event", GTK_TYPE_WIDGET),
+                                   GTK_TYPE_OFFSCREEN_BOX,
+                                   g_cclosure_new (G_CALLBACK (gtk_offscreen_box_damage_event),
+                                                   NULL, NULL));
+
+  container_class->add = gtk_offscreen_box_add;
+  container_class->remove = gtk_offscreen_box_remove;
+  container_class->forall = gtk_offscreen_box_forall;
+  container_class->child_type = gtk_offscreen_box_child_type;
+}
+
+static void
+gtk_offscreen_box_init (GtkOffscreenBox *offscreen_box)
+{
+  GTK_WIDGET_UNSET_FLAGS (offscreen_box, GTK_NO_WINDOW);
+}
+
+GtkWidget*
+gtk_offscreen_box_new (void)
+{
+  return g_object_new (GTK_TYPE_OFFSCREEN_BOX, NULL);
+}
+
+static void
+gtk_offscreen_box_realize (GtkWidget *widget)
+{
+  GdkWindowAttr attributes;
+  gint attributes_mask;
+  gint border_width;
+  GtkOffscreenBox *offscreen_box;
+  GtkRequisition child_requisition;
+  int start_y = 0;
+
+  GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
+
+  offscreen_box = GTK_OFFSCREEN_BOX (widget);
+  border_width = GTK_CONTAINER (widget)->border_width;
+
+  attributes.x = widget->allocation.x + border_width;
+  attributes.y = widget->allocation.y + border_width;
+  attributes.width = widget->allocation.width - 2 * border_width;
+  attributes.height = widget->allocation.height - 2 * border_width;
+  attributes.window_type = GDK_WINDOW_CHILD;
+  attributes.event_mask = gtk_widget_get_events (widget)
+			| GDK_EXPOSURE_MASK
+			| GDK_POINTER_MOTION_MASK
+			| GDK_BUTTON_PRESS_MASK
+			| GDK_BUTTON_RELEASE_MASK
+			| GDK_SCROLL_MASK
+			| GDK_ENTER_NOTIFY_MASK
+			| GDK_LEAVE_NOTIFY_MASK;
+
+  attributes.visual = gtk_widget_get_visual (widget);
+  attributes.colormap = gtk_widget_get_colormap (widget);
+  attributes.wclass = GDK_INPUT_OUTPUT;
+
+  attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
+
+  widget->window = gdk_window_new (gtk_widget_get_parent_window (widget),
+				   &attributes, attributes_mask);
+  gdk_window_set_user_data (widget->window, widget);
+  gdk_window_set_offscreen_hooks (widget->window, &offscreen_hooks);
+
+  /* Child 1 */
+  attributes.x = attributes.y = 0;
+  if (offscreen_box->child1 && GTK_WIDGET_VISIBLE (offscreen_box->child1))
+    {
+      attributes.width = offscreen_box->child1->allocation.width;
+      attributes.height = offscreen_box->child1->allocation.height;
+      start_y += offscreen_box->child1->allocation.height;
+    }
+  offscreen_box->offscreen_window1 = gdk_offscreen_window_new (widget->window,
+							       &attributes, attributes_mask);
+  gdk_window_set_user_data (offscreen_box->offscreen_window1, widget);
+  if (offscreen_box->child1)
+    gtk_widget_set_parent_window (offscreen_box->child1, offscreen_box->offscreen_window1);
+
+  /* Child 2 */
+  attributes.y = start_y;
+  child_requisition.width = child_requisition.height = 0;
+  if (offscreen_box->child2 && GTK_WIDGET_VISIBLE (offscreen_box->child2))
+    {
+      attributes.width = offscreen_box->child2->allocation.width;
+      attributes.height = offscreen_box->child2->allocation.height;
+    }
+  offscreen_box->offscreen_window2 = gdk_offscreen_window_new (widget->window,
+							       &attributes, attributes_mask);
+  gdk_window_set_user_data (offscreen_box->offscreen_window2, widget);
+  if (offscreen_box->child2)
+    gtk_widget_set_parent_window (offscreen_box->child2, offscreen_box->offscreen_window2);
+
+  widget->style = gtk_style_attach (widget->style, widget->window);
+
+  gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
+  gtk_style_set_background (widget->style, offscreen_box->offscreen_window1, GTK_STATE_NORMAL);
+  gtk_style_set_background (widget->style, offscreen_box->offscreen_window2, GTK_STATE_NORMAL);
+
+  gdk_window_show (offscreen_box->offscreen_window1);
+  gdk_window_show (offscreen_box->offscreen_window2);
+}
+
+static void
+gtk_offscreen_box_unrealize (GtkWidget *widget)
+{
+  GtkOffscreenBox *offscreen_box;
+
+  offscreen_box = GTK_OFFSCREEN_BOX (widget);
+
+  gdk_window_set_user_data (offscreen_box->offscreen_window1, NULL);
+  gdk_window_destroy (offscreen_box->offscreen_window1);
+  offscreen_box->offscreen_window1 = NULL;
+
+  gdk_window_set_user_data (offscreen_box->offscreen_window2, NULL);
+  gdk_window_destroy (offscreen_box->offscreen_window2);
+  offscreen_box->offscreen_window2 = NULL;
+
+  GTK_WIDGET_CLASS (gtk_offscreen_box_parent_class)->unrealize (widget);
+}
+
+static GType
+gtk_offscreen_box_child_type (GtkContainer *container)
+{
+  if (!GTK_OFFSCREEN_BOX (container)->child1 || !GTK_OFFSCREEN_BOX (container)->child2)
+    return GTK_TYPE_WIDGET;
+  else
+    return G_TYPE_NONE;
+}
+
+static void
+gtk_offscreen_box_add (GtkContainer *container,
+		       GtkWidget    *widget)
+{
+  GtkOffscreenBox *offscreen_box;
+
+  g_return_if_fail (GTK_IS_OFFSCREEN_BOX (container));
+
+  offscreen_box = GTK_OFFSCREEN_BOX (container);
+
+  if (!offscreen_box->child1)
+    gtk_offscreen_box_add1 (offscreen_box, widget);
+  else if (!offscreen_box->child2)
+    gtk_offscreen_box_add2 (offscreen_box, widget);
+  else
+    g_warning ("GtkOffscreenBox cannot have more than 2 children\n");
+}
+
+void
+gtk_offscreen_box_add1 (GtkOffscreenBox  *offscreen_box,
+			GtkWidget    *child)
+{
+  g_return_if_fail (GTK_IS_WIDGET (child));
+
+  if (offscreen_box->child1 == NULL)
+    {
+      gtk_widget_set_parent_window (child, offscreen_box->offscreen_window1);
+      gtk_widget_set_parent (child, GTK_WIDGET (offscreen_box));
+      offscreen_box->child1 = child;
+    }
+}
+
+void
+gtk_offscreen_box_add2 (GtkOffscreenBox  *offscreen_box,
+			GtkWidget    *child)
+{
+  g_return_if_fail (GTK_IS_WIDGET (child));
+
+  if (offscreen_box->child2 == NULL)
+    {
+      gtk_widget_set_parent_window (child, offscreen_box->offscreen_window2);
+      gtk_widget_set_parent (child, GTK_WIDGET (offscreen_box));
+      offscreen_box->child2 = child;
+    }
+}
+
+static void
+gtk_offscreen_box_remove (GtkContainer *container,
+			  GtkWidget    *widget)
+{
+  GtkOffscreenBox *offscreen_box;
+  gboolean was_visible;
+
+
+  offscreen_box = GTK_OFFSCREEN_BOX (container);
+  was_visible = GTK_WIDGET_VISIBLE (widget);
+
+  if (offscreen_box->child1 == widget)
+    {
+      gtk_widget_unparent (widget);
+
+      offscreen_box->child1 = NULL;
+
+      if (was_visible && GTK_WIDGET_VISIBLE (container))
+	gtk_widget_queue_resize (GTK_WIDGET (container));
+    }
+  else if (offscreen_box->child2 == widget)
+    {
+      gtk_widget_unparent (widget);
+
+      offscreen_box->child2 = NULL;
+
+      if (was_visible && GTK_WIDGET_VISIBLE (container))
+	gtk_widget_queue_resize (GTK_WIDGET (container));
+    }
+}
+
+static void
+gtk_offscreen_box_forall (GtkContainer *container,
+			  gboolean      include_internals,
+			  GtkCallback   callback,
+			  gpointer      callback_data)
+{
+  GtkOffscreenBox *offscreen_box;
+
+  g_return_if_fail (callback != NULL);
+
+  offscreen_box = GTK_OFFSCREEN_BOX (container);
+
+  if (offscreen_box->child1)
+    (*callback) (offscreen_box->child1, callback_data);
+  if (offscreen_box->child2)
+    (*callback) (offscreen_box->child2, callback_data);
+}
+
+static void
+gtk_offscreen_box_map (GtkWidget *widget)
+{
+  GTK_WIDGET_CLASS (gtk_offscreen_box_parent_class)->map (widget);
+}
+
+static void
+gtk_offscreen_box_unmap (GtkWidget *widget)
+{
+  GTK_WIDGET_CLASS (gtk_offscreen_box_parent_class)->unmap (widget);
+}
+
+void
+gtk_offscreen_box_set_angle (GtkOffscreenBox  *offscreen_box,
+			     gdouble           angle)
+{
+  g_return_if_fail (GTK_IS_OFFSCREEN_BOX (offscreen_box));
+
+  offscreen_box->angle = angle;
+  gtk_widget_queue_draw (GTK_WIDGET (offscreen_box));
+
+  /* TODO: Really needs to resent pointer events if over the rotated window */
+}
+
+
+static void
+gtk_offscreen_box_size_request (GtkWidget      *widget,
+				GtkRequisition *requisition)
+{
+  GtkOffscreenBox *offscreen_box = GTK_OFFSCREEN_BOX (widget);
+  int w, h;
+
+  w = 0;
+  h = 0;
+
+  if (offscreen_box->child1 && GTK_WIDGET_VISIBLE (offscreen_box->child1))
+    {
+      GtkRequisition child_requisition;
+
+      gtk_widget_size_request (offscreen_box->child1, &child_requisition);
+
+      w = MAX (w, CHILD1_SIZE_SCALE * child_requisition.width);
+      h += CHILD1_SIZE_SCALE * child_requisition.height;
+    }
+
+  if (offscreen_box->child2 && GTK_WIDGET_VISIBLE (offscreen_box->child2))
+    {
+      GtkRequisition child_requisition;
+
+      gtk_widget_size_request (offscreen_box->child2, &child_requisition);
+
+      w = MAX (w, CHILD2_SIZE_SCALE * child_requisition.width);
+      h += CHILD2_SIZE_SCALE * child_requisition.height;
+    }
+
+  requisition->width = GTK_CONTAINER (widget)->border_width * 2 + w;
+  requisition->height = GTK_CONTAINER (widget)->border_width * 2 + h;
+}
+
+static void
+gtk_offscreen_box_size_allocate (GtkWidget     *widget,
+				 GtkAllocation *allocation)
+{
+  GtkOffscreenBox *offscreen_box;
+  gint border_width;
+  gint start_y;
+
+  widget->allocation = *allocation;
+  offscreen_box = GTK_OFFSCREEN_BOX (widget);
+
+  border_width = GTK_CONTAINER (widget)->border_width;
+
+  if (GTK_WIDGET_REALIZED (widget))
+    gdk_window_move_resize (widget->window,
+                            allocation->x + border_width,
+                            allocation->y + border_width,
+                            allocation->width - border_width * 2,
+                            allocation->height - border_width * 2);
+
+  start_y = 0;
+
+  if (offscreen_box->child1 && GTK_WIDGET_VISIBLE (offscreen_box->child1))
+    {
+      GtkRequisition child_requisition;
+      GtkAllocation child_allocation;
+
+      gtk_widget_get_child_requisition (offscreen_box->child1, &child_requisition);
+      child_allocation.x = child_requisition.width * (CHILD1_SIZE_SCALE - 1.0) / 2;
+      child_allocation.y = start_y + child_requisition.height * (CHILD1_SIZE_SCALE - 1.0) / 2;
+      child_allocation.width = MAX (1, (gint) widget->allocation.width - 2 * border_width);
+      child_allocation.height = child_requisition.height;
+
+      start_y += CHILD1_SIZE_SCALE * child_requisition.height;
+
+      if (GTK_WIDGET_REALIZED (widget))
+	gdk_window_move_resize (offscreen_box->offscreen_window1,
+				child_allocation.x,
+                                child_allocation.y,
+				child_allocation.width,
+                                child_allocation.height);
+
+      child_allocation.x = child_allocation.y = 0;
+      gtk_widget_size_allocate (offscreen_box->child1, &child_allocation);
+    }
+
+  if (offscreen_box->child2 && GTK_WIDGET_VISIBLE (offscreen_box->child2))
+    {
+      GtkRequisition child_requisition;
+      GtkAllocation child_allocation;
+
+      gtk_widget_get_child_requisition (offscreen_box->child2, &child_requisition);
+      child_allocation.x = child_requisition.width * (CHILD2_SIZE_SCALE - 1.0) / 2;
+      child_allocation.y = start_y + child_requisition.height * (CHILD2_SIZE_SCALE - 1.0) / 2;
+      child_allocation.width = MAX (1, (gint) widget->allocation.width - 2 * border_width);
+      child_allocation.height = child_requisition.height;
+
+      start_y += CHILD2_SIZE_SCALE * child_requisition.height;
+
+      if (GTK_WIDGET_REALIZED (widget))
+	gdk_window_move_resize (offscreen_box->offscreen_window2,
+				child_allocation.x,
+                                child_allocation.y,
+				child_allocation.width,
+                                child_allocation.height);
+
+      child_allocation.x = child_allocation.y = 0;
+      gtk_widget_size_allocate (offscreen_box->child2, &child_allocation);
+    }
+}
+
+static gboolean
+gtk_offscreen_box_damage_event (GtkWidget      *widget,
+                                GdkEventExpose *event)
+{
+  gdk_window_invalidate_rect (widget->window, NULL, FALSE);
+
+  return TRUE;
+}
+
+static gint
+gtk_offscreen_box_expose (GtkWidget      *widget,
+			  GdkEventExpose *event)
+{
+  GtkOffscreenBox *offscreen_box = GTK_OFFSCREEN_BOX (widget);
+
+  if (GTK_WIDGET_DRAWABLE (widget))
+    {
+      if (event->window == widget->window)
+	{
+	  int start_y = 0;
+
+	  if (offscreen_box->child1 && GTK_WIDGET_VISIBLE (offscreen_box->child1))
+	    {
+	      GdkPixmap *pixmap = gdk_window_get_offscreen_pixmap (offscreen_box->offscreen_window1);
+              const GtkAllocation child_area = offscreen_box->child1->allocation;
+	      cairo_t *cr = gdk_cairo_create (widget->window);
+
+              gdk_cairo_set_source_pixmap (cr, pixmap, 0, 0);
+	      cairo_paint (cr);
+              cairo_destroy (cr);
+	      start_y += child_area.height;
+	    }
+
+	  if (offscreen_box->child2 && GTK_WIDGET_VISIBLE (offscreen_box->child2))
+	    {
+	      GdkPixmap *pixmap = gdk_window_get_offscreen_pixmap (offscreen_box->offscreen_window2);
+              const GtkAllocation child_area = offscreen_box->child2->allocation;
+	      cairo_t *cr = gdk_cairo_create (widget->window);
+
+	      cairo_translate (cr, 0, start_y);
+	      cairo_translate (cr, child_area.width / 2, child_area.height / 2);
+	      cairo_rotate (cr, offscreen_box->angle);
+	      cairo_translate (cr, -child_area.width / 2, -child_area.height / 2);
+	      gdk_cairo_set_source_pixmap (cr, pixmap, 0, 0);
+	      cairo_paint (cr);
+              cairo_destroy (cr);
+	    }
+	}
+      else if (event->window == offscreen_box->offscreen_window1)
+	{
+	  gtk_paint_flat_box (widget->style, event->window,
+			      GTK_STATE_NORMAL, GTK_SHADOW_NONE,
+			      &event->area, widget, "blah",
+			      0, 0, -1, -1);
+
+	  if (offscreen_box->child1)
+	    gtk_container_propagate_expose (GTK_CONTAINER (widget),
+					    offscreen_box->child1,
+                                            event);
+	}
+      else if (event->window == offscreen_box->offscreen_window2)
+	{
+	  gtk_paint_flat_box (widget->style, event->window,
+			      GTK_STATE_NORMAL, GTK_SHADOW_NONE,
+			      &event->area, widget, "blah",
+			      0, 0, -1, -1);
+
+	  if (offscreen_box->child2)
+	    gtk_container_propagate_expose (GTK_CONTAINER (widget),
+					    offscreen_box->child2,
+                                            event);
+	}
+    }
+
+  return FALSE;
+}
+
+static gboolean
+parent_to_child1 (GtkOffscreenBox *offscreen_box,
+		  gdouble          x,
+                  gdouble          y,
+                  gdouble         *child_x,
+                  gdouble         *child_y)
+{
+  const GtkAllocation child_area = offscreen_box->child1->allocation;
+  *child_x = x;
+  *child_y = y;
+  return x >= 0 && x <= child_area.width && y >= 0 && y <= child_area.height;
+}
+
+static void
+parent_to_window_coordinate (GdkWindow *parent,
+                             GdkWindow *window,
+			     gdouble    x,
+                             gdouble    y,
+			     gdouble   *win_x,
+                             gdouble   *win_y)
+{
+  GtkOffscreenBox *offscreen_box = NULL;
+  GtkAllocation child2_area;
+  gpointer window_data;
+  double pos_x, pos_y, rot_x, rot_y, start_y, angle;
+
+  gdk_window_get_user_data (parent, &window_data);
+  offscreen_box = window_data;
+  if (!offscreen_box)
+    return;
+
+  if (window == (gpointer) offscreen_box->offscreen_window1)
+    {
+      *win_x = x;
+      *win_y = y;
+
+      return;
+    }
+  g_assert (window == (gpointer) offscreen_box->offscreen_window2);
+
+  start_y = offscreen_box->child1 ? offscreen_box->child1->allocation.height : 0;
+  child2_area = offscreen_box->child2->allocation;
+
+  pos_x = x - child2_area.width / 2;
+  pos_y = y - start_y - child2_area.height / 2;
+
+  angle = -offscreen_box->angle;
+  rot_x = pos_x * cos (angle) - pos_y * sin (angle);
+  rot_y = pos_x * sin (angle) + pos_y * cos (angle);
+
+  *win_x = rot_x + child2_area.width / 2;
+  *win_y = rot_y + child2_area.height / 2;
+}
+
+static void
+window_to_parent_coordinate (GdkWindow *window,
+                             GdkWindow *parent,
+			     gdouble    x,
+                             gdouble    y,
+			     gdouble   *parent_x,
+                             gdouble   *parent_y)
+{
+  GtkOffscreenBox *offscreen_box = NULL;
+  GtkAllocation child2_area;
+  gpointer window_data;
+  gdouble pos_x, pos_y, rot_x, rot_y, start_y, angle;
+
+  gdk_window_get_user_data (parent, &window_data);
+  offscreen_box = window_data;
+  if (!offscreen_box)
+    return;
+
+  if (window == (gpointer) offscreen_box->offscreen_window1)
+    {
+      *parent_x = x;
+      *parent_y = y;
+
+      return;
+    }
+  g_assert (window == (gpointer) offscreen_box->offscreen_window2);
+
+  start_y = offscreen_box->child1 ? offscreen_box->child1->allocation.height : 0;
+  child2_area = offscreen_box->child2->allocation;
+
+  pos_x = x - child2_area.width / 2;
+  pos_y = y - child2_area.height / 2;
+
+  angle = offscreen_box->angle;
+  rot_x = pos_x * cos (angle) - pos_y * sin (angle);
+  rot_y = pos_x * sin (angle) + pos_y * cos (angle);
+
+  rot_x += child2_area.width / 2;
+  rot_y += child2_area.height / 2;
+
+  *parent_x = rot_x;
+  *parent_y = rot_y + start_y;
+}
+
+static gboolean
+parent_to_child2 (GtkOffscreenBox *offscreen_box,
+		  gdouble          x,
+                  gdouble          y,
+                  gdouble         *child_x,
+                  gdouble         *child_y)
+{
+  const GtkAllocation child_area = offscreen_box->child2->allocation;
+  double pos_x, pos_y, rot_x, rot_y;
+  double angle;
+  double start_y = offscreen_box->child1 ? offscreen_box->child1->allocation.height : 0;
+
+  pos_x = x - child_area.width / 2;
+  pos_y = y - start_y - child_area.height / 2;
+
+  angle = -offscreen_box->angle;
+  rot_x = pos_x * cos (angle) - pos_y * sin (angle);
+  rot_y = pos_x * sin (angle) + pos_y * cos (angle);
+
+  *child_x = rot_x + child_area.width / 2;
+  *child_y = rot_y + child_area.height / 2;
+
+  return (*child_x >= 0 && *child_x < child_area.width &&
+	  *child_y >= 0 && *child_y < child_area.height);
+}
+
+static GdkWindow *
+find_offscreen_child (GdkWindow *window,
+                      gdouble    x,
+                      gdouble    y)
+{
+  GtkOffscreenBox *offscreen_box = NULL;
+  gpointer window_data;
+  gdouble ox, oy;
+
+  gdk_window_get_user_data (window, &window_data);
+  offscreen_box = window_data;
+  g_assert (offscreen_box != NULL);
+
+  /* Child2 is drawn last, pick first */
+  if (offscreen_box->child2 &&
+      parent_to_child2 (offscreen_box, x, y, &ox, &oy))
+    return offscreen_box->offscreen_window2;
+
+  if (offscreen_box->child1 &&
+      parent_to_child1 (offscreen_box, x, y, &ox, &oy))
+    return offscreen_box->offscreen_window1;
+
+  return NULL;
+}
diff --git a/tests/gtkoffscreenbox.h b/tests/gtkoffscreenbox.h
new file mode 100644
index 0000000..aa1d15d
--- /dev/null
+++ b/tests/gtkoffscreenbox.h
@@ -0,0 +1,52 @@
+#ifndef __GTK_OFFSCREEN_BOX_H__
+#define __GTK_OFFSCREEN_BOX_H__
+
+
+#include <gdk/gdk.h>
+#include <gtk/gtk.h>
+
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_OFFSCREEN_BOX              (gtk_offscreen_box_get_type ())
+#define GTK_OFFSCREEN_BOX(obj)              (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_OFFSCREEN_BOX, GtkOffscreenBox))
+#define GTK_OFFSCREEN_BOX_CLASS(klass)      (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_OFFSCREEN_BOX, GtkOffscreenBoxClass))
+#define GTK_IS_OFFSCREEN_BOX(obj)           (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_OFFSCREEN_BOX))
+#define GTK_IS_OFFSCREEN_BOX_CLASS(klass)   (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_OFFSCREEN_BOX))
+#define GTK_OFFSCREEN_BOX_GET_CLASS(obj)    (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_OFFSCREEN_BOX, GtkOffscreenBoxClass))
+
+typedef struct _GtkOffscreenBox	  GtkOffscreenBox;
+typedef struct _GtkOffscreenBoxClass  GtkOffscreenBoxClass;
+
+struct _GtkOffscreenBox
+{
+  GtkContainer container;
+
+  GtkWidget *child1;
+  GtkWidget *child2;
+
+  GdkWindow *offscreen_window1;
+  GdkWindow *offscreen_window2;
+
+  gdouble angle;
+};
+
+struct _GtkOffscreenBoxClass
+{
+  GtkBinClass parent_class;
+};
+
+GType	   gtk_offscreen_box_get_type           (void) G_GNUC_CONST;
+GtkWidget* gtk_offscreen_box_new       (void);
+void       gtk_offscreen_box_add1      (GtkOffscreenBox *offscreen,
+					GtkWidget       *child);
+void       gtk_offscreen_box_add2      (GtkOffscreenBox *offscreen,
+					GtkWidget       *child);
+void       gtk_offscreen_box_set_angle (GtkOffscreenBox *offscreen,
+					double           angle);
+
+
+
+G_END_DECLS
+
+#endif /* __GTK_OFFSCREEN_BOX_H__ */
diff --git a/tests/testoffscreen.c b/tests/testoffscreen.c
new file mode 100644
index 0000000..f332d62
--- /dev/null
+++ b/tests/testoffscreen.c
@@ -0,0 +1,381 @@
+/*
+ * testoffscreen.c
+ */
+
+#undef GTK_DISABLE_DEPRECATED
+
+#include <math.h>
+#include <gtk/gtk.h>
+#include "gtkoffscreenbox.h"
+
+
+static void
+combo_changed_cb (GtkWidget *combo,
+		  gpointer   data)
+{
+  GtkWidget *label = GTK_WIDGET (data);
+  gint active;
+
+  active = gtk_combo_box_get_active (GTK_COMBO_BOX (combo));
+
+  gtk_label_set_ellipsize (GTK_LABEL (label), (PangoEllipsizeMode)active);
+}
+
+static gboolean
+layout_expose_handler (GtkWidget      *widget,
+                       GdkEventExpose *event)
+{
+  GtkLayout *layout = GTK_LAYOUT (widget);
+
+  gint i,j;
+  gint imin, imax, jmin, jmax;
+
+  if (event->window != layout->bin_window)
+    return FALSE;
+
+  imin = (event->area.x) / 10;
+  imax = (event->area.x + event->area.width + 9) / 10;
+
+  jmin = (event->area.y) / 10;
+  jmax = (event->area.y + event->area.height + 9) / 10;
+
+  for (i = imin; i < imax; i++)
+    for (j = jmin; j < jmax; j++)
+      if ((i + j) % 2)
+	gdk_draw_rectangle (layout->bin_window,
+                            widget->style->black_gc,
+                            TRUE,
+                            10 * i, 10 * j,
+                            1 + i % 10, 1 + j % 10);
+
+  return FALSE;
+}
+
+static gboolean
+scroll_layout (gpointer data)
+{
+  GtkWidget *layout = data;
+  GtkAdjustment *adj;
+
+  adj = gtk_layout_get_hadjustment (GTK_LAYOUT (layout));
+  gtk_adjustment_set_value (adj,
+			    gtk_adjustment_get_value (adj) + 5.0);
+  return TRUE;
+}
+
+static guint layout_timeout;
+
+static void
+create_layout (GtkWidget *vbox)
+{
+  GtkWidget *layout;
+  GtkWidget *scrolledwindow;
+  GtkWidget *button;
+  gchar buf[16];
+  gint i, j;
+
+  scrolledwindow = gtk_scrolled_window_new (NULL, NULL);
+  gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolledwindow),
+				       GTK_SHADOW_IN);
+  gtk_scrolled_window_set_placement (GTK_SCROLLED_WINDOW (scrolledwindow),
+				     GTK_CORNER_TOP_RIGHT);
+
+  gtk_box_pack_start (GTK_BOX (vbox), scrolledwindow, TRUE, TRUE, 0);
+
+  layout = gtk_layout_new (NULL, NULL);
+  gtk_container_add (GTK_CONTAINER (scrolledwindow), layout);
+
+  /* We set step sizes here since GtkLayout does not set
+   * them itself.
+   */
+  GTK_LAYOUT (layout)->hadjustment->step_increment = 10.0;
+  GTK_LAYOUT (layout)->vadjustment->step_increment = 10.0;
+
+  gtk_widget_set_events (layout, GDK_EXPOSURE_MASK);
+  g_signal_connect (layout, "expose_event",
+		    G_CALLBACK (layout_expose_handler),
+                    NULL);
+
+  gtk_layout_set_size (GTK_LAYOUT (layout), 1600, 128000);
+
+  for (i = 0 ; i < 16 ; i++)
+    for (j = 0 ; j < 16 ; j++)
+      {
+	g_snprintf (buf, sizeof (buf), "Button %d, %d", i, j);
+
+	if ((i + j) % 2)
+	  button = gtk_button_new_with_label (buf);
+	else
+	  button = gtk_label_new (buf);
+
+	gtk_layout_put (GTK_LAYOUT (layout), button,
+			j * 100, i * 100);
+      }
+
+  for (i = 16; i < 1280; i++)
+    {
+      g_snprintf (buf, sizeof (buf), "Button %d, %d", i, 0);
+
+      if (i % 2)
+	button = gtk_button_new_with_label (buf);
+      else
+	button = gtk_label_new (buf);
+
+      gtk_layout_put (GTK_LAYOUT (layout), button,
+		      0, i * 100);
+    }
+
+  layout_timeout = g_timeout_add (1000, scroll_layout, layout);
+}
+
+static void
+create_treeview (GtkWidget *vbox)
+{
+  GtkWidget *scrolledwindow;
+  GtkListStore *store;
+  GtkWidget *tree_view;
+  GSList *stock_ids;
+  GSList *list;
+
+  scrolledwindow = gtk_scrolled_window_new (NULL, NULL);
+  gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolledwindow),
+				       GTK_SHADOW_IN);
+
+  gtk_box_pack_start (GTK_BOX (vbox), scrolledwindow, TRUE, TRUE, 0);
+
+  store = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_STRING);
+  tree_view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (store));
+  g_object_unref (store);
+
+  gtk_container_add (GTK_CONTAINER (scrolledwindow), tree_view);
+
+  gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (tree_view), -1,
+                                               "Icon",
+                                               gtk_cell_renderer_pixbuf_new (),
+                                               "stock-id", 0,
+                                               NULL);
+  gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (tree_view), -1,
+                                               "Label",
+                                               gtk_cell_renderer_text_new (),
+                                               "text", 1,
+                                               NULL);
+
+  stock_ids = gtk_stock_list_ids ();
+
+  for (list = stock_ids; list; list = g_slist_next (list))
+    {
+      const gchar *stock_id = list->data;
+      GtkStockItem item;
+
+      if (gtk_stock_lookup (stock_id, &item))
+        {
+          gtk_list_store_insert_with_values (store, NULL, -1,
+                                             0, item.stock_id,
+                                             1, item.label,
+                                             -1);
+        }
+    }
+
+  g_slist_foreach (stock_ids, (GFunc) g_free, NULL);
+  g_slist_free (stock_ids);
+}
+
+static GtkWidget *
+create_widgets (void)
+{
+  GtkWidget *main_hbox, *main_vbox;
+  GtkWidget *vbox, *hbox, *label, *combo, *entry, *button, *cb;
+  GtkWidget *sw, *text_view;
+  GList *cbitems = NULL;
+
+  main_vbox = gtk_vbox_new (0, FALSE);
+
+  main_hbox = gtk_hbox_new (0, FALSE);
+  gtk_box_pack_start (GTK_BOX (main_vbox), main_hbox, TRUE, TRUE, 0);
+
+  vbox = gtk_vbox_new (0, FALSE);
+  gtk_box_pack_start (GTK_BOX (main_hbox), vbox, TRUE, TRUE, 0);
+
+  hbox = gtk_hbox_new (0, FALSE);
+  gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
+
+  label = gtk_label_new ("This label may be ellipsized\nto make it fit.");
+  gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, TRUE, 0);
+
+  combo = gtk_combo_box_new_text ();
+  gtk_combo_box_append_text (GTK_COMBO_BOX (combo), "NONE");
+  gtk_combo_box_append_text (GTK_COMBO_BOX (combo), "START");
+  gtk_combo_box_append_text (GTK_COMBO_BOX (combo), "MIDDLE");
+  gtk_combo_box_append_text (GTK_COMBO_BOX (combo), "END");
+  gtk_combo_box_set_active (GTK_COMBO_BOX (combo), 0);
+  gtk_box_pack_start (GTK_BOX (hbox), combo, TRUE, TRUE, 0);
+
+  g_signal_connect (combo, "changed",
+                    G_CALLBACK (combo_changed_cb),
+                    label);
+
+  entry = gtk_entry_new ();
+  gtk_entry_set_text (GTK_ENTRY (entry), "an entry - lots of text.... lots of text.... lots of text.... lots of text.... ");
+  gtk_box_pack_start (GTK_BOX (vbox), entry, FALSE, FALSE, 0);
+
+  label = gtk_label_new ("Label after entry.");
+  gtk_label_set_selectable (GTK_LABEL (label), TRUE);
+  gtk_box_pack_start (GTK_BOX (vbox), label, TRUE, TRUE, 0);
+
+  button = gtk_button_new_with_label ("Button");
+  gtk_box_pack_start (GTK_BOX (vbox), button, TRUE, TRUE, 0);
+
+  button = gtk_check_button_new_with_mnemonic ("_Check button");
+  gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
+
+  cb = gtk_combo_new ();
+  cbitems = g_list_append (cbitems, "item0");
+  cbitems = g_list_append (cbitems, "item1 item1");
+  cbitems = g_list_append (cbitems, "item2 item2 item2");
+  gtk_combo_set_popdown_strings (GTK_COMBO (cb), cbitems);
+  gtk_entry_set_text (GTK_ENTRY (GTK_COMBO (cb)->entry), "hello world ♥ foo");
+  gtk_editable_select_region (GTK_EDITABLE (GTK_COMBO (cb)->entry),
+			      0, -1);
+  gtk_box_pack_start (GTK_BOX (vbox), cb, TRUE, TRUE, 0);
+
+  sw = gtk_scrolled_window_new (NULL, NULL);
+  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
+				  GTK_POLICY_AUTOMATIC,
+				  GTK_POLICY_AUTOMATIC);
+  text_view = gtk_text_view_new ();
+  gtk_box_pack_start (GTK_BOX (vbox), sw, TRUE, TRUE, 0);
+  gtk_container_add (GTK_CONTAINER (sw), text_view);
+
+  create_layout (vbox);
+
+  create_treeview (main_hbox);
+
+  return main_vbox;
+}
+
+
+static void
+scale_changed (GtkRange        *range,
+	       GtkOffscreenBox *offscreen_box)
+{
+  gtk_offscreen_box_set_angle (offscreen_box, gtk_range_get_value (range));
+}
+
+static GtkWidget *scale = NULL;
+
+static void
+remove_clicked (GtkButton *button,
+                GtkWidget *widget)
+{
+  gtk_widget_destroy (widget);
+  g_source_remove (layout_timeout);
+
+  gtk_widget_set_sensitive (GTK_WIDGET (button), FALSE);
+  gtk_widget_set_sensitive (scale, FALSE);
+}
+
+int
+main (int   argc,
+      char *argv[])
+{
+  GtkWidget *window, *widget, *vbox, *button;
+  GtkWidget *offscreen = NULL;
+  gboolean use_offscreen;
+
+  gtk_init (&argc, &argv);
+
+  use_offscreen = argc == 1;
+
+  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+  gtk_window_set_default_size (GTK_WINDOW (window), 300,300);
+
+  g_signal_connect (window, "destroy",
+                    G_CALLBACK (gtk_main_quit),
+                    NULL);
+
+  vbox = gtk_vbox_new (0, FALSE);
+  gtk_container_add (GTK_CONTAINER (window), vbox);
+
+  scale = gtk_hscale_new_with_range (0,
+				     M_PI * 2,
+				     0.01);
+  gtk_box_pack_start (GTK_BOX (vbox), scale, FALSE, FALSE, 0);
+
+  button = gtk_button_new_with_label ("Remove child 2");
+  gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
+
+  if (use_offscreen)
+    {
+      offscreen = gtk_offscreen_box_new ();
+
+      g_signal_connect (scale, "value_changed",
+                        G_CALLBACK (scale_changed),
+                        offscreen);
+    }
+  else
+    {
+      offscreen = gtk_vpaned_new ();
+    }
+
+  gtk_box_pack_start (GTK_BOX (vbox), offscreen, TRUE, TRUE, 0);
+
+  widget = create_widgets ();
+  if (use_offscreen)
+    gtk_offscreen_box_add1 (GTK_OFFSCREEN_BOX (offscreen),
+			    widget);
+  else
+    gtk_paned_add1 (GTK_PANED (offscreen), widget);
+
+  widget = create_widgets ();
+  if (1)
+    {
+      GtkWidget *widget2, *box2, *offscreen2;
+
+      offscreen2 = gtk_offscreen_box_new ();
+      gtk_box_pack_start (GTK_BOX (widget), offscreen2, FALSE, FALSE, 0);
+
+      g_signal_connect (scale, "value_changed",
+                        G_CALLBACK (scale_changed),
+                        offscreen2);
+
+      box2 = gtk_vbox_new (FALSE, 0);
+      gtk_offscreen_box_add2 (GTK_OFFSCREEN_BOX (offscreen2), box2);
+
+      widget2 = gtk_button_new_with_label ("Offscreen in offscreen");
+      gtk_box_pack_start (GTK_BOX (box2), widget2, FALSE, FALSE, 0);
+
+      widget2 = gtk_entry_new ();
+      gtk_entry_set_text (GTK_ENTRY (widget2), "Offscreen in offscreen");
+      gtk_box_pack_start (GTK_BOX (box2), widget2, FALSE, FALSE, 0);
+    }
+
+  if (use_offscreen)
+    gtk_offscreen_box_add2 (GTK_OFFSCREEN_BOX (offscreen), widget);
+  else
+    gtk_paned_add2 (GTK_PANED (offscreen), widget);
+
+  gtk_widget_show_all (window);
+
+  g_signal_connect (G_OBJECT (button), "clicked",
+                    G_CALLBACK (remove_clicked),
+                    widget);
+
+  /* redirect */
+  if (0)
+    {
+      GtkWidget *redirect_win;
+
+      redirect_win = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+      gtk_window_set_default_size (GTK_WINDOW (redirect_win), 400,400);
+      gtk_widget_show (redirect_win);
+      gtk_widget_realize (redirect_win);
+      gtk_widget_realize (window);
+      gdk_window_redirect_to_drawable (window->window,
+				       GDK_DRAWABLE (redirect_win->window),
+				       0, 0, 0, 0, -1, -1);
+    }
+
+  gtk_main ();
+
+  return 0;
+}
