]> Pileus Git - ~andy/gtk/blobdiff - gdk/gdkwindow.c
stylecontext: Do invalidation on first resize container
[~andy/gtk] / gdk / gdkwindow.c
index 4022b91281542e02321d2190541b43cbb548233c..6d48080294cb93cd9f915d058485eaf67147a283 100644 (file)
 #include "gdkdeviceprivate.h"
 #include "gdkvisualprivate.h"
 #include "gdkmarshalers.h"
+#include "gdkframeclockidle.h"
 #include "gdkwindowimpl.h"
 
 #include <math.h>
 
+/* for the use of round() */
+#include "fallback-c89.c"
+
 #undef DEBUG_WINDOW_PRINTING
 
 
  * <firstterm>composited</firstterm> window it is the responsibility of the
  * application to render the window contents at the right spot.
  * </para>
- * <example id="composited-window-example">
- * <title>Composited windows</title>
- * <programlisting>
- * <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" parse="text" href="../../../../examples/gdk/composited-window-example.c"><xi:fallback>FIXME: MISSING XINCLUDE CONTENT</xi:fallback></xi:include>
- * </programlisting></example>
- * <para>
- * In the example <xref linkend="composited-window-example"/>, a button is
- * placed inside of an event box inside of a window. The event box is set as
- * composited and therefore is no longer automatically drawn to the screen.
- *
- * When the contents of the event box change, an expose event is generated on
- * its parent window (which, in this case, belongs to the toplevel #GtkWindow).
- * The expose handler for this widget is responsible for merging the changes
- * back on the screen in the way that it wishes.
- *
- * In our case, we merge the contents with a 50% transparency. We also set the
- * background colour of the window to red. The effect is that the background
- * shows through the button.
- * </para>
  * </refsect2>
  * <refsect2 id="OFFSCREEN-WINDOWS">
  * <title>Offscreen Windows</title>
  * be it a toplevel window or a child window. In this setup the
  * GdkWindow (and other GdkDrawables) were platform independent classes,
  * and the actual platform specific implementation was in a delegate
- * object availible as "impl" in the window object.
+ * object available as "impl" in the window object.
  *
  * With the addition of client side windows and offscreen windows this
  * changes a bit. The application-visible GdkWindow object behaves as
@@ -202,6 +187,7 @@ struct _GdkWindowPaint
   cairo_region_t *region;
   cairo_surface_t *surface;
   cairo_region_t *flushed;
+  guint8 alpha;
   guint uses_implicit : 1;
 };
 
@@ -254,6 +240,10 @@ static void gdk_window_invalidate_rect_full (GdkWindow          *window,
                                             gboolean            invalidate_children,
                                             ClearBg             clear_bg);
 static void _gdk_window_propagate_has_alpha_background (GdkWindow *window);
+static cairo_surface_t *gdk_window_ref_impl_surface (GdkWindow *window);
+
+static void gdk_window_set_frame_clock (GdkWindow      *window,
+                                        GdkFrameClock  *clock);
 
 static guint signals[LAST_SIGNAL] = { 0 };
 
@@ -263,6 +253,43 @@ static const cairo_user_data_key_t gdk_window_cairo_key;
 
 G_DEFINE_ABSTRACT_TYPE (GdkWindow, gdk_window, G_TYPE_OBJECT)
 
+#ifdef DEBUG_WINDOW_PRINTING
+char *
+print_region (cairo_region_t *region)
+{
+  GString *s = g_string_new ("{");
+  if (cairo_region_is_empty (region))
+    {
+      g_string_append (s, "empty");
+    }
+  else
+    {
+      int num = cairo_region_num_rectangles (region);
+      cairo_rectangle_int_t r;
+
+      if (num == 1)
+       {
+         cairo_region_get_rectangle (region, 0, &r);
+         g_string_append_printf (s, "%dx%d @%d,%d", r.width, r.height, r.x, r.y);
+       }
+      else
+       {
+         int i;
+         cairo_region_get_extents (region, &r);
+         g_string_append_printf (s, "extent: %dx%d @%d,%d, details: ", r.width, r.height, r.x, r.y);
+         for (i = 0; i < num; i++)
+           {
+             g_string_append_printf (s, "[%dx%d @%d,%d]", r.width, r.height, r.x, r.y);
+             if (i != num -1)
+               g_string_append (s, ", ");
+           }
+       }
+    }
+  g_string_append (s, "}");
+  return g_string_free (s, FALSE);
+}
+#endif
+
 GType
 _gdk_paintable_get_type (void)
 {
@@ -295,6 +322,7 @@ gdk_window_init (GdkWindow *window)
   window->window_type = GDK_WINDOW_CHILD;
 
   window->state = GDK_WINDOW_STATE_WITHDRAWN;
+  window->fullscreen_mode = GDK_FULLSCREEN_ON_CURRENT_MONITOR;
   window->width = 1;
   window->height = 1;
   window->toplevel_window_type = -1;
@@ -397,11 +425,11 @@ gdk_window_class_init (GdkWindowClass *klass)
   /**
    * GdkWindow::to-embedder:
    * @window: the offscreen window on which the signal is emitted
-   * @offscreen-x: x coordinate in the offscreen window
-   * @offscreen-y: y coordinate in the offscreen window
-   * @embedder-x: (out) (type double): return location for the x
+   * @offscreen_x: x coordinate in the offscreen window
+   * @offscreen_y: y coordinate in the offscreen window
+   * @embedder_x: (out) (type double): return location for the x
    *     coordinate in the embedder window
-   * @embedder-y: (out) (type double): return location for the y
+   * @embedder_y: (out) (type double): return location for the y
    *     coordinate in the embedder window
    *
    * The ::to-embedder signal is emitted to translate coordinates
@@ -428,11 +456,11 @@ gdk_window_class_init (GdkWindowClass *klass)
   /**
    * GdkWindow::from-embedder:
    * @window: the offscreen window on which the signal is emitted
-   * @embedder-x: x coordinate in the embedder window
-   * @embedder-y: y coordinate in the embedder window
-   * @offscreen-x: (out) (type double): return location for the x
+   * @embedder_x: x coordinate in the embedder window
+   * @embedder_y: y coordinate in the embedder window
+   * @offscreen_x: (out) (type double): return location for the x
    *     coordinate in the offscreen window
-   * @offscreen-y: (out) (type double): return location for the y
+   * @offscreen_y: (out) (type double): return location for the y
    *     coordinate in the offscreen window
    *
    * The ::from-embedder signal is emitted to translate coordinates
@@ -650,6 +678,13 @@ gdk_window_has_no_impl (GdkWindow *window)
   return window->impl_window != window;
 }
 
+static gboolean
+gdk_window_has_alpha (GdkWindow *window)
+{
+  return !gdk_window_has_impl (window) &&
+    (window->has_alpha_background || window->alpha != 255);
+}
+
 static void
 remove_layered_child_area (GdkWindow *window,
                           cairo_region_t *region)
@@ -677,7 +712,7 @@ remove_layered_child_area (GdkWindow *window,
        continue;
 
       /* Only non-impl children with alpha add to the layered region */
-      if (!child->has_alpha_background && gdk_window_has_impl (child))
+      if (!gdk_window_has_alpha (child))
        continue;
 
       r.x = child->x;
@@ -779,7 +814,7 @@ remove_child_area (GdkWindow *window,
            }
        }
 
-      if (child->has_alpha_background)
+      if (gdk_window_has_alpha (child))
        {
          if (layered_region != NULL)
            cairo_region_union (layered_region, child_region);
@@ -1243,11 +1278,12 @@ get_native_device_event_mask (GdkWindow *private,
        * lists due to some non-native child window.
        */
       if (gdk_window_is_toplevel (private) ||
-         mask & GDK_BUTTON_PRESS_MASK)
-       mask |=
-         GDK_POINTER_MOTION_MASK |
-         GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
-         GDK_SCROLL_MASK;
+          mask & GDK_BUTTON_PRESS_MASK)
+        mask |=
+          GDK_TOUCH_MASK |
+          GDK_POINTER_MOTION_MASK |
+          GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
+          GDK_SCROLL_MASK;
 
       return mask;
     }
@@ -1299,7 +1335,8 @@ sync_native_window_stack_position (GdkWindow *window)
  * @parent: (allow-none): a #GdkWindow, or %NULL to create the window as a child of
  *   the default root window for the default display.
  * @attributes: attributes of the new window
- * @attributes_mask: mask indicating which fields in @attributes are valid
+ * @attributes_mask: (type GdkWindowAttributesType): mask indicating which
+ *   fields in @attributes are valid
  *
  * Creates a new #GdkWindow using the attributes from
  * @attributes. See #GdkWindowAttr and #GdkWindowAttributesType for
@@ -1343,6 +1380,11 @@ gdk_window_new (GdkWindow     *parent,
       return NULL;
     }
 
+  if (attributes_mask & GDK_WA_VISUAL)
+    {
+      g_return_val_if_fail (gdk_visual_get_screen (attributes->visual) == screen, NULL);
+    }
+
   display = gdk_screen_get_display (screen);
 
   window = _gdk_display_create_window (display);
@@ -1373,6 +1415,7 @@ gdk_window_new (GdkWindow     *parent,
   window->y = y;
   window->width = (attributes->width > 1) ? (attributes->width) : (1);
   window->height = (attributes->height > 1) ? (attributes->height) : (1);
+  window->alpha = 255;
 
   if (attributes->wclass == GDK_INPUT_ONLY)
     {
@@ -1430,6 +1473,13 @@ gdk_window_new (GdkWindow     *parent,
   if (window->parent)
     window->parent->children = g_list_prepend (window->parent->children, window);
 
+  if (window->parent->window_type == GDK_WINDOW_ROOT)
+    {
+      GdkFrameClock *frame_clock = g_object_new (GDK_TYPE_FRAME_CLOCK_IDLE, NULL);
+      gdk_window_set_frame_clock (window, frame_clock);
+      g_object_unref (frame_clock);
+    }
+
   native = FALSE;
   if (window->parent->window_type == GDK_WINDOW_ROOT)
     native = TRUE; /* Always use native windows for toplevels */
@@ -1680,6 +1730,28 @@ gdk_window_reparent (GdkWindow *window,
        }
     }
 
+  /* If we changed the window type, we might have to set or
+   * unset the frame clock on the window
+   */
+  if (GDK_WINDOW_TYPE (new_parent) == GDK_WINDOW_ROOT &&
+      GDK_WINDOW_TYPE (window) != GDK_WINDOW_FOREIGN)
+    {
+      if (window->frame_clock == NULL)
+        {
+          GdkFrameClock *frame_clock = g_object_new (GDK_TYPE_FRAME_CLOCK_IDLE, NULL);
+          gdk_window_set_frame_clock (window, frame_clock);
+          g_object_unref (frame_clock);
+        }
+    }
+  else
+    {
+      if (window->frame_clock != NULL)
+        {
+          g_object_run_dispose (G_OBJECT (window->frame_clock));
+          gdk_window_set_frame_clock (window, NULL);
+        }
+    }
+
   /* We might have changed window type for a native windows, so we
      need to change the event mask too. */
   if (gdk_window_has_impl (window))
@@ -1999,6 +2071,12 @@ _gdk_window_destroy_hierarchy (GdkWindow *window,
                }
            }
 
+          if (window->frame_clock)
+            {
+              g_object_run_dispose (G_OBJECT (window->frame_clock));
+              gdk_window_set_frame_clock (window, NULL);
+            }
+
          gdk_window_free_paint_stack (window);
 
           if (window->background)
@@ -2669,7 +2747,7 @@ gdk_window_get_content (GdkWindow *window)
 
   g_return_val_if_fail (GDK_IS_WINDOW (window), 0);
 
-  surface = _gdk_window_ref_cairo_surface (window);
+  surface = gdk_window_ref_impl_surface (window);
   content = cairo_surface_get_content (surface);
   cairo_surface_destroy (surface);
 
@@ -2688,7 +2766,8 @@ gdk_window_get_content (GdkWindow *window)
  * directly to the window or a child window.
  */
 static gboolean
-gdk_window_begin_implicit_paint (GdkWindow *window, GdkRectangle *rect)
+gdk_window_begin_implicit_paint (GdkWindow *window, GdkRectangle *rect,
+                                gboolean with_alpha, guint8 alpha)
 {
   GdkWindowPaint *paint;
 
@@ -2697,8 +2776,7 @@ gdk_window_begin_implicit_paint (GdkWindow *window, GdkRectangle *rect)
   if (GDK_IS_PAINTABLE (window->impl))
     return FALSE; /* Implementation does double buffering */
 
-  if (window->paint_stack != NULL ||
-      window->implicit_paint != NULL)
+  if (window->paint_stack != NULL)
     return FALSE; /* Don't stack implicit paints */
 
   /* Never do implicit paints for foreign windows, they don't need
@@ -2713,13 +2791,14 @@ gdk_window_begin_implicit_paint (GdkWindow *window, GdkRectangle *rect)
   paint->region = cairo_region_create (); /* Empty */
   paint->uses_implicit = FALSE;
   paint->flushed = NULL;
+  paint->alpha = alpha;
   paint->surface = gdk_window_create_similar_surface (window,
-                                                      gdk_window_get_content (window),
+                                                     with_alpha ? CAIRO_CONTENT_COLOR_ALPHA : gdk_window_get_content (window),
                                                      MAX (rect->width, 1),
                                                       MAX (rect->height, 1));
   cairo_surface_set_device_offset (paint->surface, -rect->x, -rect->y);
 
-  window->implicit_paint = paint;
+  window->implicit_paint = g_slist_prepend (window->implicit_paint, paint);
 
   return TRUE;
 }
@@ -2744,60 +2823,66 @@ gdk_cairo_create_for_impl (GdkWindow *window)
   return cr;
 }
 
-/* Ensure that all content related to this (sub)window is pushed to the
-   native region. If there is an active paint then that area is not
-   pushed, in order to not show partially finished double buffers. */
+/* This is called whenever something is drawing directly to the
+ * window, bypassing the double buffering. When this happens we
+ * need to mark any the currently drawn data in the double buffer
+ * as invalid to avoid later drawing it back over the directly
+ * rendered pixels. We also need to mark this region as "flushed"
+ * so that if we later try to paint on it double-buffered we need
+ * to read back the on-window pixels rather than relying on what
+ * is in the current double-buffer pixmap.
+ *
+ * Note that this doesn't correctly handle the case where the
+ * non-double buffered drawing uses transparency and relies on
+ * what the windows below it draws. A fix for that would require
+ * drawing the existing double-buffered background to the window,
+ * but that causes ugly flashes. Non-double buffered drawing is
+ * typically only used in old code or when the drawed widget
+ * already has a double-buffering layer, and in these cases the
+ * pixels are opaque anyway. If you need transparency, don't
+ * disable double buffering.
+ */
 static void
 gdk_window_flush_implicit_paint (GdkWindow *window)
 {
   GdkWindow *impl_window;
   GdkWindowPaint *paint;
   cairo_region_t *region;
-  GSList *list;
+  GSList *l;
 
   impl_window = gdk_window_get_impl_window (window);
   if (impl_window->implicit_paint == NULL)
     return;
 
-  paint = impl_window->implicit_paint;
-  region = cairo_region_copy (window->clip_region_with_children);
+  /* There is no proper way to flush any non-toplevel implicit
+     paint, because direct rendering is not compatible with
+     opacity rendering, as it requires an opacity group.
 
-  cairo_region_translate (region, window->abs_x, window->abs_y);
+     We warn and flush just the outermost paint in this case.
+  */
+  l = g_slist_last (impl_window->implicit_paint);
+  if (l != impl_window->implicit_paint)
+    g_warning ("Non double buffered drawing not supported inside transparent windows");
 
-  if (paint->flushed == NULL)
-    paint->flushed = cairo_region_copy (region);
-  else
-    cairo_region_union (paint->flushed, region);
-
-  cairo_region_intersect (region, paint->region);
+  paint = l->data;
 
-  /* Don't flush active double buffers, as that may show partially done
-   * rendering */
-  for (list = window->paint_stack; list != NULL; list = list->next)
-    {
-      GdkWindowPaint *tmp_paint = list->data;
+  region = cairo_region_copy (window->clip_region_with_children);
+  cairo_region_translate (region, window->abs_x, window->abs_y);
 
-      cairo_region_subtract (region, tmp_paint->region);
-    }
+  /* Anything in the whole flushed window that was drawn is now
+     considered unpainted, so that we don't push it back at the
+     end of the implicit paint overwriting the directly rendered
+     pixels. */
+  cairo_region_subtract (paint->region, region);
 
-  if (!GDK_WINDOW_DESTROYED (window) && !cairo_region_is_empty (region))
+  /* Save flushed area so we can read it back if we draw over it later */
+  if (paint->flushed == NULL)
+    paint->flushed = region;
+  else
     {
-      cairo_t *cr;
-
-      /* Remove flushed region from the implicit paint */
-      cairo_region_subtract (paint->region, region);
-
-      /* Some regions are valid, push these to window now */
-      cr = gdk_cairo_create_for_impl (window);
-      gdk_cairo_region (cr, region);
-      cairo_clip (cr);
-      cairo_set_source_surface (cr, paint->surface, 0, 0);
-      cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
-      cairo_paint (cr);
-      cairo_destroy (cr);
+      cairo_region_union (paint->flushed, region);
+      cairo_region_destroy (region);
     }
-
-  cairo_region_destroy (region);
 }
 
 /* Ends an implicit paint, paired with gdk_window_begin_implicit_paint returning TRUE */
@@ -2810,24 +2895,69 @@ gdk_window_end_implicit_paint (GdkWindow *window)
 
   g_assert (window->implicit_paint != NULL);
 
-  paint = window->implicit_paint;
+  paint = window->implicit_paint->data;
 
-  window->implicit_paint = NULL;
+  window->implicit_paint = g_slist_delete_link (window->implicit_paint,
+                                               window->implicit_paint);
 
-  if (!GDK_WINDOW_DESTROYED (window) && !cairo_region_is_empty (paint->region))
+  if (!GDK_WINDOW_DESTROYED (window) && !cairo_region_is_empty (paint->region) && paint->alpha > 0)
     {
+      GdkWindowPaint *parent_paint = NULL;
       cairo_t *cr;
 
       /* Some regions are valid, push these to window now */
-      cr = gdk_cairo_create_for_impl (window);
+      if (window->implicit_paint)
+       {
+         parent_paint = window->implicit_paint->data;
+
+         /* If the toplevel implicit paint was flushed, restore it now
+            before we blend over it. */
+         if (parent_paint->flushed != NULL &&
+             !cairo_region_is_empty (parent_paint->flushed))
+           {
+             cairo_surface_t *source_surface = gdk_window_ref_impl_surface (window);
+             cairo_region_t *flushed = cairo_region_copy (parent_paint->flushed);
+
+             cr = cairo_create (parent_paint->surface);
+             cairo_set_source_surface (cr, source_surface, 0, 0);
+             cairo_surface_destroy (source_surface);
+
+             cairo_region_intersect (flushed, paint->region);
+             gdk_cairo_region (cr, flushed);
+             cairo_clip (cr);
+
+             cairo_region_subtract (parent_paint->flushed, flushed);
+             cairo_region_destroy (flushed);
+
+             cairo_paint (cr);
+             cairo_destroy (cr);
+           }
+
+         cr = cairo_create (parent_paint->surface);
+       }
+      else
+       cr = gdk_cairo_create_for_impl (window);
+
       gdk_cairo_region (cr, paint->region);
       cairo_clip (cr);
       cairo_set_source_surface (cr, paint->surface, 0, 0);
-      cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
-      cairo_paint (cr);
+      if (paint->alpha == 255)
+       {
+         cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
+         cairo_paint (cr);
+       }
+      else
+       {
+         cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
+         cairo_paint_with_alpha (cr, paint->alpha / 255.0);
+       }
+
       cairo_destroy (cr);
+
+      if (parent_paint)
+       cairo_region_union (parent_paint->region, paint->region);
     }
-  
+
   cairo_region_destroy (paint->region);
   if (paint->flushed)
     cairo_region_destroy (paint->flushed);
@@ -2929,7 +3059,7 @@ gdk_window_begin_paint_region (GdkWindow       *window,
     }
 
   impl_window = gdk_window_get_impl_window (window);
-  implicit_paint = impl_window->implicit_paint;
+  implicit_paint = impl_window->implicit_paint != NULL ? impl_window->implicit_paint->data : NULL;
 
   paint = g_new (GdkWindowPaint, 1);
   paint->region = cairo_region_copy (region);
@@ -2969,8 +3099,7 @@ gdk_window_begin_paint_region (GdkWindow       *window,
      by being drawn in back to front order. However, if implicit paints are not used, for
      instance if it was flushed due to a non-double-buffered paint in the middle of the
      expose we need to copy in the existing data here. */
-  if (!gdk_window_has_impl (window) &&
-      window->has_alpha_background &&
+  if (gdk_window_has_alpha (window) &&
       (!implicit_paint ||
        (implicit_paint && implicit_paint->flushed != NULL && !cairo_region_is_empty (implicit_paint->flushed))))
     {
@@ -3298,6 +3427,8 @@ move_region_on_impl (GdkWindow *impl_window,
                     cairo_region_t *region, /* In impl window coords */
                     int dx, int dy)
 {
+  GSList *l;
+
   if ((dx == 0 && dy == 0) ||
       cairo_region_is_empty (region))
     {
@@ -3336,9 +3467,9 @@ move_region_on_impl (GdkWindow *impl_window,
   /* If we're currently exposing this window, don't copy to this
      destination, as it will be overdrawn when the expose is done,
      instead invalidate it and repaint later. */
-  if (impl_window->implicit_paint)
+  for (l = impl_window->implicit_paint; l != NULL; l = l->next)
     {
-      GdkWindowPaint *implicit_paint = impl_window->implicit_paint;
+      GdkWindowPaint *implicit_paint = l->data;
       cairo_region_t *exposing;
 
       exposing = cairo_region_copy (implicit_paint->region);
@@ -3410,28 +3541,6 @@ gdk_window_flush (GdkWindow *window)
   gdk_window_flush_implicit_paint (window);
 }
 
-/* If we're about to move/resize or otherwise change the
- * hierarchy of a client side window in an impl and we're
- * called from an expose event handler then we need to
- * flush any already painted parts of the implicit paint
- * that are not part of the current paint, as these may
- * be used when scrolling or may overdraw the changes
- * caused by the hierarchy change.
- */
-static void
-gdk_window_flush_if_exposing (GdkWindow *window)
-{
-  GdkWindow *impl_window;
-
-  impl_window = gdk_window_get_impl_window (window);
-
-  /* If we're in an implicit paint (i.e. in an expose handler, flush
-     all the already finished exposes to get things to an uptodate state. */
-  if (impl_window->implicit_paint)
-    gdk_window_flush (window);
-}
-
-
 static void
 gdk_window_flush_recursive_helper (GdkWindow *window,
                                   GdkWindowImpl *impl)
@@ -3712,7 +3821,6 @@ gdk_cairo_create (GdkWindow *window)
 /* Code for dirty-region queueing
  */
 static GSList *update_windows = NULL;
-static guint update_idle = 0;
 static gboolean debug_updates = FALSE;
 
 static inline gboolean
@@ -3811,14 +3919,6 @@ gdk_window_remove_update_window (GdkWindow *window)
   update_windows = g_slist_remove (update_windows, window);
 }
 
-static gboolean
-gdk_window_update_idle (gpointer data)
-{
-  gdk_window_process_all_updates ();
-
-  return FALSE;
-}
-
 static gboolean
 gdk_window_is_toplevel_frozen (GdkWindow *window)
 {
@@ -3832,16 +3932,20 @@ gdk_window_is_toplevel_frozen (GdkWindow *window)
 static void
 gdk_window_schedule_update (GdkWindow *window)
 {
+  GdkFrameClock *frame_clock;
+
   if (window &&
       (window->update_freeze_count ||
        gdk_window_is_toplevel_frozen (window)))
     return;
 
-  if (!update_idle)
-    update_idle =
-      gdk_threads_add_idle_full (GDK_PRIORITY_REDRAW,
-                                gdk_window_update_idle,
-                                NULL, NULL);
+  /* If there's no frame clock (a foreign window), then the invalid
+   * region will just stick around unless gdk_window_process_updates()
+   * is called. */
+  frame_clock = gdk_window_get_frame_clock (window);
+  if (frame_clock)
+    gdk_frame_clock_request_phase (gdk_window_get_frame_clock (window),
+                                   GDK_FRAME_CLOCK_PHASE_PAINT);
 }
 
 void
@@ -3850,7 +3954,9 @@ _gdk_window_process_updates_recurse (GdkWindow *window,
 {
   GdkWindow *child;
   cairo_region_t *clipped_expose_region;
+  GdkRectangle clip_box;
   GList *l, *children;
+  gboolean end_implicit;
 
   if (cairo_region_is_empty (expose_region))
     return;
@@ -3859,6 +3965,17 @@ _gdk_window_process_updates_recurse (GdkWindow *window,
       gdk_window_has_impl (window))
     _gdk_window_add_damage ((GdkWindow *) window->impl_window, expose_region);
 
+  end_implicit = FALSE;
+  if (window->alpha != 255 && !gdk_window_has_impl (window))
+    {
+      if (window->alpha == 0)
+       return;
+
+      cairo_region_get_extents (expose_region, &clip_box);
+      clip_box.x += window->abs_x;
+      clip_box.y += window->abs_y;
+      end_implicit = gdk_window_begin_implicit_paint (window->impl_window, &clip_box, TRUE, window->alpha);
+    }
 
   /* Paint the window before the children, clipped to the window region
      with visible child windows removed */
@@ -3931,6 +4048,8 @@ _gdk_window_process_updates_recurse (GdkWindow *window,
 
   g_list_free_full (children, g_object_unref);
 
+  if (end_implicit)
+    gdk_window_end_implicit_paint (window->impl_window);
 }
 
 /* Process and remove any invalid area on the native window by creating
@@ -4052,7 +4171,7 @@ gdk_window_process_updates_internal (GdkWindow *window)
           */
 
          cairo_region_get_extents (update_area, &clip_box);
-         end_implicit = gdk_window_begin_implicit_paint (window, &clip_box);
+         end_implicit = gdk_window_begin_implicit_paint (window, &clip_box, FALSE, 255);
          expose_region = cairo_region_copy (update_area);
          impl_class = GDK_WINDOW_IMPL_GET_CLASS (window->impl);
          if (!end_implicit)
@@ -4078,7 +4197,7 @@ gdk_window_process_updates_internal (GdkWindow *window)
               * be to late to anti-expose now. Since this is merely an
               * optimization we just avoid doing it at all in that case.
               */
-             if (window->implicit_paint != NULL && !window->implicit_paint->flushed)
+             if (window->implicit_paint != NULL && !((GdkWindowPaint *)window->implicit_paint->data)->flushed)
                 save_region = impl_class->queue_antiexpose (window, update_area);
 
              gdk_window_end_implicit_paint (window);
@@ -4169,18 +4288,13 @@ gdk_window_process_all_updates (void)
       /* We can't do this now since that would recurse, so
         delay it until after the recursion is done. */
       got_recursive_update = TRUE;
-      update_idle = 0;
       return;
     }
 
   in_process_all_updates = TRUE;
   got_recursive_update = FALSE;
 
-  if (update_idle)
-    g_source_remove (update_idle);
-
   update_windows = NULL;
-  update_idle = 0;
 
   before_process_all_updates ();
 
@@ -4215,31 +4329,20 @@ gdk_window_process_all_updates (void)
      redraw now so that it eventually happens,
      otherwise we could miss an update if nothing
      else schedules an update. */
-  if (got_recursive_update && !update_idle)
-    update_idle =
-      gdk_threads_add_idle_full (GDK_PRIORITY_REDRAW,
-                                gdk_window_update_idle,
-                                NULL, NULL);
+  if (got_recursive_update)
+    gdk_window_schedule_update (NULL);
 }
 
-/**
- * gdk_window_process_updates:
- * @window: a #GdkWindow
- * @update_children: whether to also process updates for child windows
- *
- * Sends one or more expose events to @window. The areas in each
- * expose event will cover the entire update area for the window (see
- * gdk_window_invalidate_region() for details). Normally GDK calls
- * gdk_window_process_all_updates() on your behalf, so there's no
- * need to call this function unless you want to force expose events
- * to be delivered immediately and synchronously (vs. the usual
- * case, where GDK delivers them in an idle handler). Occasionally
- * this is useful to produce nicer scrolling behavior, for example.
- *
- **/
-void
-gdk_window_process_updates (GdkWindow *window,
-                           gboolean   update_children)
+
+enum {
+  PROCESS_UPDATES_NO_RECURSE,
+  PROCESS_UPDATES_WITH_ALL_CHILDREN,
+  PROCESS_UPDATES_WITH_SAME_CLOCK_CHILDREN
+};
+
+static void
+gdk_window_process_updates_with_mode (GdkWindow     *window,
+                                      int            recurse_mode)
 {
   GdkWindow *impl_window;
 
@@ -4265,7 +4368,7 @@ gdk_window_process_updates (GdkWindow *window,
       gdk_window_remove_update_window ((GdkWindow *)impl_window);
     }
 
-  if (update_children)
+  if (recurse_mode != PROCESS_UPDATES_NO_RECURSE)
     {
       /* process updates in reverse stacking order so composition or
        * painting over achieves the desired effect for offscreen windows
@@ -4277,8 +4380,14 @@ gdk_window_process_updates (GdkWindow *window,
 
       for (node = g_list_last (children); node; node = node->prev)
        {
-         gdk_window_process_updates (node->data, TRUE);
-         g_object_unref (node->data);
+          GdkWindow *child = node->data;
+          if (recurse_mode == PROCESS_UPDATES_WITH_ALL_CHILDREN ||
+              (recurse_mode == PROCESS_UPDATES_WITH_SAME_CLOCK_CHILDREN &&
+               child->frame_clock == NULL))
+            {
+              gdk_window_process_updates (child, TRUE);
+            }
+         g_object_unref (child);
        }
 
       g_list_free (children);
@@ -4287,6 +4396,33 @@ gdk_window_process_updates (GdkWindow *window,
   g_object_unref (window);
 }
 
+/**
+ * gdk_window_process_updates:
+ * @window: a #GdkWindow
+ * @update_children: whether to also process updates for child windows
+ *
+ * Sends one or more expose events to @window. The areas in each
+ * expose event will cover the entire update area for the window (see
+ * gdk_window_invalidate_region() for details). Normally GDK calls
+ * gdk_window_process_all_updates() on your behalf, so there's no
+ * need to call this function unless you want to force expose events
+ * to be delivered immediately and synchronously (vs. the usual
+ * case, where GDK delivers them in an idle handler). Occasionally
+ * this is useful to produce nicer scrolling behavior, for example.
+ *
+ **/
+void
+gdk_window_process_updates (GdkWindow *window,
+                           gboolean   update_children)
+{
+  g_return_if_fail (GDK_IS_WINDOW (window));
+
+  return gdk_window_process_updates_with_mode (window,
+                                               update_children ?
+                                               PROCESS_UPDATES_WITH_ALL_CHILDREN :
+                                               PROCESS_UPDATES_NO_RECURSE);
+}
+
 static void
 gdk_window_invalidate_rect_full (GdkWindow          *window,
                                  const GdkRectangle *rect,
@@ -4773,6 +4909,7 @@ gdk_window_freeze_toplevel_updates_libgtk_only (GdkWindow *window)
   g_return_if_fail (window->window_type != GDK_WINDOW_CHILD);
 
   window->update_and_descendants_freeze_count++;
+  _gdk_frame_clock_freeze (gdk_window_get_frame_clock (window));
 }
 
 /**
@@ -4793,6 +4930,7 @@ gdk_window_thaw_toplevel_updates_libgtk_only (GdkWindow *window)
   g_return_if_fail (window->update_and_descendants_freeze_count > 0);
 
   window->update_and_descendants_freeze_count--;
+  _gdk_frame_clock_thaw (gdk_window_get_frame_clock (window));
 
   gdk_window_schedule_update (window);
 }
@@ -5332,8 +5470,6 @@ gdk_window_raise (GdkWindow *window)
   if (window->destroyed)
     return;
 
-  gdk_window_flush_if_exposing (window);
-
   /* Keep children in (reverse) stacking order */
   gdk_window_raise_internal (window);
 
@@ -5454,8 +5590,6 @@ gdk_window_lower (GdkWindow *window)
   if (window->destroyed)
     return;
 
-  gdk_window_flush_if_exposing (window);
-
   /* Keep children in (reverse) stacking order */
   gdk_window_lower_internal (window);
 
@@ -5511,8 +5645,6 @@ gdk_window_restack (GdkWindow     *window,
       return;
     }
 
-  gdk_window_flush_if_exposing (window);
-
   if (gdk_window_is_toplevel (window))
     {
       g_return_if_fail (gdk_window_is_toplevel (sibling));
@@ -6068,8 +6200,6 @@ gdk_window_move_resize_internal (GdkWindow *window,
        window->y == y)))
     return;
 
-  gdk_window_flush_if_exposing (window);
-
   /* Handle child windows */
 
   expose = FALSE;
@@ -6171,7 +6301,7 @@ gdk_window_move_resize_internal (GdkWindow *window,
        * Everything in the old and new regions that is not copied must be
        * invalidated (including children) as this is newly exposed
        */
-      if (window->has_alpha_background)
+      if (gdk_window_has_alpha (window))
        copy_area = cairo_region_create (); /* Copy nothing for alpha windows */
       else
        copy_area = cairo_region_copy (new_region);
@@ -6354,8 +6484,6 @@ gdk_window_scroll (GdkWindow *window,
   if (window->destroyed)
     return;
 
-  gdk_window_flush_if_exposing (window);
-
   old_layered_area = cairo_region_copy (window->layered_region);
   old_native_child_region = collect_native_child_region (window, FALSE);
   if (old_native_child_region)
@@ -6397,7 +6525,7 @@ gdk_window_scroll (GdkWindow *window,
   impl_window = gdk_window_get_impl_window (window);
 
   /* Calculate the area that can be gotten by copying the old area */
-  if (window->has_alpha_background)
+  if (gdk_window_has_alpha (window))
     copy_area = cairo_region_create (); /* Copy nothing for alpha windows */
   else
     copy_area = cairo_region_copy (window->clip_region);
@@ -6485,7 +6613,7 @@ gdk_window_move_region (GdkWindow       *window,
   impl_window = gdk_window_get_impl_window (window);
 
   /* compute source regions */
-  if (window->has_alpha_background)
+  if (gdk_window_has_alpha (window))
     copy_area = cairo_region_create (); /* Copy nothing for alpha windows */
   else
     copy_area = cairo_region_copy (region);
@@ -6555,8 +6683,8 @@ gdk_window_set_background (GdkWindow      *window,
  * See also gdk_window_set_background_pattern().
  **/
 void
-gdk_window_set_background_rgba (GdkWindow *window,
-                                GdkRGBA   *rgba)
+gdk_window_set_background_rgba (GdkWindow     *window,
+                                const GdkRGBA *rgba)
 {
   cairo_pattern_t *pattern;
 
@@ -8115,7 +8243,7 @@ static const guint type_masks[] = {
   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 */
+  GDK_SCROLL_MASK | GDK_SMOOTH_SCROLL_MASK,/* GDK_SCROLL= 31 */
   0, /* GDK_WINDOW_STATE = 32 */
   0, /* GDK_SETTING = 33 */
   0, /* GDK_OWNER_CHANGE = 34 */
@@ -8156,6 +8284,8 @@ is_button_type (GdkEventType type)
         type == GDK_2BUTTON_PRESS ||
         type == GDK_3BUTTON_PRESS ||
         type == GDK_BUTTON_RELEASE ||
+         type == GDK_TOUCH_BEGIN ||
+         type == GDK_TOUCH_END ||
         type == GDK_SCROLL;
 }
 
@@ -8163,10 +8293,20 @@ static gboolean
 is_motion_type (GdkEventType type)
 {
   return type == GDK_MOTION_NOTIFY ||
+         type == GDK_TOUCH_UPDATE ||
         type == GDK_ENTER_NOTIFY ||
         type == GDK_LEAVE_NOTIFY;
 }
 
+static gboolean
+is_touch_type (GdkEventType type)
+{
+  return type == GDK_TOUCH_BEGIN ||
+         type == GDK_TOUCH_UPDATE ||
+         type == GDK_TOUCH_END ||
+         type == GDK_TOUCH_CANCEL;
+}
+
 static GdkWindow *
 find_common_ancestor (GdkWindow *win1,
                      GdkWindow *win2)
@@ -8248,7 +8388,6 @@ _gdk_make_event (GdkWindow    *window,
       event->touch.state = the_state;
       break;
 
-
     case GDK_SCROLL:
       event->scroll.time = the_time;
       event->scroll.state = the_state;
@@ -8337,14 +8476,27 @@ send_crossing_event (GdkDisplay                 *display,
   GdkEvent *event;
   guint32 window_event_mask, type_event_mask;
   GdkDeviceGrabInfo *grab;
+  GdkTouchGrabInfo *touch_grab = NULL;
   GdkPointerWindowInfo *pointer_info;
   gboolean block_event = FALSE;
+  GdkEventSequence *sequence;
 
   grab = _gdk_display_has_device_grab (display, device, serial);
   pointer_info = _gdk_display_get_pointer_info (display, device);
 
-  if (grab != NULL &&
-      !grab->owner_events)
+  sequence = gdk_event_get_event_sequence (event_in_queue);
+  if (sequence)
+    touch_grab = _gdk_display_has_touch_grab (display, device, sequence, serial);
+
+  if (touch_grab)
+    {
+      if (window != touch_grab->window)
+        return;
+
+      window_event_mask = touch_grab->event_mask;
+    }
+  else if (grab != NULL &&
+           !grab->owner_events)
     {
       /* !owner_event => only report events wrt grab window, ignore rest */
       if ((GdkWindow *)window != grab->window)
@@ -8355,10 +8507,12 @@ send_crossing_event (GdkDisplay                 *display,
     window_event_mask = window->event_mask;
 
   if (type == GDK_ENTER_NOTIFY &&
-      pointer_info->need_touch_press_enter &&
+      (pointer_info->need_touch_press_enter ||
+       gdk_device_get_source (source_device) == GDK_SOURCE_TOUCHSCREEN) &&
       mode != GDK_CROSSING_TOUCH_BEGIN &&
       mode != GDK_CROSSING_TOUCH_END)
     {
+      pointer_info->need_touch_press_enter = TRUE;
       block_event = TRUE;
     }
   else if (type == GDK_LEAVE_NOTIFY)
@@ -9067,10 +9221,17 @@ do_synthesize_crossing_event (gpointer data)
                                 serial);
           if (new_window_under_pointer != pointer_info->window_under_pointer)
             {
+              GdkDevice *source_device;
+
+              if (pointer_info->last_slave)
+                source_device = pointer_info->last_slave;
+              else
+                source_device = device;
+
               _gdk_synthesize_crossing_events (display,
                                                pointer_info->window_under_pointer,
                                                new_window_under_pointer,
-                                               device, pointer_info->last_slave,
+                                               device, source_device,
                                                GDK_CROSSING_NORMAL,
                                                pointer_info->toplevel_x,
                                                pointer_info->toplevel_y,
@@ -9109,17 +9270,54 @@ _gdk_synthesize_crossing_events_for_geometry_change (GdkWindow *changed_window)
 static GdkWindow *
 get_event_window (GdkDisplay                 *display,
                   GdkDevice                  *device,
-                 GdkWindow                  *pointer_window,
-                 GdkEventType                type,
-                 GdkModifierType             mask,
-                 guint                      *evmask_out,
-                 gulong                      serial)
-{
-  guint evmask;
+                  GdkEventSequence           *sequence,
+                  GdkWindow                  *pointer_window,
+                  GdkEventType                type,
+                  GdkModifierType             mask,
+                  guint                      *evmask_out,
+                  gboolean                    pointer_emulated,
+                  gulong                      serial)
+{
+  guint evmask, emulated_mask = 0;
   GdkWindow *grab_window;
   GdkDeviceGrabInfo *grab;
+  GdkTouchGrabInfo *touch_grab;
 
-  grab = _gdk_display_has_device_grab (display, device, serial);
+  touch_grab = _gdk_display_has_touch_grab (display, device, sequence, serial);
+  grab = _gdk_display_get_last_device_grab (display, device);
+
+  if (is_touch_type (type) && pointer_emulated)
+    {
+      switch (type)
+        {
+        case GDK_TOUCH_BEGIN:
+          emulated_mask |= GDK_BUTTON_PRESS_MASK;
+          break;
+        case GDK_TOUCH_UPDATE:
+          emulated_mask |= GDK_POINTER_MOTION_MASK;
+          break;
+        case GDK_TOUCH_END:
+          emulated_mask |= GDK_BUTTON_RELEASE_MASK;
+        default:
+          break;
+        }
+    }
+
+  if (touch_grab != NULL &&
+      (!grab || grab->implicit || touch_grab->serial >= grab->serial_start))
+    {
+      evmask = touch_grab->event_mask;
+      evmask = update_evmask_for_button_motion (evmask, mask);
+
+      if (evmask & (type_masks[type] | emulated_mask))
+        {
+          if (evmask_out)
+            *evmask_out = evmask;
+          return touch_grab->window;
+        }
+      else
+        return NULL;
+    }
 
   if (grab != NULL && !grab->owner_events)
     {
@@ -9128,7 +9326,7 @@ get_event_window (GdkDisplay                 *display,
 
       grab_window = grab->window;
 
-      if (evmask & type_masks[type])
+      if (evmask & (type_masks[type] | emulated_mask))
        {
          if (evmask_out)
            *evmask_out = evmask;
@@ -9143,7 +9341,7 @@ get_event_window (GdkDisplay                 *display,
       evmask = pointer_window->event_mask;
       evmask = update_evmask_for_button_motion (evmask, mask);
 
-      if (evmask & type_masks[type])
+      if (evmask & (type_masks[type] | emulated_mask))
        {
          if (evmask_out)
            *evmask_out = evmask;
@@ -9159,7 +9357,7 @@ get_event_window (GdkDisplay                 *display,
       evmask = grab->event_mask;
       evmask = update_evmask_for_button_motion (evmask, mask);
 
-      if (evmask & type_masks[type])
+      if (evmask & (type_masks[type] | emulated_mask))
        {
          if (evmask_out)
            *evmask_out = evmask;
@@ -9186,7 +9384,9 @@ proxy_pointer_event (GdkDisplay                 *display,
   gdouble toplevel_x, toplevel_y;
   guint32 time_;
   gboolean non_linear, need_synthetic_enter = FALSE;
+  gint event_type;
 
+  event_type = source_event->type;
   event_window = source_event->any.window;
   gdk_event_get_coords (source_event, &toplevel_x, &toplevel_y);
   gdk_event_get_state (source_event, &state);
@@ -9207,8 +9407,8 @@ proxy_pointer_event (GdkDisplay                 *display,
 
   if (pointer_info->need_touch_press_enter &&
       gdk_device_get_source (pointer_info->last_slave) != GDK_SOURCE_TOUCHSCREEN &&
-      gdk_device_get_source (pointer_info->last_slave) != GDK_SOURCE_TOUCHPAD)
-
+      (source_event->type != GDK_TOUCH_UPDATE ||
+       _gdk_event_get_pointer_emulated (source_event)))
     {
       pointer_info->need_touch_press_enter = FALSE;
       need_synthetic_enter = TRUE;
@@ -9320,20 +9520,49 @@ proxy_pointer_event (GdkDisplay                 *display,
                                       serial, non_linear);
       _gdk_display_set_window_under_pointer (display, device, pointer_window);
     }
-  else if (source_event->type == GDK_MOTION_NOTIFY)
+  else if (source_event->type == GDK_MOTION_NOTIFY ||
+           source_event->type == GDK_TOUCH_UPDATE)
     {
       GdkWindow *event_win;
       guint evmask;
       gboolean is_hint;
+      GdkEventSequence *sequence;
+
+      sequence = gdk_event_get_event_sequence (source_event);
 
       event_win = get_event_window (display,
                                     device,
+                                    sequence,
                                     pointer_window,
                                     source_event->type,
                                     state,
                                     &evmask,
+                                    _gdk_event_get_pointer_emulated (source_event),
                                     serial);
 
+      if (event_type == GDK_TOUCH_UPDATE)
+        {
+          if (_gdk_event_get_pointer_emulated (source_event))
+            {
+              /* Touch events emulating pointer events are transformed back
+               * to pointer events if:
+               * 1 - The event window doesn't select for touch events
+               * 2 - There's no touch grab for this sequence, which means
+               *     it was started as a pointer sequence, but a device
+               *     grab added touch events afterwards, the sequence must
+               *     not mutate in this case.
+               */
+              if ((evmask & GDK_TOUCH_MASK) == 0 ||
+                  !_gdk_display_has_touch_grab (display, device, sequence, serial))
+                event_type = GDK_MOTION_NOTIFY;
+            }
+          else if ((evmask & GDK_TOUCH_MASK) == 0)
+            return TRUE;
+        }
+
+      if (is_touch_type (source_event->type) && !is_touch_type (event_type))
+        state |= GDK_BUTTON1_MASK;
+
       if (event_win &&
           gdk_device_get_device_type (device) != GDK_DEVICE_TYPE_MASTER &&
           gdk_window_get_device_events (event_win, device) == 0)
@@ -9351,9 +9580,11 @@ proxy_pointer_event (GdkDisplay                 *display,
                                          toplevel_x, toplevel_y,
                                          state, time_, NULL,
                                          serial, FALSE);
+
       is_hint = FALSE;
 
       if (event_win &&
+          event_type == GDK_MOTION_NOTIFY &&
          (evmask & GDK_POINTER_MOTION_HINT_MASK))
        {
           gulong *device_serial;
@@ -9371,22 +9602,56 @@ proxy_pointer_event (GdkDisplay                 *display,
            }
        }
 
-      if (event_win && !display->ignore_core_events)
+      if (!event_win)
+        return TRUE;
+
+      event = gdk_event_new (event_type);
+      event->any.window = g_object_ref (event_win);
+      event->any.send_event = source_event->any.send_event;
+
+      gdk_event_set_device (event, gdk_event_get_device (source_event));
+      gdk_event_set_source_device (event, source_device);
+
+      if (event_type == GDK_TOUCH_UPDATE)
        {
-         event = _gdk_make_event (event_win, GDK_MOTION_NOTIFY, source_event, FALSE);
-         event->motion.time = time_;
+         event->touch.time = time_;
+         event->touch.state = state | GDK_BUTTON1_MASK;
+         event->touch.sequence = source_event->touch.sequence;
+         event->touch.emulating_pointer = source_event->touch.emulating_pointer;
          convert_toplevel_coords_to_window (event_win,
                                             toplevel_x, toplevel_y,
-                                            &event->motion.x, &event->motion.y);
-         event->motion.x_root = source_event->motion.x_root;
-         event->motion.y_root = source_event->motion.y_root;
+                                            &event->touch.x, &event->touch.y);
+         gdk_event_get_root_coords (source_event,
+                                    &event->touch.x_root,
+                                    &event->touch.y_root);
+
+         event->touch.axes = g_memdup (source_event->touch.axes,
+                                       sizeof (gdouble) * gdk_device_get_n_axes (source_event->touch.device));
+       }
+      else
+       {
+         event->motion.time = time_;
          event->motion.state = state;
          event->motion.is_hint = is_hint;
-         event->motion.device = source_event->motion.device;
-          event->motion.axes = g_memdup (source_event->motion.axes,
-                                         sizeof (gdouble) * gdk_device_get_n_axes (source_event->motion.device));
-          gdk_event_set_source_device (event, source_device);
+
+         convert_toplevel_coords_to_window (event_win,
+                                            toplevel_x, toplevel_y,
+                                            &event->motion.x, &event->motion.y);
+         gdk_event_get_root_coords (source_event,
+                                    &event->motion.x_root,
+                                    &event->motion.y_root);
+
+         if (is_touch_type (source_event->type))
+           event->motion.axes = g_memdup (source_event->touch.axes,
+                                          sizeof (gdouble) * gdk_device_get_n_axes (source_event->touch.device));
+         else
+           event->motion.axes = g_memdup (source_event->motion.axes,
+                                          sizeof (gdouble) * gdk_device_get_n_axes (source_event->motion.device));
        }
+
+      /* Just insert the event */
+      _gdk_event_queue_insert_after (gdk_window_get_display (event_win),
+                                    source_event, event);
     }
 
   /* unlink all move events from queue.
@@ -9410,6 +9675,7 @@ proxy_button_event (GdkEvent *source_event,
   GdkWindow *parent;
   GdkEvent *event;
   GdkPointerWindowInfo *pointer_info;
+  GdkDeviceGrabInfo *pointer_grab;
   guint state;
   guint32 time_;
   GdkEventType type;
@@ -9417,6 +9683,8 @@ proxy_button_event (GdkEvent *source_event,
   GdkDisplay *display;
   GdkWindow *w;
   GdkDevice *device, *source_device;
+  GdkEventMask evmask;
+  GdkEventSequence *sequence;
 
   type = source_event->any.type;
   event_window = source_event->any.window;
@@ -9429,11 +9697,18 @@ proxy_button_event (GdkEvent *source_event,
   toplevel_window = convert_native_coords_to_toplevel (event_window,
                                                       toplevel_x, toplevel_y,
                                                       &toplevel_x, &toplevel_y);
+
+  sequence = gdk_event_get_event_sequence (source_event);
+
   pointer_info = _gdk_display_get_pointer_info (display, device);
+  pointer_grab = _gdk_display_has_device_grab (display, device, serial);
 
-  if (type == GDK_BUTTON_PRESS &&
+  if ((type == GDK_BUTTON_PRESS ||
+       type == GDK_TOUCH_BEGIN) &&
       !source_event->any.send_event &&
-      _gdk_display_has_device_grab (display, device, serial) == NULL)
+      (!pointer_grab ||
+       (type == GDK_TOUCH_BEGIN && pointer_grab->implicit &&
+        !_gdk_event_get_pointer_emulated (source_event))))
     {
       pointer_window =
        _gdk_window_find_descendant_at (toplevel_window,
@@ -9446,23 +9721,46 @@ proxy_button_event (GdkEvent *source_event,
             (parent = get_event_parent (w)) != NULL &&
             parent->window_type != GDK_WINDOW_ROOT)
        {
-         if (w->event_mask & GDK_BUTTON_PRESS_MASK)
+         if (w->event_mask & GDK_BUTTON_PRESS_MASK &&
+              (type == GDK_BUTTON_PRESS ||
+               _gdk_event_get_pointer_emulated (source_event)))
            break;
+
+          if (type == GDK_TOUCH_BEGIN &&
+              w->event_mask & GDK_TOUCH_MASK)
+            break;
+
          w = parent;
        }
-      pointer_window = (GdkWindow *)w;
-
-      _gdk_display_add_device_grab  (display,
-                                     device,
-                                     pointer_window,
-                                     event_window,
-                                     GDK_OWNERSHIP_NONE,
-                                     FALSE,
-                                     gdk_window_get_events (pointer_window),
-                                     serial,
-                                    time_,
-                                     TRUE);
-      _gdk_display_device_grab_update (display, device, source_device, serial);
+      pointer_window = w;
+
+      if (pointer_window)
+        {
+          if (type == GDK_TOUCH_BEGIN &&
+              pointer_window->event_mask & GDK_TOUCH_MASK)
+            {
+              _gdk_display_add_touch_grab (display, device, sequence,
+                                           pointer_window, event_window,
+                                           gdk_window_get_events (pointer_window),
+                                           serial, time_);
+            }
+          else if (type == GDK_BUTTON_PRESS ||
+                   _gdk_event_get_pointer_emulated (source_event))
+            {
+              _gdk_display_add_device_grab  (display,
+                                             device,
+                                             pointer_window,
+                                             event_window,
+                                             GDK_OWNERSHIP_NONE,
+                                             FALSE,
+                                             gdk_window_get_events (pointer_window),
+                                             serial,
+                                             time_,
+                                             TRUE);
+              _gdk_display_device_grab_update (display, device,
+                                               source_device, serial);
+            }
+        }
     }
 
   pointer_window = get_pointer_window (display, toplevel_window, device,
@@ -9471,18 +9769,43 @@ proxy_button_event (GdkEvent *source_event,
 
   event_win = get_event_window (display,
                                 device,
-                               pointer_window,
-                               type, state,
-                               NULL, serial);
+                                sequence,
+                                pointer_window,
+                                type, state,
+                                &evmask,
+                                _gdk_event_get_pointer_emulated (source_event),
+                                serial);
+
+  if (type == GDK_TOUCH_BEGIN || type == GDK_TOUCH_END)
+    {
+      if (_gdk_event_get_pointer_emulated (source_event))
+        {
+          if ((evmask & GDK_TOUCH_MASK) == 0 ||
+              !_gdk_display_has_touch_grab (display, device, sequence, serial))
+            {
+              if (type == GDK_TOUCH_BEGIN)
+                type = GDK_BUTTON_PRESS;
+              else if (type == GDK_TOUCH_END)
+                type = GDK_BUTTON_RELEASE;
+            }
+        }
+      else if ((evmask & GDK_TOUCH_MASK) == 0)
+        return TRUE;
+    }
+
+  if (source_event->type == GDK_TOUCH_END && !is_touch_type (type))
+    state |= GDK_BUTTON1_MASK;
 
-  if (event_win == NULL || display->ignore_core_events)
+  if (event_win == NULL)
     return TRUE;
 
   if (gdk_device_get_device_type (device) != GDK_DEVICE_TYPE_MASTER &&
       gdk_window_get_device_events (event_win, device) == 0)
     return TRUE;
 
-  if (type == GDK_BUTTON_PRESS &&
+  if ((type == GDK_BUTTON_PRESS ||
+       (type == GDK_TOUCH_BEGIN &&
+        _gdk_event_get_pointer_emulated (source_event))) &&
       pointer_info->need_touch_press_enter)
     {
       GdkCrossingMode mode;
@@ -9491,8 +9814,7 @@ proxy_button_event (GdkEvent *source_event,
        * which synthesized a leave notify event, so synthesize another enter
        * notify to tell the pointer is on the window.
        */
-      if (gdk_device_get_source (source_device) == GDK_SOURCE_TOUCHSCREEN ||
-          gdk_device_get_source (source_device) == GDK_SOURCE_TOUCHPAD)
+      if (gdk_device_get_source (source_device) == GDK_SOURCE_TOUCHSCREEN)
         mode = GDK_CROSSING_TOUCH_BEGIN;
       else
         mode = GDK_CROSSING_DEVICE_SWITCH;
@@ -9506,6 +9828,13 @@ proxy_button_event (GdkEvent *source_event,
                                        state, time_, source_event,
                                        serial, FALSE);
     }
+  else if (type == GDK_SCROLL &&
+           (((evmask & GDK_SMOOTH_SCROLL_MASK) == 0 &&
+             source_event->scroll.direction == GDK_SCROLL_SMOOTH) ||
+            ((evmask & GDK_SMOOTH_SCROLL_MASK) != 0 &&
+             source_event->scroll.direction != GDK_SCROLL_SMOOTH &&
+             _gdk_event_get_pointer_emulated (source_event))))
+    return FALSE;
 
   event = _gdk_make_event (event_win, type, source_event, FALSE);
 
@@ -9517,21 +9846,70 @@ proxy_button_event (GdkEvent *source_event,
       convert_toplevel_coords_to_window (event_win,
                                         toplevel_x, toplevel_y,
                                         &event->button.x, &event->button.y);
-      event->button.x_root = source_event->button.x_root;
-      event->button.y_root = source_event->button.y_root;
-      event->button.state = state;
-      event->button.device = source_event->button.device;
-      event->button.axes = g_memdup (source_event->button.axes,
-                                     sizeof (gdouble) * gdk_device_get_n_axes (source_event->button.device));
-
+      gdk_event_get_root_coords (source_event,
+                                &event->button.x_root,
+                                &event->button.y_root);
+      gdk_event_set_device (event, gdk_event_get_device (source_event));
       gdk_event_set_source_device (event, source_device);
 
+      if (is_touch_type (source_event->type))
+        {
+          if (type == GDK_BUTTON_RELEASE)
+            event->button.state |= GDK_BUTTON1_MASK;
+         event->button.button = 1;
+         event->button.axes = g_memdup (source_event->touch.axes,
+                                        sizeof (gdouble) * gdk_device_get_n_axes (source_event->touch.device));
+       }
+      else
+       {
+         event->button.button = source_event->button.button;
+         event->button.axes = g_memdup (source_event->button.axes,
+                                        sizeof (gdouble) * gdk_device_get_n_axes (source_event->button.device));
+       }
+
       if (type == GDK_BUTTON_PRESS)
         _gdk_event_button_generate (display, event);
-      else if (type == GDK_BUTTON_RELEASE &&
+      else if ((type == GDK_BUTTON_RELEASE ||
+                (type == GDK_TOUCH_END &&
+                 _gdk_event_get_pointer_emulated (source_event))) &&
                pointer_window == pointer_info->window_under_pointer &&
-               (gdk_device_get_source (source_device) == GDK_SOURCE_TOUCHSCREEN ||
-                gdk_device_get_source (source_device) == GDK_SOURCE_TOUCHPAD))
+               gdk_device_get_source (source_device) == GDK_SOURCE_TOUCHSCREEN)
+        {
+          /* Synthesize a leave notify event
+           * whenever a touch device is released
+           */
+          pointer_info->need_touch_press_enter = TRUE;
+          _gdk_synthesize_crossing_events (display,
+                                           pointer_window, NULL,
+                                           device, source_device,
+                                           GDK_CROSSING_TOUCH_END,
+                                           toplevel_x, toplevel_y,
+                                           state, time_, NULL,
+                                           serial, FALSE);
+        }
+      return TRUE;
+
+    case GDK_TOUCH_BEGIN:
+    case GDK_TOUCH_END:
+      convert_toplevel_coords_to_window (event_win,
+                                         toplevel_x, toplevel_y,
+                                         &event->button.x, &event->button.y);
+      gdk_event_get_root_coords (source_event,
+                                &event->touch.x_root,
+                                &event->touch.y_root);
+      event->touch.state = state;
+      event->touch.device = source_event->touch.device;
+      event->touch.axes = g_memdup (source_event->touch.axes,
+                                     sizeof (gdouble) * gdk_device_get_n_axes (source_event->touch.device));
+      event->touch.sequence = source_event->touch.sequence;
+      event->touch.emulating_pointer = source_event->touch.emulating_pointer;
+
+      gdk_event_set_source_device (event, source_device);
+
+      if ((type == GDK_TOUCH_END &&
+           _gdk_event_get_pointer_emulated (source_event)) &&
+           pointer_window == pointer_info->window_under_pointer &&
+           gdk_device_get_source (source_device) == GDK_SOURCE_TOUCHSCREEN)
         {
           /* Synthesize a leave notify event
            * whenever a touch device is released
@@ -9556,6 +9934,8 @@ proxy_button_event (GdkEvent *source_event,
       event->scroll.y_root = source_event->scroll.y_root;
       event->scroll.state = state;
       event->scroll.device = source_event->scroll.device;
+      event->scroll.delta_x = source_event->scroll.delta_x;
+      event->scroll.delta_y = source_event->scroll.delta_y;
       gdk_event_set_source_device (event, source_device);
       return TRUE;
 
@@ -9567,11 +9947,16 @@ proxy_button_event (GdkEvent *source_event,
 }
 
 #ifdef DEBUG_WINDOW_PRINTING
+
+#ifdef GDK_WINDOWING_X11
+#include "x11/gdkx.h"
+#endif
+
 static void
 gdk_window_print (GdkWindow *window,
                  int indent)
 {
-  GdkRectangle r;
+  char *s;
   const char *window_types[] = {
     "root",
     "toplevel",
@@ -9610,11 +9995,12 @@ gdk_window_print (GdkWindow *window,
   g_print (" abs[%d,%d]",
           window->abs_x, window->abs_y);
 
-  cairo_region_get_extents (window->clip_region, &r);
-  if (cairo_region_is_empty (window->clip_region))
-    g_print (" clipbox[empty]");
-  else
-    g_print (" clipbox[%d,%d %dx%d]", r.x, r.y, r.width, r.height);
+  if (window->alpha != 255)
+    g_print (" alpha[%d]",
+          window->alpha);
+
+  s = print_region (window->clip_region);
+  g_print (" clipbox[%s]", s);
 
   g_print ("\n");
 }
@@ -9646,7 +10032,7 @@ _gdk_windowing_got_event (GdkDisplay *display,
 {
   GdkWindow *event_window;
   gdouble x, y;
-  gboolean unlink_event;
+  gboolean unlink_event = FALSE;
   GdkDeviceGrabInfo *button_release_grab;
   GdkPointerWindowInfo *pointer_info = NULL;
   GdkDevice *device, *source_device;
@@ -9669,7 +10055,7 @@ _gdk_windowing_got_event (GdkDisplay *display,
           if (source_device != pointer_info->last_slave &&
               gdk_device_get_device_type (source_device) == GDK_DEVICE_TYPE_SLAVE)
             pointer_info->last_slave = source_device;
-          else
+          else if (pointer_info->last_slave)
             source_device = pointer_info->last_slave;
         }
 
@@ -9689,7 +10075,7 @@ _gdk_windowing_got_event (GdkDisplay *display,
 
   event_window = event->any.window;
   if (!event_window)
-    return;
+    goto out;
 
 #ifdef DEBUG_WINDOW_PRINTING
   if (event->type == GDK_KEY_PRESS &&
@@ -9704,13 +10090,13 @@ _gdk_windowing_got_event (GdkDisplay *display,
     {
       event_window->native_visibility = event->visibility.state;
       gdk_window_update_visibility_recursively (event_window, event_window);
-      return;
+      goto out;
     }
 
   if (!(is_button_type (event->type) ||
         is_motion_type (event->type)) ||
       event_window->window_type == GDK_WINDOW_ROOT)
-    return;
+    goto out;
 
   is_toplevel = gdk_window_is_toplevel (event_window);
 
@@ -9774,7 +10160,9 @@ _gdk_windowing_got_event (GdkDisplay *display,
         }
     }
 
-  if (pointer_info)
+  if (pointer_info &&
+      (!is_touch_type (event->type) ||
+       _gdk_event_get_pointer_emulated (event)))
     {
       guint old_state, old_button;
 
@@ -9791,6 +10179,9 @@ _gdk_windowing_got_event (GdkDisplay *display,
       if (event->type == GDK_BUTTON_PRESS ||
           event->type == GDK_BUTTON_RELEASE)
         pointer_info->button = event->button.button;
+      else if (event->type == GDK_TOUCH_BEGIN ||
+               event->type == GDK_TOUCH_END)
+        pointer_info->button = 1;
 
       if (device &&
           (pointer_info->state != old_state ||
@@ -9798,22 +10189,37 @@ _gdk_windowing_got_event (GdkDisplay *display,
         _gdk_display_enable_motion_hints (display, device);
     }
 
-  unlink_event = FALSE;
   if (is_motion_type (event->type))
     unlink_event = proxy_pointer_event (display, event, serial);
   else if (is_button_type (event->type))
     unlink_event = proxy_button_event (event, serial);
 
-  if (event->type == GDK_BUTTON_RELEASE && !event->any.send_event)
+  if ((event->type == GDK_BUTTON_RELEASE ||
+       event->type == GDK_TOUCH_END) &&
+      !event->any.send_event)
     {
-      button_release_grab = _gdk_display_has_device_grab (display, device, serial);
-      if (button_release_grab &&
-          button_release_grab->implicit &&
-          (event->button.state & GDK_ANY_BUTTON_MASK & ~(GDK_BUTTON1_MASK << (event->button.button - 1))) == 0)
+      GdkEventSequence *sequence;
+
+      sequence = gdk_event_get_event_sequence (event);
+      if (event->type == GDK_TOUCH_END && sequence)
         {
-          button_release_grab->serial_end = serial;
-          button_release_grab->implicit_ungrab = FALSE;
-          _gdk_display_device_grab_update (display, device, source_device, serial);
+          _gdk_display_end_touch_grab (display, device, sequence);
+        }
+
+      if (event->type == GDK_BUTTON_RELEASE ||
+          _gdk_event_get_pointer_emulated (event))
+        {
+          button_release_grab =
+            _gdk_display_has_device_grab (display, device, serial);
+
+          if (button_release_grab &&
+              button_release_grab->implicit &&
+              (event->button.state & GDK_ANY_BUTTON_MASK & ~(GDK_BUTTON1_MASK << (event->button.button - 1))) == 0)
+            {
+              button_release_grab->serial_end = serial;
+              button_release_grab->implicit_ungrab = FALSE;
+              _gdk_display_device_grab_update (display, device, source_device, serial);
+            }
         }
     }
 
@@ -9824,6 +10230,13 @@ _gdk_windowing_got_event (GdkDisplay *display,
       g_list_free_1 (event_link);
       gdk_event_free (event);
     }
+
+  /* This does two things - first it sees if there are motions at the
+   * end of the queue that can be compressed. Second, if there is just
+   * a single motion that won't be dispatched because it is a compression
+   * candidate it queues up flushing the event queue.
+   */
+  _gdk_event_queue_handle_motion_compression (display);
 }
 
 /**
@@ -9863,7 +10276,7 @@ gdk_window_create_similar_surface (GdkWindow *     window,
 
   g_return_val_if_fail (GDK_IS_WINDOW (window), NULL);
   
-  window_surface = _gdk_window_ref_cairo_surface (window);
+  window_surface = gdk_window_ref_impl_surface (window);
 
   switch (_gdk_rendering_mode)
   {
@@ -10163,7 +10576,7 @@ gdk_window_get_root_origin (GdkWindow *window,
 /**
  * gdk_window_get_frame_extents:
  * @window: a toplevel #GdkWindow
- * @rect: rectangle to fill with bounding box of the window frame
+ * @rect: (out): rectangle to fill with bounding box of the window frame
  *
  * Obtains the bounding box of the window, including window manager
  * titlebar/borders if any. The frame position is given in root window
@@ -10429,6 +10842,67 @@ gdk_window_fullscreen (GdkWindow *window)
   GDK_WINDOW_IMPL_GET_CLASS (window->impl)->fullscreen (window);
 }
 
+/**
+ * gdk_window_set_fullscreen_mode:
+ * @window: a toplevel #GdkWindow
+ * @mode: fullscreen mode
+ *
+ * Specifies whether the @window should span over all monitors (in a multi-head
+ * setup) or only the current monitor when in fullscreen mode.
+ *
+ * The @mode argument is from the #GdkFullscreenMode enumeration.
+ * If #GDK_FULLSCREEN_ON_ALL_MONITORS is specified, the fullscreen @window will
+ * span over all monitors from the #GdkScreen.
+ *
+ * On X11, searches through the list of monitors from the #GdkScreen the ones
+ * which delimit the 4 edges of the entire #GdkScreen and will ask the window
+ * manager to span the @window over these monitors.
+ *
+ * If the XINERAMA extension is not available or not usable, this function
+ * has no effect.
+ *
+ * Not all window managers support this, so you can't rely on the fullscreen
+ * window to span over the multiple monitors when #GDK_FULLSCREEN_ON_ALL_MONITORS
+ * is specified.
+ *
+ * Since: 3.8
+ **/
+void
+gdk_window_set_fullscreen_mode (GdkWindow        *window,
+                                GdkFullscreenMode mode)
+{
+  GdkWindowImplClass *impl_class;
+
+  g_return_if_fail (GDK_IS_WINDOW (window));
+
+  if (window->fullscreen_mode != mode)
+    {
+      window->fullscreen_mode = mode;
+
+      impl_class = GDK_WINDOW_IMPL_GET_CLASS (window->impl);
+      if (impl_class->apply_fullscreen_mode != NULL)
+        impl_class->apply_fullscreen_mode (window);
+    }
+}
+
+/**
+ * gdk_window_get_fullscreen_mode:
+ * @window: a toplevel #GdkWindow
+ *
+ * Obtains the #GdkFullscreenMode of the @window.
+ *
+ * Returns: The #GdkFullscreenMode applied to the window when fullscreen.
+ *
+ * Since: 3.8
+ **/
+GdkFullscreenMode
+gdk_window_get_fullscreen_mode (GdkWindow *window)
+{
+  g_return_val_if_fail (GDK_IS_WINDOW (window), GDK_FULLSCREEN_ON_CURRENT_MONITOR);
+
+  return window->fullscreen_mode;
+}
+
 /**
  * gdk_window_unfullscreen:
  * @window: a toplevel #GdkWindow
@@ -10735,61 +11209,49 @@ gdk_window_begin_move_drag (GdkWindow *window,
  * gdk_window_enable_synchronized_configure:
  * @window: a toplevel #GdkWindow
  *
- * Indicates that the application will cooperate with the window
- * system in synchronizing the window repaint with the window
- * manager during resizing operations. After an application calls
- * this function, it must call gdk_window_configure_finished() every
- * time it has finished all processing associated with a set of
- * Configure events. Toplevel GTK+ windows automatically use this
- * protocol.
- *
- * On X, calling this function makes @window participate in the
- * _NET_WM_SYNC_REQUEST window manager protocol.
+ * Does nothing, present only for compatiblity.
  *
  * Since: 2.6
+ * Deprecated: 3.8: this function is no longer needed
  **/
 void
 gdk_window_enable_synchronized_configure (GdkWindow *window)
 {
-  GDK_WINDOW_IMPL_GET_CLASS (window->impl)->enable_synchronized_configure (window);
 }
 
 /**
  * gdk_window_configure_finished:
  * @window: a toplevel #GdkWindow
- * 
- * Signal to the window system that the application has finished
- * handling Configure events it has received. Window Managers can
- * use this to better synchronize the frame repaint with the
- * application. GTK+ applications will automatically call this
- * function when appropriate.
  *
- * This function can only be called if gdk_window_enable_synchronized_configure()
- * was called previously.
+ * Does nothing, present only for compatiblity.
  *
  * Since: 2.6
+ * Deprecated: 3.8: this function is no longer needed
  **/
 void
 gdk_window_configure_finished (GdkWindow *window)
 {
-  GDK_WINDOW_IMPL_GET_CLASS (window->impl)->configure_finished (window);
 }
 
 /**
  * gdk_window_set_opacity:
- * @window: a top-level #GdkWindow
+ * @window: a top-level or non-native #GdkWindow
  * @opacity: opacity
  *
- * Request the windowing system to make @window partially transparent,
+ * Set @window to render as partially transparent,
  * with opacity 0 being fully transparent and 1 fully opaque. (Values
  * of the opacity parameter are clamped to the [0,1] range.) 
  *
- * On X11, this works only on X screens with a compositing manager 
- * running.
+ * For toplevel windows this depends on support from the windowing system
+ * that may not always be there. For instance, On X11, this works only on
+ * X screens with a compositing manager running.
+ *
+ * For child windows this function only works for non-native windows.
  *
- * For setting up per-pixel alpha, see gdk_screen_get_rgba_visual().
- * For making non-toplevel windows translucent, see 
- * gdk_window_set_composited().
+ * For setting up per-pixel alpha topelevels, see gdk_screen_get_rgba_visual(),
+ * and for non-toplevels, see gdk_window_set_composited().
+ *
+ * Support for non-toplevel windows was added in 3.8.
  *
  * Since: 2.12
  */
@@ -10797,7 +11259,23 @@ void
 gdk_window_set_opacity (GdkWindow *window,
                        gdouble    opacity)
 {
-  GDK_WINDOW_IMPL_GET_CLASS (window->impl)->set_opacity (window, opacity);
+  if (opacity < 0)
+    opacity = 0;
+  else if (opacity > 1)
+    opacity = 1;
+
+  window->alpha = round (opacity * 255);
+
+  if (window->destroyed)
+    return;
+
+  if (gdk_window_has_impl (window))
+    GDK_WINDOW_IMPL_GET_CLASS (window->impl)->set_opacity (window, opacity);
+  else
+    {
+      recompute_visible_regions (window, TRUE, FALSE);
+      gdk_window_invalidate_rect_full (window, NULL, TRUE, CLEAR_BG_ALL);
+    }
 }
 
 /* This function is called when the XWindow is really gone.
@@ -11108,3 +11586,113 @@ gdk_property_delete (GdkWindow *window,
 {
   GDK_WINDOW_IMPL_GET_CLASS (window->impl)->delete_property (window, property);
 }
+
+static void
+gdk_window_flush_events (GdkFrameClock *clock,
+                         void          *data)
+{
+  GdkWindow *window;
+  GdkDisplay *display;
+
+  window = GDK_WINDOW (data);
+
+  display = gdk_window_get_display (window);
+  _gdk_display_flush_events (display);
+  _gdk_display_pause_events (display);
+
+  gdk_frame_clock_request_phase (clock, GDK_FRAME_CLOCK_PHASE_RESUME_EVENTS);
+}
+
+static void
+gdk_window_paint_on_clock (GdkFrameClock *clock,
+                          void          *data)
+{
+  GdkWindow *window;
+
+  window = GDK_WINDOW (data);
+
+  /* Update window and any children on the same clock.
+   */
+  gdk_window_process_updates_with_mode (window, PROCESS_UPDATES_WITH_SAME_CLOCK_CHILDREN);
+}
+
+static void
+gdk_window_resume_events (GdkFrameClock *clock,
+                          void          *data)
+{
+  GdkWindow *window;
+  GdkDisplay *display;
+
+  window = GDK_WINDOW (data);
+
+  display = gdk_window_get_display (window);
+  _gdk_display_unpause_events (display);
+}
+
+static void
+gdk_window_set_frame_clock (GdkWindow     *window,
+                            GdkFrameClock *clock)
+{
+  g_return_if_fail (GDK_IS_WINDOW (window));
+  g_return_if_fail (clock == NULL || GDK_IS_FRAME_CLOCK (clock));
+  g_return_if_fail (clock == NULL || gdk_window_is_toplevel (window));
+
+  if (clock == window->frame_clock)
+    return;
+
+  if (clock)
+    {
+      g_object_ref (clock);
+      g_signal_connect (G_OBJECT (clock),
+                        "flush-events",
+                        G_CALLBACK (gdk_window_flush_events),
+                        window);
+      g_signal_connect (G_OBJECT (clock),
+                        "paint",
+                        G_CALLBACK (gdk_window_paint_on_clock),
+                        window);
+      g_signal_connect (G_OBJECT (clock),
+                        "resume-events",
+                        G_CALLBACK (gdk_window_resume_events),
+                        window);
+    }
+
+  if (window->frame_clock)
+    {
+      g_signal_handlers_disconnect_by_func (G_OBJECT (window->frame_clock),
+                                            G_CALLBACK (gdk_window_flush_events),
+                                            window);
+      g_signal_handlers_disconnect_by_func (G_OBJECT (window->frame_clock),
+                                            G_CALLBACK (gdk_window_paint_on_clock),
+                                            window);
+      g_signal_handlers_disconnect_by_func (G_OBJECT (window->frame_clock),
+                                            G_CALLBACK (gdk_window_resume_events),
+                                            window);
+      g_object_unref (window->frame_clock);
+    }
+
+  window->frame_clock = clock;
+}
+
+/**
+ * gdk_window_get_frame_clock:
+ * @window: window to get frame clock for
+ *
+ * Gets the frame clock for the window. The frame clock for a window
+ * never changes unless the window is reparented to a new toplevel
+ * window.
+ *
+ * Since: 3.8
+ * Return value: (transfer none): the frame clock
+ */
+GdkFrameClock*
+gdk_window_get_frame_clock (GdkWindow *window)
+{
+  GdkWindow *toplevel;
+
+  g_return_val_if_fail (GDK_IS_WINDOW (window), NULL);
+
+  toplevel = gdk_window_get_toplevel (window);
+
+  return toplevel->frame_clock;
+}