]> Pileus Git - ~andy/gtk/blobdiff - gdk/gdkwindow.c
Merge in Gdk-custom.c introspection annotations
[~andy/gtk] / gdk / gdkwindow.c
index f9fb2adccc9c9a03fcf1b30e83ec7af1c3253d85..cd7def89d86bfe1fe561826f35671403aa0ddf90 100644 (file)
@@ -134,6 +134,12 @@ enum {
   PROP_CURSOR
 };
 
+typedef enum {
+  CLEAR_BG_NONE,
+  CLEAR_BG_WINCLEARED, /* Clear backgrounds except those that the window system clears */
+  CLEAR_BG_ALL
+} ClearBg;
+
 struct _GdkWindowPaint
 {
   GdkRegion *region;
@@ -142,6 +148,7 @@ struct _GdkWindowPaint
   gint y_offset;
   cairo_surface_t *surface;
   guint uses_implicit : 1;
+  guint flushed : 1;
   guint32 region_tag;
 };
 
@@ -321,15 +328,24 @@ static void recompute_visible_regions   (GdkWindowObject *private,
                                         gboolean recalculate_siblings,
                                         gboolean recalculate_children);
 static void gdk_window_flush_outstanding_moves (GdkWindow *window);
-static void gdk_window_flush            (GdkWindow *window);
 static void gdk_window_flush_recursive  (GdkWindowObject *window);
 static void do_move_region_bits_on_impl (GdkWindowObject *private,
                                         GdkRegion *region, /* In impl window coords */
                                         int dx, int dy);
 static void gdk_window_invalidate_in_parent (GdkWindowObject *private);
-static void move_native_children (GdkWindowObject *private);
-static void update_cursor (GdkDisplay *display);
+static void move_native_children        (GdkWindowObject *private);
+static void update_cursor               (GdkDisplay *display);
+static void impl_window_add_update_area (GdkWindowObject *impl_window,
+                                        GdkRegion *region);
 static void gdk_window_region_move_free (GdkWindowRegionMove *move);
+static void gdk_window_invalidate_region_full (GdkWindow       *window,
+                                              const GdkRegion *region,
+                                              gboolean         invalidate_children,
+                                              ClearBg          clear_bg);
+static void gdk_window_invalidate_rect_full (GdkWindow          *window,
+                                            const GdkRectangle *rect,
+                                            gboolean            invalidate_children,
+                                            ClearBg             clear_bg);
 
 static guint signals[LAST_SIGNAL] = { 0 };
 
@@ -466,10 +482,11 @@ gdk_window_class_init (GdkWindowObjectClass *klass)
   /* Properties */
   g_object_class_install_property (object_class,
                                    PROP_CURSOR,
-                                   g_param_spec_pointer ("cursor",
-                                                         P_("Cursor"),
-                                                         P_("Cursor"),
-                                                         G_PARAM_READWRITE));
+                                   g_param_spec_boxed ("cursor",
+                                                       P_("Cursor"),
+                                                       P_("Cursor"),
+                                                       GDK_TYPE_CURSOR,
+                                                       G_PARAM_READWRITE));
 
   /**
    * GdkWindow::pick-embedded-child:
@@ -610,7 +627,7 @@ gdk_window_set_property (GObject      *object,
   switch (prop_id)
     {
     case PROP_CURSOR:
-      gdk_window_set_cursor (window, g_value_get_pointer (value));
+      gdk_window_set_cursor (window, g_value_get_boxed (value));
       break;
 
     default:
@@ -630,7 +647,7 @@ gdk_window_get_property (GObject    *object,
   switch (prop_id)
     {
     case PROP_CURSOR:
-      g_value_set_pointer (value, gdk_window_get_cursor (window));
+      g_value_set_boxed (value, gdk_window_get_cursor (window));
       break;
 
     default:
@@ -1160,28 +1177,58 @@ get_native_event_mask (GdkWindowObject *private)
     return private->event_mask;
   else
     {
-      return
-       /* We need thse for all native window so we can emulate
-          events on children: */
+      GdkEventMask mask;
+
+      /* Do whatever the app asks to, since the app
+       * may be asking for weird things for native windows,
+       * but don't use motion hints as that may affect non-native
+       * child windows that don't want it. Also, we need to
+       * set all the app-specified masks since they will be picked
+       * up by any implicit grabs (i.e. if they were not set as
+       * native we would not get the events we need). */
+      mask = private->event_mask & ~GDK_POINTER_MOTION_HINT_MASK;
+
+      /* We need thse for all native windows so we can
+        emulate events on children: */
+      mask |=
        GDK_EXPOSURE_MASK |
        GDK_VISIBILITY_NOTIFY_MASK |
-       GDK_POINTER_MOTION_MASK |
-       GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
-       GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
-       GDK_SCROLL_MASK |
-       /* Then do whatever the app asks to, since the app
-        * may be asking for weird things for native windows,
-        * but filter out things that override the above
-        * requests somehow. */
-       (private->event_mask &
-        ~(GDK_POINTER_MOTION_HINT_MASK |
-          GDK_BUTTON_MOTION_MASK |
-          GDK_BUTTON1_MOTION_MASK |
-          GDK_BUTTON2_MOTION_MASK |
-          GDK_BUTTON3_MOTION_MASK));
+       GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK;
+
+      /* Additionally we select for pointer and button events
+       * for toplevels as we need to get these to emulate
+       * them for non-native subwindows. Even though we don't
+       * select on them for all native windows we will get them
+       * as the events are propagated out to the first window
+       * that select for them.
+       * Not selecting for button press on all windows is an
+       * important thing, because in X only one client can do
+       * so, and we don't want to unexpectedly prevent another
+       * client from doing it.
+       */
+      if (gdk_window_is_toplevel (private))
+       mask |=
+         GDK_POINTER_MOTION_MASK |
+         GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
+         GDK_SCROLL_MASK;
+
+      return mask;
     }
 }
 
+static GdkEventMask
+get_native_grab_event_mask (GdkEventMask grab_mask)
+{
+  /* Similar to the above but for pointer events only */
+  return
+    GDK_POINTER_MOTION_MASK |
+    GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
+    GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
+    GDK_SCROLL_MASK |
+    (grab_mask &
+     ~GDK_POINTER_MOTION_HINT_MASK);
+}
+
 /* Puts the native window in the right order wrt the other native windows
  * in the hierarchy, given the position it has in the client side data.
  * This is useful if some operation changed the stacking order.
@@ -1209,7 +1256,7 @@ sync_native_window_stack_position (GdkWindow *window)
 
 /**
  * gdk_window_new:
- * @parent: a #GdkWindow, or %NULL to create the window as a child of
+ * @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
@@ -1219,7 +1266,7 @@ sync_native_window_stack_position (GdkWindow *window)
  * more details.  Note: to use this on displays other than the default
  * display, @parent must be specified.
  *
- * Return value: the new #GdkWindow
+ * Return value: (transfer none): the new #GdkWindow
  **/
 GdkWindow*
 gdk_window_new (GdkWindow     *parent,
@@ -1684,6 +1731,67 @@ gdk_window_reparent (GdkWindow *window,
     _gdk_synthesize_crossing_events_for_geometry_change (window);
 }
 
+static gboolean
+temporary_disable_extension_events (GdkWindowObject *window)
+{
+  GdkWindowObject *child;
+  GList *l;
+  gboolean res;
+
+  if (window->extension_events != 0)
+    {
+      g_object_set_data (G_OBJECT (window),
+                        "gdk-window-extension-events",
+                        GINT_TO_POINTER (window->extension_events));
+      gdk_input_set_extension_events ((GdkWindow *)window, 0,
+                                     GDK_EXTENSION_EVENTS_NONE);
+    }
+  else
+    res = FALSE;
+
+  for (l = window->children; l != NULL; l = l->next)
+    {
+      child = l->data;
+
+      if (window->impl_window == child->impl_window)
+       res |= temporary_disable_extension_events (window);
+    }
+
+  return res;
+}
+
+static void
+reenable_extension_events (GdkWindowObject *window)
+{
+  GdkWindowObject *child;
+  GList *l;
+  int mask;
+
+  mask = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (window),
+                                             "gdk-window-extension-events"));
+
+  if (mask != 0)
+    {
+      /* We don't have the mode here, so we pass in cursor.
+        This works with the current code since mode is not
+        stored except as part of the mask, and cursor doesn't
+        change the mask. */
+      gdk_input_set_extension_events ((GdkWindow *)window, mask,
+                                     GDK_EXTENSION_EVENTS_CURSOR);
+      g_object_set_data (G_OBJECT (window),
+                        "gdk-window-extension-events",
+                        NULL);
+    }
+
+  for (l = window->children; l != NULL; l = l->next)
+    {
+      child = l->data;
+
+      if (window->impl_window == child->impl_window)
+       reenable_extension_events (window);
+    }
+}
+
 /**
  * gdk_window_ensure_native:
  * @window: a #GdkWindow
@@ -1711,6 +1819,7 @@ gdk_window_ensure_native (GdkWindow *window)
   GdkWindowObject *above;
   GList listhead;
   GdkWindowImplIface *impl_iface;
+  gboolean disabled_extension_events;
 
   g_return_val_if_fail (GDK_IS_WINDOW (window), FALSE);
 
@@ -1731,6 +1840,12 @@ gdk_window_ensure_native (GdkWindow *window)
 
   /* Need to create a native window */
 
+  /* First we disable any extension events on the window or its
+     descendants to handle the native input window moving */
+  disabled_extension_events = FALSE;
+  if (impl_window->input_window)
+    disabled_extension_events = temporary_disable_extension_events (private);
+
   screen = gdk_drawable_get_screen (window);
   visual = gdk_drawable_get_visual (window);
 
@@ -1784,6 +1899,9 @@ gdk_window_ensure_native (GdkWindow *window)
   if (gdk_window_is_viewable (window))
     impl_iface->show (window, FALSE);
 
+  if (disabled_extension_events)
+    reenable_extension_events (private);
+
   return TRUE;
 }
 
@@ -2095,6 +2213,20 @@ gdk_window_get_window_type (GdkWindow *window)
   return GDK_WINDOW_TYPE (window);
 }
 
+/**
+ * gdk_window_is_destroyed:
+ * @window: a #GdkWindow
+ *
+ * Check to see if a window is destroyed..
+ *
+ * Return value: %TRUE if the window is destroyed
+ **/
+gboolean
+gdk_window_is_destroyed (GdkWindow *window)
+{
+  return GDK_WINDOW_DESTROYED (window);
+}
+
 /**
  * gdk_window_get_position:
  * @window: a #GdkWindow
@@ -2479,11 +2611,20 @@ gdk_window_begin_implicit_paint (GdkWindow *window, GdkRectangle *rect)
       private->implicit_paint != NULL)
     return FALSE; /* Don't stack implicit paints */
 
+  /* Never do implicit paints for foreign windows, they don't need
+   * double buffer combination since they have no client side children,
+   * and creating pixmaps for them is risky since they could disappear
+   * at any time
+   */
+  if (private->window_type == GDK_WINDOW_FOREIGN)
+    return FALSE;
+
   paint = g_new (GdkWindowPaint, 1);
   paint->region = gdk_region_new (); /* Empty */
   paint->x_offset = rect->x;
   paint->y_offset = rect->y;
   paint->uses_implicit = FALSE;
+  paint->flushed = FALSE;
   paint->surface = NULL;
   paint->pixmap =
     gdk_pixmap_new (window,
@@ -2512,6 +2653,7 @@ gdk_window_flush_implicit_paint (GdkWindow *window)
     return;
 
   paint = impl_window->implicit_paint;
+  paint->flushed = TRUE;
   region = gdk_region_copy (private->clip_region_with_children);
 
   /* Don't flush active double buffers, as that may show partially done
@@ -2528,6 +2670,9 @@ gdk_window_flush_implicit_paint (GdkWindow *window)
 
   if (!gdk_region_empty (region))
     {
+      /* Remove flushed region from the implicit paint */
+      gdk_region_subtract (paint->region, region);
+
       /* Some regions are valid, push these to window now */
       tmp_gc = _gdk_drawable_get_scratch_gc ((GdkDrawable *)window, FALSE);
       _gdk_gc_set_clip_region_internal (tmp_gc, region, TRUE);
@@ -2535,9 +2680,6 @@ gdk_window_flush_implicit_paint (GdkWindow *window)
                         0, 0, paint->x_offset, paint->y_offset, -1, -1);
       /* Reset clip region of the cached GdkGC */
       gdk_gc_set_clip_region (tmp_gc, NULL);
-
-      /* Remove flushed region from the implicit paint */
-      gdk_region_subtract (paint->region, region);
     }
   else
     gdk_region_destroy (region);
@@ -2569,6 +2711,8 @@ gdk_window_end_implicit_paint (GdkWindow *window)
       /* Reset clip region of the cached GdkGC */
       gdk_gc_set_clip_region (tmp_gc, NULL);
     }
+  else
+    gdk_region_destroy (paint->region);
 
   g_object_unref (paint->pixmap);
   g_free (paint);
@@ -3117,12 +3261,10 @@ append_move_region (GdkWindowObject *impl_window,
 /* Moves bits and update area by dx/dy in impl window.
    Takes ownership of region to avoid copy (because we may change it) */
 static void
-move_region_on_impl (GdkWindowObject *private,
+move_region_on_impl (GdkWindowObject *impl_window,
                     GdkRegion *region, /* In impl window coords */
                     int dx, int dy)
 {
-  GdkWindowObject *impl_window;
-
   if ((dx == 0 && dy == 0) ||
       gdk_region_empty (region))
     {
@@ -3130,12 +3272,13 @@ move_region_on_impl (GdkWindowObject *private,
       return;
     }
 
-  impl_window = gdk_window_get_impl_window (private);
+  g_assert (impl_window == gdk_window_get_impl_window (impl_window));
 
   /* Move any old invalid regions in the copy source area by dx/dy */
   if (impl_window->update_area)
     {
       GdkRegion *update_area;
+
       update_area = gdk_region_copy (region);
 
       /* Convert from target to source */
@@ -3157,6 +3300,22 @@ move_region_on_impl (GdkWindowObject *private,
       gdk_region_destroy (update_area);
     }
 
+  /* 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)
+    {
+      GdkWindowPaint *implicit_paint = impl_window->implicit_paint;
+      GdkRegion *exposing;
+
+      exposing = gdk_region_copy (implicit_paint->region);
+      gdk_region_intersect (exposing, region);
+      gdk_region_subtract (region, exposing);
+
+      impl_window_add_update_area (impl_window, exposing);
+      gdk_region_destroy (exposing);
+    }
+
   if (1) /* Enable flicker free handling of moves. */
     append_move_region (impl_window, region, dx, dy);
   else
@@ -3195,13 +3354,59 @@ gdk_window_flush_outstanding_moves (GdkWindow *window)
   impl_window->outstanding_moves = NULL;
 }
 
-static void
+/**
+ * gdk_window_flush:
+ * @window: a #GdkWindow
+ *
+ * Flush all outstanding cached operations on a window, leaving the
+ * window in a state which reflects all that has been drawn before.
+ *
+ * Gdk uses multiple kinds of caching to get better performance and
+ * nicer drawing. For instance, during exposes all paints to a window
+ * using double buffered rendering are keep on a pixmap until the last
+ * window has been exposed. It also delays window moves/scrolls until
+ * as long as possible until next update to avoid tearing when moving
+ * windows.
+ *
+ * Normally this should be completely invisible to applications, as
+ * we automatically flush the windows when required, but this might
+ * be needed if you for instance mix direct native drawing with
+ * gdk drawing. For Gtk widgets that don't use double buffering this
+ * will be called automatically before sending the expose event.
+ *
+ * Since: 2.18
+ **/
+void
 gdk_window_flush (GdkWindow *window)
 {
   gdk_window_flush_outstanding_moves (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)
+{
+  GdkWindowObject *private;
+  GdkWindowObject *impl_window;
+
+  private = (GdkWindowObject *) window;
+  impl_window = gdk_window_get_impl_window (private);
+
+  /* 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 (GdkWindowObject *window,
                                   GdkWindow *impl)
@@ -3251,12 +3456,12 @@ gdk_window_get_offsets (GdkWindow *window,
 /**
  * gdk_window_get_internal_paint_info:
  * @window: a #GdkWindow
- * @real_drawable: location to store the drawable to which drawing should be
+ * @real_drawable: (out): location to store the drawable to which drawing should be
  *            done.
- * @x_offset: location to store the X offset between coordinates in @window,
+ * @x_offset: (out): location to store the X offset between coordinates in @window,
  *            and the underlying window system primitive coordinates for
  *            *@real_drawable.
- * @y_offset: location to store the Y offset between coordinates in @window,
+ * @y_offset: (out): location to store the Y offset between coordinates in @window,
  *            and the underlying window system primitive coordinates for
  *            *@real_drawable.
  *
@@ -3778,6 +3983,7 @@ gdk_window_draw_drawable (GdkDrawable *drawable,
            clip = private->clip_region;
          gdk_region_intersect (exposure_region, clip);
 
+         _gdk_gc_remove_drawable_clip (gc);
          clip = _gdk_gc_get_clip_region (gc);
          if (clip)
            {
@@ -3803,9 +4009,10 @@ gdk_window_draw_drawable (GdkDrawable *drawable,
          gdk_region_subtract (exposure_region, clip);
          gdk_region_destroy (clip);
 
-         gdk_window_invalidate_region (GDK_WINDOW (private),
-                                       exposure_region,
-                                       _gdk_gc_get_subwindow (gc) == GDK_INCLUDE_INFERIORS);
+         gdk_window_invalidate_region_full (GDK_WINDOW (private),
+                                             exposure_region,
+                                             _gdk_gc_get_subwindow (gc) == GDK_INCLUDE_INFERIORS,
+                                             CLEAR_BG_ALL);
 
          gdk_region_destroy (exposure_region);
        }
@@ -4268,8 +4475,12 @@ gdk_window_clear (GdkWindow *window)
                         width, height);
 }
 
+/* TRUE if the window clears to the same pixels as a native
+   window clear. This means you can use the native window
+   clearing operation, and additionally it means any clearing
+   done by the native window system for you will already be right */
 static gboolean
-clears_on_native (GdkWindowObject *private)
+clears_as_native (GdkWindowObject *private)
 {
   GdkWindowObject *next;
 
@@ -4303,13 +4514,17 @@ gdk_window_clear_region_internal (GdkWindow *window,
 
       impl_iface = GDK_WINDOW_IMPL_GET_IFACE (private->impl);
 
-      if (impl_iface->clear_region && clears_on_native (private))
+      if (impl_iface->clear_region && clears_as_native (private))
        {
          GdkRegion *copy;
          copy = gdk_region_copy (region);
          gdk_region_intersect (copy,
                                private->clip_region_with_children);
 
+
+         /* Drawing directly to the window, flush anything outstanding to
+            guarantee ordering. */
+         gdk_window_flush (window);
          impl_iface->clear_region (window, copy, send_expose);
 
          gdk_region_destroy (copy);
@@ -4331,7 +4546,6 @@ gdk_window_clear_area_internal (GdkWindow *window,
                                gint       height,
                                gboolean   send_expose)
 {
-  GdkWindowObject *private = (GdkWindowObject *)window;
   GdkRectangle rect;
   GdkRegion *region;
 
@@ -4340,12 +4554,10 @@ gdk_window_clear_area_internal (GdkWindow *window,
   if (GDK_WINDOW_DESTROYED (window))
     return;
 
-  /* This is what XClearArea does, and e.g. GtkCList uses it,
-     so we need to duplicate that */
-  if (width == 0)
-    width = private->width - x;
-  if (height == 0)
-    height = private->height - y;
+  /* Terminate early to avoid weird interpretation of
+     zero width/height by XClearArea */
+  if (width == 0 || height == 0)
+    return;
 
   rect.x = x;
   rect.y = y;
@@ -4857,10 +5069,10 @@ gdk_window_schedule_update (GdkWindow *window)
     return;
 
   if (!update_idle)
-    {
-      update_idle = gdk_threads_add_idle_full (GDK_PRIORITY_REDRAW,
-                                    gdk_window_update_idle, NULL, NULL);
-    }
+    update_idle =
+      gdk_threads_add_idle_full (GDK_PRIORITY_REDRAW,
+                                gdk_window_update_idle,
+                                NULL, NULL);
 }
 
 void
@@ -4944,12 +5156,17 @@ _gdk_window_process_updates_recurse (GdkWindow *window,
 
          g_object_unref (window);
        }
-      else if (private->bg_pixmap != GDK_NO_BG)
+      else if (private->bg_pixmap != GDK_NO_BG &&
+              private->window_type != GDK_WINDOW_FOREIGN)
        {
          /* No exposure mask set, so nothing will be drawn, the
           * app relies on the background being what it specified
           * for the window. So, we need to clear this manually.
           *
+          * For foreign windows if expose is not set that generally
+          * means some other client paints them, so don't clear
+          * there.
+          *
           * We use begin/end_paint around the clear so that we can
           * piggyback on the implicit paint */
 
@@ -4987,8 +5204,7 @@ gdk_window_process_updates_internal (GdkWindow *window)
       GdkRegion *update_area = private->update_area;
       private->update_area = NULL;
 
-      if (_gdk_event_func && gdk_window_is_viewable (window)  &&
-         private->window_type != GDK_WINDOW_FOREIGN)
+      if (_gdk_event_func && gdk_window_is_viewable (window))
        {
          GdkRegion *expose_region;
          gboolean end_implicit;
@@ -5109,7 +5325,8 @@ 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 (private->implicit_paint != NULL) /* didn't flush implicit paint */
+             if (private->implicit_paint != NULL &&
+                 !private->implicit_paint->flushed)
                {
                  impl_iface = GDK_WINDOW_IMPL_GET_IFACE (private->impl);
                  save_region = impl_iface->queue_antiexpose (window, update_area);
@@ -5168,11 +5385,19 @@ gdk_window_process_all_updates (void)
   GSList *old_update_windows = update_windows;
   GSList *tmp_list = update_windows;
   static gboolean in_process_all_updates = FALSE;
+  static gboolean got_recursive_update = FALSE;
 
   if (in_process_all_updates)
-    return;
+    {
+      /* 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);
@@ -5208,6 +5433,16 @@ gdk_window_process_all_updates (void)
   _gdk_windowing_after_process_all_updates ();
 
   in_process_all_updates = FALSE;
+
+  /* If we ignored a recursive call, schedule a
+     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);
 }
 
 /**
@@ -5244,7 +5479,11 @@ gdk_window_process_updates (GdkWindow *window,
   if ((impl_window->update_area ||
        impl_window->outstanding_moves) &&
       !impl_window->update_freeze_count &&
-      !gdk_window_is_toplevel_frozen (window))
+      !gdk_window_is_toplevel_frozen (window) &&
+
+      /* Don't recurse into process_updates_internal, we'll
+       * do the update later when idle instead. */
+      impl_window->implicit_paint == NULL)
     {
       gdk_window_process_updates_internal ((GdkWindow *)impl_window);
       gdk_window_remove_update_window ((GdkWindow *)impl_window);
@@ -5272,21 +5511,11 @@ gdk_window_process_updates (GdkWindow *window,
   g_object_unref (window);
 }
 
-/**
- * gdk_window_invalidate_rect:
- * @window: a #GdkWindow
- * @rect: rectangle to invalidate or %NULL to invalidate the whole
- *      window
- * @invalidate_children: whether to also invalidate child windows
- *
- * A convenience wrapper around gdk_window_invalidate_region() which
- * invalidates a rectangular region. See
- * gdk_window_invalidate_region() for details.
- **/
-void
-gdk_window_invalidate_rect (GdkWindow          *window,
-                           const GdkRectangle *rect,
-                           gboolean            invalidate_children)
+static void
+gdk_window_invalidate_rect_full (GdkWindow          *window,
+                                 const GdkRectangle *rect,
+                                 gboolean            invalidate_children,
+                                 ClearBg             clear_bg)
 {
   GdkRectangle window_rect;
   GdkRegion *region;
@@ -5311,10 +5540,29 @@ gdk_window_invalidate_rect (GdkWindow          *window,
     }
 
   region = gdk_region_rectangle (rect);
-  gdk_window_invalidate_region (window, region, invalidate_children);
+  gdk_window_invalidate_region_full (window, region, invalidate_children, clear_bg);
   gdk_region_destroy (region);
 }
 
+/**
+ * gdk_window_invalidate_rect:
+ * @window: a #GdkWindow
+ * @rect: rectangle to invalidate or %NULL to invalidate the whole
+ *      window
+ * @invalidate_children: whether to also invalidate child windows
+ *
+ * A convenience wrapper around gdk_window_invalidate_region() which
+ * invalidates a rectangular region. See
+ * gdk_window_invalidate_region() for details.
+ **/
+void
+gdk_window_invalidate_rect (GdkWindow          *window,
+                           const GdkRectangle *rect,
+                           gboolean            invalidate_children)
+{
+  gdk_window_invalidate_rect_full (window, rect, invalidate_children, CLEAR_BG_NONE);
+}
+
 static void
 draw_ugly_color (GdkWindow       *window,
                 const GdkRegion *region)
@@ -5339,37 +5587,37 @@ draw_ugly_color (GdkWindow       *window,
   g_object_unref (ugly_gc);
 }
 
-/**
- * gdk_window_invalidate_maybe_recurse:
- * @window: a #GdkWindow
- * @region: a #GdkRegion
- * @child_func: function to use to decide if to recurse to a child,
- *              %NULL means never recurse.
- * @user_data: data passed to @child_func
- *
- * Adds @region to the update area for @window. The update area is the
- * region that needs to be redrawn, or "dirty region." The call
- * gdk_window_process_updates() sends one or more expose events to the
- * window, which together cover the entire update area. An
- * application would normally redraw the contents of @window in
- * response to those expose events.
- *
- * GDK will call gdk_window_process_all_updates() on your behalf
- * whenever your program returns to the main loop and becomes idle, so
- * normally there's no need to do that manually, you just need to
- * invalidate regions that you know should be redrawn.
- *
- * The @child_func parameter controls whether the region of
- * each child window that intersects @region will also be invalidated.
- * Only children for which @child_func returns TRUE will have the area
- * invalidated.
- **/
-void
-gdk_window_invalidate_maybe_recurse (GdkWindow       *window,
-                                    const GdkRegion *region,
-                                    gboolean       (*child_func) (GdkWindow *,
-                                                                  gpointer),
-                                    gpointer   user_data)
+static void
+impl_window_add_update_area (GdkWindowObject *impl_window,
+                            GdkRegion *region)
+{
+  if (impl_window->update_area)
+    gdk_region_union (impl_window->update_area, region);
+  else
+    {
+      gdk_window_add_update_window ((GdkWindow *)impl_window);
+      impl_window->update_area = gdk_region_copy (region);
+      gdk_window_schedule_update ((GdkWindow *)impl_window);
+    }
+}
+
+/* clear_bg controls if the region will be cleared to
+ * the background color/pixmap if the exposure mask is not
+ * set for the window, whereas this might not otherwise be
+ * done (unless necessary to emulate background settings).
+ * Set this to CLEAR_BG_WINCLEARED or CLEAR_BG_ALL if you
+ * need to clear the background, such as when exposing the area beneath a
+ * hidden or moved window, but not when an app requests repaint or when the
+ * windowing system exposes a newly visible area (because then the windowing
+ * system has already cleared the area).
+ */
+static void
+gdk_window_invalidate_maybe_recurse_full (GdkWindow       *window,
+                                         const GdkRegion *region,
+                                         ClearBg          clear_bg,
+                                         gboolean       (*child_func) (GdkWindow *,
+                                                                       gpointer),
+                                         gpointer   user_data)
 {
   GdkWindowObject *private = (GdkWindowObject *)window;
   GdkWindowObject *impl_window;
@@ -5420,8 +5668,8 @@ gdk_window_invalidate_maybe_recurse (GdkWindow       *window,
              gdk_region_offset (child_region, - child_rect.x, - child_rect.y);
              gdk_region_intersect (child_region, tmp);
 
-             gdk_window_invalidate_maybe_recurse ((GdkWindow *)child,
-                                                  child_region, child_func, user_data);
+             gdk_window_invalidate_maybe_recurse_full ((GdkWindow *)child,
+                                                       child_region, clear_bg, child_func, user_data);
 
              gdk_region_destroy (tmp);
            }
@@ -5445,22 +5693,58 @@ gdk_window_invalidate_maybe_recurse (GdkWindow       *window,
 
       /* Convert to impl coords */
       gdk_region_offset (visible_region, private->abs_x, private->abs_y);
-      if (impl_window->update_area)
-       {
-         gdk_region_union (impl_window->update_area, visible_region);
-       }
-      else
-       {
-         gdk_window_add_update_window ((GdkWindow *)impl_window);
-         impl_window->update_area = gdk_region_copy (visible_region);
 
-         gdk_window_schedule_update ((GdkWindow *)impl_window);
-       }
+      /* Only invalidate area if app requested expose events or if
+        we need to clear the area (by request or to emulate background
+        clearing for non-native windows or native windows with no support
+        for window backgrounds */
+      if (private->event_mask & GDK_EXPOSURE_MASK ||
+         clear_bg == CLEAR_BG_ALL ||
+         (clear_bg == CLEAR_BG_WINCLEARED &&
+          (!clears_as_native (private) ||
+           !GDK_WINDOW_IMPL_GET_IFACE (private->impl)->supports_native_bg)))
+       impl_window_add_update_area (impl_window, visible_region);
     }
 
   gdk_region_destroy (visible_region);
 }
 
+/**
+ * gdk_window_invalidate_maybe_recurse:
+ * @window: a #GdkWindow
+ * @region: a #GdkRegion
+ * @child_func: function to use to decide if to recurse to a child,
+ *              %NULL means never recurse.
+ * @user_data: data passed to @child_func
+ *
+ * Adds @region to the update area for @window. The update area is the
+ * region that needs to be redrawn, or "dirty region." The call
+ * gdk_window_process_updates() sends one or more expose events to the
+ * window, which together cover the entire update area. An
+ * application would normally redraw the contents of @window in
+ * response to those expose events.
+ *
+ * GDK will call gdk_window_process_all_updates() on your behalf
+ * whenever your program returns to the main loop and becomes idle, so
+ * normally there's no need to do that manually, you just need to
+ * invalidate regions that you know should be redrawn.
+ *
+ * The @child_func parameter controls whether the region of
+ * each child window that intersects @region will also be invalidated.
+ * Only children for which @child_func returns TRUE will have the area
+ * invalidated.
+ **/
+void
+gdk_window_invalidate_maybe_recurse (GdkWindow       *window,
+                                    const GdkRegion *region,
+                                    gboolean       (*child_func) (GdkWindow *,
+                                                                  gpointer),
+                                    gpointer   user_data)
+{
+  gdk_window_invalidate_maybe_recurse_full (window, region, CLEAR_BG_NONE,
+                                           child_func, user_data);
+}
+
 static gboolean
 true_predicate (GdkWindow *window,
                gpointer   user_data)
@@ -5468,6 +5752,18 @@ true_predicate (GdkWindow *window,
   return TRUE;
 }
 
+static void
+gdk_window_invalidate_region_full (GdkWindow       *window,
+                                   const GdkRegion *region,
+                                   gboolean         invalidate_children,
+                                   ClearBg          clear_bg)
+{
+  gdk_window_invalidate_maybe_recurse_full (window, region, clear_bg,
+                                           invalidate_children ?
+                                           true_predicate : (gboolean (*) (GdkWindow *, gpointer))NULL,
+                                      NULL);
+}
+
 /**
  * gdk_window_invalidate_region:
  * @window: a #GdkWindow
@@ -5558,9 +5854,9 @@ _gdk_window_invalidate_for_expose (GdkWindow       *window,
       gdk_region_destroy (move_region);
     }
 
-  gdk_window_invalidate_maybe_recurse (window, region,
-                                      (gboolean (*) (GdkWindow *, gpointer))gdk_window_has_no_impl,
-                                      NULL);
+  gdk_window_invalidate_maybe_recurse_full (window, region, CLEAR_BG_WINCLEARED,
+                                           (gboolean (*) (GdkWindow *, gpointer))gdk_window_has_no_impl,
+                                           NULL);
 }
 
 
@@ -5903,18 +6199,18 @@ gdk_window_constrain_size (GdkGeometry *geometry,
 /**
  * gdk_window_get_pointer:
  * @window: a #GdkWindow
- * @x: return location for X coordinate of pointer or %NULL to not
+ * @x: (out) (allow-none): return location for X coordinate of pointer or %NULL to not
  *      return the X coordinate
- * @y: return location for Y coordinate of pointer or %NULL to not
+ * @y: (out) (allow-none):  return location for Y coordinate of pointer or %NULL to not
  *      return the Y coordinate
- * @mask: return location for modifier mask or %NULL to not return the
+ * @mask: (out) (allow-none): return location for modifier mask or %NULL to not return the
  *      modifier mask
  *
  * Obtains the current pointer position and modifier state.
  * The position is given in coordinates relative to the upper left
  * corner of @window.
  *
- * Return value: the window containing the pointer (as with
+ * Return value: (transfer none): the window containing the pointer (as with
  * gdk_window_at_pointer()), or %NULL if the window containing the
  * pointer isn't known to GDK
  **/
@@ -5963,8 +6259,8 @@ gdk_window_get_pointer (GdkWindow   *window,
 
 /**
  * gdk_window_at_pointer:
- * @win_x: return location for origin of the window under the pointer
- * @win_y: return location for origin of the window under the pointer
+ * @win_x: (out) (allow-none): return location for origin of the window under the pointer
+ * @win_y: (out) (allow-none): return location for origin of the window under the pointer
  *
  * Obtains the window underneath the mouse pointer, returning the
  * location of that window in @win_x, @win_y. Returns %NULL if the
@@ -5975,7 +6271,7 @@ gdk_window_get_pointer (GdkWindow   *window,
  * NOTE: For multihead-aware widgets or applications use
  * gdk_display_get_window_at_pointer() instead.
  *
- * Return value: window under the mouse pointer
+ * Return value: (transfer none): window under the mouse pointer
  **/
 GdkWindow*
 gdk_window_at_pointer (gint *win_x,
@@ -6256,7 +6552,7 @@ gdk_window_show_internal (GdkWindow *window, gboolean raise)
       if (gdk_window_is_viewable (window))
        {
          _gdk_synthesize_crossing_events_for_geometry_change (window);
-         gdk_window_invalidate_rect (window, NULL, TRUE);
+         gdk_window_invalidate_rect_full (window, NULL, TRUE, CLEAR_BG_ALL);
        }
     }
 }
@@ -6303,6 +6599,8 @@ gdk_window_raise (GdkWindow *window)
   if (private->destroyed)
     return;
 
+  gdk_window_flush_if_exposing (window);
+
   old_region = NULL;
   if (gdk_window_is_viewable (window) &&
       !private->input_only)
@@ -6318,7 +6616,7 @@ gdk_window_raise (GdkWindow *window)
       new_region = gdk_region_copy (private->clip_region);
 
       gdk_region_subtract (new_region, old_region);
-      gdk_window_invalidate_region (window, new_region, TRUE);
+      gdk_window_invalidate_region_full (window, new_region, TRUE, CLEAR_BG_ALL);
 
       gdk_region_destroy (old_region);
       gdk_region_destroy (new_region);
@@ -6409,7 +6707,7 @@ gdk_window_invalidate_in_parent (GdkWindowObject *private)
   child.height = private->height;
   gdk_rectangle_intersect (&r, &child, &r);
 
-  gdk_window_invalidate_rect (GDK_WINDOW (private->parent), &r, TRUE);
+  gdk_window_invalidate_rect_full (GDK_WINDOW (private->parent), &r, TRUE, CLEAR_BG_ALL);
 }
 
 
@@ -6439,6 +6737,8 @@ gdk_window_lower (GdkWindow *window)
   if (private->destroyed)
     return;
 
+  gdk_window_flush_if_exposing (window);
+
   /* Keep children in (reverse) stacking order */
   gdk_window_lower_internal (window);
 
@@ -6448,6 +6748,125 @@ gdk_window_lower (GdkWindow *window)
   gdk_window_invalidate_in_parent (private);
 }
 
+/**
+ * gdk_window_restack:
+ * @window: a #GdkWindow
+ * @sibling: a #GdkWindow that is a sibling of @window, or %NULL
+ * @above: a boolean
+ *
+ * Changes the position of  @window in the Z-order (stacking order), so that
+ * it is above @sibling (if @above is %TRUE) or below @sibling (if @above is
+ * %FALSE).
+ *
+ * If @sibling is %NULL, then this either raises (if @above is %TRUE) or
+ * lowers the window.
+ *
+ * If @window is a toplevel, the window manager may choose to deny the
+ * request to move the window in the Z-order, gdk_window_restack() only
+ * requests the restack, does not guarantee it.
+ *
+ * Since: 2.18
+ */
+void
+gdk_window_restack (GdkWindow     *window,
+                   GdkWindow     *sibling,
+                   gboolean       above)
+{
+  GdkWindowObject *private;
+  GdkWindowImplIface *impl_iface;
+  GdkWindowObject *parent;
+  GdkWindowObject *above_native;
+  GList *sibling_link;
+  GList *native_children;
+  GList *l, listhead;
+
+  g_return_if_fail (GDK_IS_WINDOW (window));
+  g_return_if_fail (sibling == NULL || GDK_IS_WINDOW (sibling));
+
+  private = (GdkWindowObject *) window;
+  if (private->destroyed)
+    return;
+
+  if (sibling == NULL)
+    {
+      if (above)
+       gdk_window_raise (window);
+      else
+       gdk_window_lower (window);
+      return;
+    }
+
+  gdk_window_flush_if_exposing (window);
+
+  if (gdk_window_is_toplevel (private))
+    {
+      g_return_if_fail (gdk_window_is_toplevel (GDK_WINDOW_OBJECT (sibling)));
+      impl_iface = GDK_WINDOW_IMPL_GET_IFACE (private->impl);
+      impl_iface->restack_toplevel (window, sibling, above);
+      return;
+    }
+
+  parent = private->parent;
+  if (parent)
+    {
+      sibling_link = g_list_find (parent->children, sibling);
+      g_return_if_fail (sibling_link != NULL);
+      if (sibling_link == NULL)
+       return;
+
+      parent->children = g_list_remove (parent->children, window);
+      if (above)
+       parent->children = g_list_insert_before (parent->children,
+                                                sibling_link,
+                                                window);
+      else
+       parent->children = g_list_insert_before (parent->children,
+                                                sibling_link->next,
+                                                window);
+
+      impl_iface = GDK_WINDOW_IMPL_GET_IFACE (private->impl);
+      if (gdk_window_has_impl (private))
+       {
+         above_native = find_native_sibling_above (parent, private);
+         if (above_native)
+           {
+             listhead.data = window;
+             listhead.next = NULL;
+             listhead.prev = NULL;
+             impl_iface->restack_under ((GdkWindow *)above_native, &listhead);
+           }
+         else
+           impl_iface->raise (window);
+       }
+      else
+       {
+         native_children = NULL;
+         get_all_native_children (private, &native_children);
+         if (native_children != NULL)
+           {
+             above_native = find_native_sibling_above (parent, private);
+             if (above_native)
+               impl_iface->restack_under ((GdkWindow *)above_native,
+                                          native_children);
+             else
+               {
+                 /* Right order, since native_children is bottom-topmost first */
+                 for (l = native_children; l != NULL; l = l->next)
+                   impl_iface->raise (l->data);
+               }
+
+             g_list_free (native_children);
+           }
+       }
+    }
+
+  recompute_visible_regions (private, TRUE, FALSE);
+
+  _gdk_synthesize_crossing_events_for_geometry_change (window);
+  gdk_window_invalidate_in_parent (private);
+}
+
+
 /**
  * gdk_window_show:
  * @window: a #GdkWindow
@@ -6555,7 +6974,8 @@ gdk_window_hide (GdkWindow *window)
     }
 
   /* Invalidate the rect */
-  gdk_window_invalidate_in_parent (private);
+  if (was_mapped)
+    gdk_window_invalidate_in_parent (private);
 }
 
 /**
@@ -6719,7 +7139,7 @@ gdk_window_move_resize_toplevel (GdkWindow *window,
        * roundtrip
        */
       gdk_region_subtract (new_region, old_region);
-      gdk_window_invalidate_region (window, new_region, TRUE);
+      gdk_window_invalidate_region_full (window, new_region, TRUE, CLEAR_BG_WINCLEARED);
 
       gdk_region_destroy (old_region);
       gdk_region_destroy (new_region);
@@ -6839,6 +7259,16 @@ gdk_window_move_resize_internal (GdkWindow *window,
       return;
     }
 
+  /* Bail early if no change */
+  if (private->width == width &&
+      private->height == height &&
+      (!with_move ||
+       (private->x == x &&
+       private->y == y)))
+    return;
+
+  gdk_window_flush_if_exposing (window);
+
   /* Handle child windows */
 
   expose = FALSE;
@@ -6958,6 +7388,7 @@ gdk_window_move_resize_internal (GdkWindow *window,
             native windows, as we can't read that data */
          gdk_region_offset (new_native_child_region, dx, dy);
          gdk_region_subtract (copy_area, new_native_child_region);
+         gdk_region_offset (new_native_child_region, -dx, -dy);
        }
 
       gdk_region_subtract (new_region, copy_area);
@@ -6975,7 +7406,14 @@ gdk_window_move_resize_internal (GdkWindow *window,
        * We also invalidate any children in that area, which could include
        * this window if it still overlaps that area.
        */
-      gdk_window_invalidate_region (GDK_WINDOW (private->parent), new_region, TRUE);
+      if (old_native_child_region)
+       {
+         /* No need to expose the region that the native window move copies */
+         gdk_region_offset (old_native_child_region, dx, dy);
+         gdk_region_intersect (old_native_child_region, new_native_child_region);
+         gdk_region_subtract (new_region, old_native_child_region);
+       }
+      gdk_window_invalidate_region_full (GDK_WINDOW (private->parent), new_region, TRUE, CLEAR_BG_ALL);
 
       gdk_region_destroy (old_region);
       gdk_region_destroy (new_region);
@@ -7100,6 +7538,8 @@ gdk_window_scroll (GdkWindow *window,
   if (private->destroyed)
     return;
 
+  gdk_window_flush_if_exposing (window);
+
   old_native_child_region = collect_native_child_region (private, FALSE);
   if (old_native_child_region)
     {
@@ -7166,7 +7606,14 @@ gdk_window_scroll (GdkWindow *window,
   move_region_on_impl (impl_window, copy_area, dx, dy); /* takes ownership of copy_area */
 
   /* Invalidate not copied regions */
-  gdk_window_invalidate_region (window, noncopy_area, TRUE);
+  if (old_native_child_region)
+    {
+      /* No need to expose the region that the native window move copies */
+      gdk_region_offset (old_native_child_region, dx, dy);
+      gdk_region_intersect (old_native_child_region, new_native_child_region);
+      gdk_region_subtract (noncopy_area, old_native_child_region);
+    }
+  gdk_window_invalidate_region_full (window, noncopy_area, TRUE, CLEAR_BG_ALL);
 
   gdk_region_destroy (noncopy_area);
 
@@ -7235,7 +7682,7 @@ gdk_window_move_region (GdkWindow       *window,
   gdk_region_offset (copy_area, private->abs_x, private->abs_y);
   move_region_on_impl (impl_window, copy_area, dx, dy); /* Takes ownership of copy_area */
 
-  gdk_window_invalidate_region (window, nocopy_area, FALSE);
+  gdk_window_invalidate_region_full (window, nocopy_area, FALSE, CLEAR_BG_ALL);
   gdk_region_destroy (nocopy_area);
 }
 
@@ -7768,7 +8215,7 @@ gdk_window_shape_combine_region (GdkWindow       *window,
       diff = gdk_region_copy (new_region);
       gdk_region_subtract (diff, old_region);
 
-      gdk_window_invalidate_region (window, diff, TRUE);
+      gdk_window_invalidate_region_full (window, diff, TRUE, CLEAR_BG_ALL);
 
       gdk_region_destroy (diff);
 
@@ -7781,7 +8228,7 @@ gdk_window_shape_combine_region (GdkWindow       *window,
          /* Adjust region to parent window coords */
          gdk_region_offset (diff, private->x, private->y);
 
-         gdk_window_invalidate_region (GDK_WINDOW (private->parent), diff, TRUE);
+         gdk_window_invalidate_region_full (GDK_WINDOW (private->parent), diff, TRUE, CLEAR_BG_ALL);
 
          gdk_region_destroy (diff);
        }
@@ -8329,7 +8776,6 @@ _gdk_window_calculate_full_clip_region (GdkWindow *window,
     {
       GList *cur;
       GdkRectangle real_clip_rect;
-      gboolean is_offscreen;
 
       if (parentwin != private)
        {
@@ -8337,8 +8783,6 @@ _gdk_window_calculate_full_clip_region (GdkWindow *window,
          y_offset += GDK_WINDOW_OBJECT (lastwin)->y;
        }
 
-      is_offscreen = gdk_window_is_offscreen (parentwin);
-
       /* children is ordered in reverse stack order */
       for (cur = parentwin->children;
           cur && cur->data != lastwin;
@@ -8463,28 +8907,36 @@ _gdk_window_event_parent_of (GdkWindow *parent,
 static void
 update_cursor (GdkDisplay *display)
 {
-  GdkWindowObject *pointer_window, *cursor_window, *parent, *toplevel;
+  GdkWindowObject *cursor_window, *parent, *toplevel;
+  GdkWindow *pointer_window;
   GdkWindowImplIface *impl_iface;
   GdkPointerGrabInfo *grab;
 
-  pointer_window = (GdkWindowObject *)display->pointer_info.window_under_pointer;
-
-  cursor_window = pointer_window;
-  while (cursor_window->cursor == NULL &&
-        (parent = get_event_parent (cursor_window)) != NULL &&
-        parent->window_type != GDK_WINDOW_ROOT)
-    cursor_window = parent;
+  pointer_window = display->pointer_info.window_under_pointer;
 
   /* We ignore the serials here and just pick the last grab
      we've sent, as that would shortly be used anyway. */
   grab = _gdk_display_get_last_pointer_grab (display);
-  if (grab != NULL &&
-      !_gdk_window_event_parent_of (grab->window, (GdkWindow *)cursor_window))
+  if (/* have grab */
+      grab != NULL &&
+      /* the pointer is not in a descendant of the grab window */
+      !_gdk_window_event_parent_of (grab->window, pointer_window))
+    /* use the cursor from the grab window */
     cursor_window = (GdkWindowObject *)grab->window;
+  else
+    /* otherwise use the cursor from the pointer window */
+    cursor_window = (GdkWindowObject *)pointer_window;
+
+  /* Find the first window with the cursor actually set, as
+     the cursor is inherited from the parent */
+  while (cursor_window->cursor == NULL &&
+        (parent = get_event_parent (cursor_window)) != NULL &&
+        parent->window_type != GDK_WINDOW_ROOT)
+    cursor_window = parent;
 
   /* Set all cursors on toplevel, otherwise its tricky to keep track of
    * which native window has what cursor set. */
-  toplevel = (GdkWindowObject *)get_event_toplevel ((GdkWindow *)pointer_window);
+  toplevel = (GdkWindowObject *)get_event_toplevel (pointer_window);
   impl_iface = GDK_WINDOW_IMPL_GET_IFACE (toplevel->impl);
   impl_iface->set_cursor ((GdkWindow *)toplevel, cursor_window->cursor);
 }
@@ -8988,21 +9440,27 @@ send_crossing_event (GdkDisplay                 *display,
                     gulong                      serial)
 {
   GdkEvent *event;
-  guint32 event_mask;
+  guint32 window_event_mask, type_event_mask;
   GdkPointerGrabInfo *grab;
   GdkWindowImplIface *impl_iface;
 
   grab = _gdk_display_has_pointer_grab (display, serial);
 
   if (grab != NULL &&
-      !grab->owner_events &&
-      (GdkWindow *)window != grab->window)
-    return;
+      !grab->owner_events)
+    {
+      /* !owner_event => only report events wrt grab window, ignore rest */
+      if ((GdkWindow *)window != grab->window)
+       return;
+      window_event_mask = grab->event_mask;
+    }
+  else
+    window_event_mask = window->event_mask;
 
   if (type == GDK_LEAVE_NOTIFY)
-    event_mask = GDK_LEAVE_NOTIFY_MASK;
+    type_event_mask = GDK_LEAVE_NOTIFY_MASK;
   else
-    event_mask = GDK_ENTER_NOTIFY_MASK;
+    type_event_mask = GDK_ENTER_NOTIFY_MASK;
 
   if (window->extension_events != 0)
     {
@@ -9011,7 +9469,7 @@ send_crossing_event (GdkDisplay                 *display,
                                         type == GDK_ENTER_NOTIFY);
     }
 
-  if (window->event_mask & event_mask)
+  if (window_event_mask & type_event_mask)
     {
       event = _gdk_make_event ((GdkWindow *)window, type, event_in_queue, TRUE);
       event->crossing.time = time_;
@@ -9214,15 +9672,11 @@ void
 _gdk_display_set_window_under_pointer (GdkDisplay *display,
                                       GdkWindow *window)
 {
-  GdkWindowObject *private;
-
   /* We don't track this if all native, and it can cause issues
      with the update_cursor call below */
   if (_gdk_native_windows)
     return;
 
-  private = (GdkWindowObject *)window;
-
   if (display->pointer_info.window_under_pointer)
     g_object_unref (display->pointer_info.window_under_pointer);
   display->pointer_info.window_under_pointer = window;
@@ -9312,7 +9766,7 @@ gdk_pointer_grab (GdkWindow *       window,
   res = _gdk_windowing_pointer_grab (window,
                                     native,
                                     owner_events,
-                                    event_mask,
+                                    get_native_grab_event_mask (event_mask),
                                     confine_to,
                                     cursor,
                                     time);
@@ -9697,6 +10151,7 @@ proxy_button_event (GdkEvent *source_event,
                                                       &toplevel_x, &toplevel_y);
 
   if (type == GDK_BUTTON_PRESS &&
+      !source_event->any.send_event &&
       _gdk_display_has_pointer_grab (display, serial) == NULL)
     {
       pointer_window =
@@ -9906,6 +10361,7 @@ _gdk_windowing_got_event (GdkDisplay *display,
   if (_gdk_native_windows)
     {
       if (event->type == GDK_BUTTON_PRESS &&
+         !event->any.send_event &&
          _gdk_display_has_pointer_grab (display, serial) == NULL)
        {
          _gdk_display_add_pointer_grab  (display,
@@ -9919,7 +10375,8 @@ _gdk_windowing_got_event (GdkDisplay *display,
          _gdk_display_pointer_grab_update (display,
                                            serial);
        }
-      if (event->type == GDK_BUTTON_RELEASE)
+      if (event->type == GDK_BUTTON_RELEASE &&
+         !event->any.send_event)
        {
          button_release_grab =
            _gdk_display_has_pointer_grab (display, serial);
@@ -9928,7 +10385,7 @@ _gdk_windowing_got_event (GdkDisplay *display,
              (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 = TRUE;
+             button_release_grab->implicit_ungrab = FALSE;
              _gdk_display_pointer_grab_update (display, serial);
            }
        }
@@ -10042,7 +10499,8 @@ _gdk_windowing_got_event (GdkDisplay *display,
     unlink_event = proxy_button_event (event,
                                       serial);
 
-  if (event->type == GDK_BUTTON_RELEASE)
+  if (event->type == GDK_BUTTON_RELEASE &&
+      !event->any.send_event)
     {
       button_release_grab =
        _gdk_display_has_pointer_grab (display, serial);
@@ -10051,7 +10509,7 @@ _gdk_windowing_got_event (GdkDisplay *display,
          (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 = TRUE;
+         button_release_grab->implicit_ungrab = FALSE;
          _gdk_display_pointer_grab_update (display, serial);
        }
     }