]> Pileus Git - ~andy/gtk/blobdiff - gdk/x11/gdkwindow-x11.c
GdkWindowX11: Only start a frame when we emit damage
[~andy/gtk] / gdk / x11 / gdkwindow-x11.c
index 1ba3e24238e61f5ff25d6f35664a3c1a5761fde5..f2c5735e79d5c8d26e6c7d4245c81547bbf2cf37 100644 (file)
@@ -13,9 +13,7 @@
  * Lesser General Public License for more details.
  *
  * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
  */
 
 /*
@@ -37,6 +35,7 @@
 #include "gdkasync.h"
 #include "gdkeventsource.h"
 #include "gdkdisplay-x11.h"
+#include "gdkframeclockidle.h"
 #include "gdkprivate-x11.h"
 
 #include <stdlib.h>
@@ -99,6 +98,7 @@ const int _gdk_x11_event_mask_table[21] =
 const gint _gdk_x11_event_mask_table_size = G_N_ELEMENTS (_gdk_x11_event_mask_table);
 
 /* Forward declarations */
+static void     gdk_x11_window_apply_fullscreen_mode (GdkWindow  *window);
 static void     gdk_window_set_static_win_gravity (GdkWindow  *window,
                                                   gboolean    on);
 static gboolean gdk_window_icon_name_set          (GdkWindow  *window);
@@ -172,7 +172,10 @@ _gdk_x11_window_get_toplevel (GdkWindow *window)
   impl = GDK_WINDOW_IMPL_X11 (window->impl);
 
   if (!impl->toplevel)
-    impl->toplevel = g_new0 (GdkToplevelX11, 1);
+    {
+      impl->toplevel = g_new0 (GdkToplevelX11, 1);
+      impl->toplevel->have_focused = TRUE;
+    }
 
   return impl->toplevel;
 }
@@ -197,6 +200,135 @@ _gdk_x11_window_update_size (GdkWindowImplX11 *impl)
     }
 }
 
+static void
+set_sync_counter(Display     *display,
+                XSyncCounter counter,
+                 gint64       value)
+{
+    XSyncValue sync_value;
+
+    XSyncIntsToValue(&sync_value,
+                     value & G_GINT64_CONSTANT(0xFFFFFFFF),
+                     value >> 32);
+    XSyncSetCounter(display, counter, sync_value);
+}
+
+static void
+window_pre_damage (GdkWindow *window)
+{
+  GdkWindow *toplevel_window = gdk_window_get_toplevel (window);
+  GdkWindowImplX11 *impl;
+
+  if (!toplevel_window || !WINDOW_IS_TOPLEVEL (toplevel_window))
+    return;
+
+  impl = GDK_WINDOW_IMPL_X11 (toplevel_window->impl);
+
+  if (impl->toplevel->in_frame &&
+      impl->toplevel->current_counter_value % 2 == 0)
+    {
+      impl->toplevel->current_counter_value += 1;
+      set_sync_counter(GDK_WINDOW_XDISPLAY (impl->wrapper),
+                      impl->toplevel->extended_update_counter,
+                      impl->toplevel->current_counter_value);
+    }
+}
+
+static void
+on_surface_changed (void *data)
+{
+  GdkWindow *window = data;
+
+  window_pre_damage (window);
+}
+
+/* We want to know when cairo drawing causes damage to the window,
+ * so we engage in the _NET_WM_FRAME_DRAWN protocol with the
+ * window only when there actually is drawing. To do that we use
+ * a technique (hack) suggested by Uli Schlachter - if we set
+ * a dummy "mime data" on the cairo surface (this facility is
+ * used to attach JPEG data to an imager), then cairo wil flush
+ * and remove the mime data before making any changes to the window.
+ */
+
+static void
+hook_surface_changed (GdkWindow *window)
+{
+  GdkWindowImplX11 *impl = GDK_WINDOW_IMPL_X11 (window->impl);
+
+  if (impl->cairo_surface)
+    cairo_surface_set_mime_data (impl->cairo_surface,
+                                 "x-gdk/change-notify",
+                                 (unsigned char *)"X",
+                                 1,
+                                 on_surface_changed,
+                                 window);
+}
+
+static void
+unhook_surface_changed (GdkWindow *window)
+{
+  GdkWindowImplX11 *impl = GDK_WINDOW_IMPL_X11 (window->impl);
+
+  if (impl->cairo_surface)
+    cairo_surface_set_mime_data (impl->cairo_surface,
+                                 "x-gdk/change-notify",
+                                 NULL, 0,
+                                 NULL, NULL);
+}
+
+static void
+gdk_x11_window_begin_frame (GdkWindow *window)
+{
+  GdkWindowImplX11 *impl;
+
+  g_return_if_fail (GDK_IS_WINDOW (window));
+
+  impl = GDK_WINDOW_IMPL_X11 (window->impl);
+
+  if (!WINDOW_IS_TOPLEVEL (window) ||
+      impl->toplevel->extended_update_counter == None)
+    return;
+
+  impl->toplevel->in_frame = TRUE;
+
+  hook_surface_changed (window);
+}
+
+static void
+gdk_x11_window_end_frame (GdkWindow *window)
+{
+  GdkWindowImplX11 *impl;
+
+  g_return_if_fail (GDK_IS_WINDOW (window));
+
+  impl = GDK_WINDOW_IMPL_X11 (window->impl);
+
+  if (!WINDOW_IS_TOPLEVEL (window) ||
+      impl->toplevel->extended_update_counter == None ||
+      !impl->toplevel->in_frame)
+    return;
+
+  impl->toplevel->in_frame = FALSE;
+
+  if (impl->toplevel->current_counter_value % 2 == 1)
+    {
+      impl->toplevel->current_counter_value += 1;
+      set_sync_counter(GDK_WINDOW_XDISPLAY (impl->wrapper),
+                      impl->toplevel->extended_update_counter,
+                      impl->toplevel->current_counter_value);
+
+      if (gdk_x11_screen_supports_net_wm_hint (gdk_window_get_screen (window),
+                                              gdk_atom_intern_static_string ("_NET_WM_FRAME_DRAWN")))
+        {
+          impl->toplevel->frame_pending = TRUE;
+          gdk_frame_clock_freeze (gdk_window_get_frame_clock (window));
+        }
+    }
+
+  unhook_surface_changed (window);
+}
+
 /*****************************************************
  * X11 specific implementations of generic functions *
  *****************************************************/
@@ -240,6 +372,9 @@ gdk_x11_ref_cairo_surface (GdkWindow *window)
       if (impl->cairo_surface)
        cairo_surface_set_user_data (impl->cairo_surface, &gdk_x11_cairo_key,
                                     impl, gdk_x11_cairo_surface_destroy);
+
+      if (WINDOW_IS_TOPLEVEL (window) && impl->toplevel->in_frame)
+        hook_surface_changed (window);
     }
   else
     cairo_surface_reference (impl->cairo_surface);
@@ -259,6 +394,9 @@ gdk_window_impl_x11_finalize (GObject *object)
 
   wrapper = impl->wrapper;
 
+  if (WINDOW_IS_TOPLEVEL (wrapper) && impl->toplevel->in_frame)
+    unhook_surface_changed (wrapper);
+
   _gdk_x11_window_grab_check_destroy (wrapper);
 
   if (!GDK_WINDOW_DESTROYED (wrapper))
@@ -600,29 +738,32 @@ ensure_sync_counter (GdkWindow *window)
     {
       GdkDisplay *display = GDK_WINDOW_DISPLAY (window);
       GdkToplevelX11 *toplevel = _gdk_x11_window_get_toplevel (window);
-      GdkWindowImplX11 *impl = GDK_WINDOW_IMPL_X11 (window->impl);
 
-      if (toplevel && impl->use_synchronized_configure &&
+      if (toplevel &&
          toplevel->update_counter == None &&
          GDK_X11_DISPLAY (display)->use_sync)
        {
          Display *xdisplay = GDK_DISPLAY_XDISPLAY (display);
          XSyncValue value;
          Atom atom;
+         XID counters[2];
 
          XSyncIntToValue (&value, 0);
          
          toplevel->update_counter = XSyncCreateCounter (xdisplay, value);
+         toplevel->extended_update_counter = XSyncCreateCounter (xdisplay, value);
          
          atom = gdk_x11_get_xatom_by_name_for_display (display,
                                                        "_NET_WM_SYNC_REQUEST_COUNTER");
-         
+
+         counters[0] = toplevel->update_counter;
+         counters[1] = toplevel->extended_update_counter;
          XChangeProperty (xdisplay, GDK_WINDOW_XID (window),
                           atom, XA_CARDINAL,
                           32, PropModeReplace,
-                          (guchar *)&toplevel->update_counter, 1);
+                          (guchar *)counters, 2);
          
-         XSyncIntToValue (&toplevel->current_counter_value, 0);
+         toplevel->current_counter_value = 0;
        }
     }
 #endif
@@ -696,6 +837,23 @@ setup_toplevel_window (GdkWindow *window,
     gdk_x11_window_set_user_time (window, GDK_X11_DISPLAY (x11_screen->display)->user_time);
 
   ensure_sync_counter (window);
+
+  /* Start off in a frozen state - we'll finish this when we first paint */
+  gdk_x11_window_begin_frame (window);
+}
+
+static void
+on_frame_clock_before_paint (GdkFrameClock *clock,
+                             GdkWindow     *window)
+{
+  gdk_x11_window_begin_frame (window);
+}
+
+static void
+on_frame_clock_after_paint (GdkFrameClock *clock,
+                            GdkWindow     *window)
+{
+  gdk_x11_window_end_frame (window);
 }
 
 void
@@ -710,6 +868,7 @@ _gdk_x11_display_create_window_impl (GdkDisplay    *display,
   GdkWindowImplX11 *impl;
   GdkX11Screen *x11_screen;
   GdkX11Display *display_x11;
+  GdkFrameClock *clock;
 
   Window xparent;
   Visual *xvisual;
@@ -854,6 +1013,16 @@ _gdk_x11_display_create_window_impl (GdkDisplay    *display,
   gdk_x11_event_source_select_events ((GdkEventSource *) display_x11->event_source,
                                       GDK_WINDOW_XID (window), event_mask,
                                       StructureNotifyMask | PropertyChangeMask);
+
+  clock = g_object_new (GDK_TYPE_FRAME_CLOCK_IDLE, NULL);
+  gdk_window_set_frame_clock (window, clock);
+  g_signal_connect (clock, "before-paint",
+                    G_CALLBACK (on_frame_clock_before_paint), window);
+  g_signal_connect (clock, "after-paint",
+                    G_CALLBACK (on_frame_clock_after_paint), window);
+
+  if (GDK_WINDOW_TYPE (window) != GDK_WINDOW_CHILD)
+    gdk_window_freeze_toplevel_updates_libgtk_only (window);
 }
 
 static GdkEventMask
@@ -999,7 +1168,7 @@ gdk_toplevel_x11_free_contents (GdkDisplay *display,
                           toplevel->update_counter);
       toplevel->update_counter = None;
 
-      XSyncIntToValue (&toplevel->current_counter_value, 0);
+      toplevel->current_counter_value = 0;
     }
 #endif
 }
@@ -1272,6 +1441,14 @@ set_initial_hints (GdkWindow *window)
       ++i;
     }
 
+  if (window->state & GDK_WINDOW_STATE_ICONIFIED)
+    {
+      atoms[i] = gdk_x11_get_xatom_by_name_for_display (display,
+                                                       "_NET_WM_STATE_HIDDEN");
+      ++i;
+      toplevel->have_hidden = TRUE;
+    }
+
   if (i > 0)
     {
       XChangeProperty (xdisplay,
@@ -1345,6 +1522,13 @@ gdk_window_x11_show (GdkWindow *window, gboolean already_mapped)
   
   if (unset_bg)
     _gdk_x11_window_tmp_reset_bg (window, TRUE);
+
+  /* Fullscreen on current monitor is the default, no need to apply this mode
+   * when mapping a window. This also ensures that the default behavior remains
+   * consistent with pre-fullscreen mode implementation.
+   */
+  if (window->fullscreen_mode != GDK_FULLSCREEN_ON_CURRENT_MONITOR)
+    gdk_x11_window_apply_fullscreen_mode (window);
 }
 
 static void
@@ -1456,6 +1640,8 @@ window_x11_move (GdkWindow *window,
 
   if (GDK_WINDOW_TYPE (window) == GDK_WINDOW_CHILD)
     {
+      /* The window isn't actually damaged, but it's parent is */
+      window_pre_damage (window);
       _gdk_x11_window_move_resize_child (window,
                                          x, y,
                                          window->width, window->height);
@@ -1485,6 +1671,8 @@ window_x11_resize (GdkWindow *window,
   if (height < 1)
     height = 1;
 
+  window_pre_damage (window);
+
   if (GDK_WINDOW_TYPE (window) == GDK_WINDOW_CHILD)
     {
       _gdk_x11_window_move_resize_child (window,
@@ -1528,6 +1716,8 @@ window_x11_move_resize (GdkWindow *window,
   if (height < 1)
     height = 1;
 
+  window_pre_damage (window);
+
   if (GDK_WINDOW_TYPE (window) == GDK_WINDOW_CHILD)
     {
       _gdk_x11_window_move_resize_child (window, x, y, width, height);
@@ -1729,7 +1919,9 @@ static void
 move_to_current_desktop (GdkWindow *window)
 {
   if (gdk_x11_screen_supports_net_wm_hint (GDK_WINDOW_SCREEN (window),
-                                          gdk_atom_intern_static_string ("_NET_WM_DESKTOP")))
+                                          gdk_atom_intern_static_string ("_NET_WM_DESKTOP")) &&
+      gdk_x11_screen_supports_net_wm_hint (GDK_WINDOW_SCREEN (window),
+                                          gdk_atom_intern_static_string ("_NET_CURRENT_DESKTOP")))
     {
       Atom type;
       gint format;
@@ -1765,7 +1957,7 @@ move_to_current_desktop (GdkWindow *window)
           xclient.format = 32;
 
           xclient.data.l[0] = *current_desktop;
-          xclient.data.l[1] = 0;
+          xclient.data.l[1] = 1; /* source indication */
           xclient.data.l[2] = 0;
           xclient.data.l[3] = 0;
           xclient.data.l[4] = 0;
@@ -1804,7 +1996,7 @@ gdk_x11_window_focus (GdkWindow *window,
       xclient.type = ClientMessage;
       xclient.window = GDK_WINDOW_XID (window);
       xclient.message_type = gdk_x11_get_xatom_by_name_for_display (display,
-                                                                       "_NET_ACTIVE_WINDOW");
+                                                                    "_NET_ACTIVE_WINDOW");
       xclient.format = 32;
       xclient.data.l[0] = 1; /* requestor type; we're an app */
       xclient.data.l[1] = timestamp;
@@ -1988,7 +2180,7 @@ gdk_wmspec_change_state (gboolean   add,
   xclient.data.l[0] = add ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE;
   xclient.data.l[1] = gdk_x11_atom_to_xatom_for_display (display, state1);
   xclient.data.l[2] = gdk_x11_atom_to_xatom_for_display (display, state2);
-  xclient.data.l[3] = 0;
+  xclient.data.l[3] = 1; /* source indication */
   xclient.data.l[4] = 0;
   
   XSendEvent (GDK_WINDOW_XDISPLAY (window), GDK_WINDOW_XROOTWIN (window), False,
@@ -2708,7 +2900,9 @@ gdk_x11_window_get_frame_extents (GdkWindow    *window,
   xwindow = GDK_WINDOW_XID (window);
 
   /* first try: use _NET_FRAME_EXTENTS */
-  if (XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display), xwindow,
+  if (gdk_x11_screen_supports_net_wm_hint (GDK_WINDOW_SCREEN (window),
+                                           gdk_atom_intern_static_string ("_NET_FRAME_EXTENTS")) &&
+      XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display), xwindow,
                           gdk_x11_get_xatom_by_name_for_display (display,
                                                                   "_NET_FRAME_EXTENTS"),
                           0, G_MAXLONG, False, XA_CARDINAL, &type_return,
@@ -2755,7 +2949,9 @@ gdk_x11_window_get_frame_extents (GdkWindow    *window,
   /* use NETWM_VIRTUAL_ROOTS if available */
   root = GDK_WINDOW_XROOTWIN (window);
 
-  if (XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display), root,
+  if (gdk_x11_screen_supports_net_wm_hint (GDK_WINDOW_SCREEN (window),
+                                           gdk_atom_intern_static_string ("_NET_VIRTUAL_ROOTS")) &&
+      XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display), root,
                          gdk_x11_get_xatom_by_name_for_display (display, 
                                                                 "_NET_VIRTUAL_ROOTS"),
                          0, G_MAXLONG, False, XA_WINDOW, &type_return,
@@ -3095,6 +3291,8 @@ gdk_x11_window_set_user_time (GdkWindow *window,
  * This function modifies or removes an arbitrary X11 window
  * property of type UTF8_STRING.  If the given @window is
  * not a toplevel window, it is ignored.
+ *
+ * Since: 3.4
  */
 void
 gdk_x11_window_set_utf8_property  (GdkWindow *window,
@@ -3468,6 +3666,9 @@ gdk_x11_window_iconify (GdkWindow *window)
       gdk_synthesize_window_state (window,
                                    0,
                                    GDK_WINDOW_STATE_ICONIFIED);
+      gdk_wmspec_change_state (TRUE, window,
+                               gdk_atom_intern_static_string ("_NET_WM_STATE_HIDDEN"),
+                               GDK_NONE);
     }
 }
 
@@ -3481,6 +3682,9 @@ gdk_x11_window_deiconify (GdkWindow *window)
   if (GDK_WINDOW_IS_MAPPED (window))
     {  
       gdk_window_show (window);
+      gdk_wmspec_change_state (FALSE, window,
+                               gdk_atom_intern_static_string ("_NET_WM_STATE_HIDDEN"),
+                               GDK_NONE);
     }
   else
     {
@@ -3488,6 +3692,9 @@ gdk_x11_window_deiconify (GdkWindow *window)
       gdk_synthesize_window_state (window,
                                    GDK_WINDOW_STATE_ICONIFIED,
                                    0);
+      gdk_wmspec_change_state (FALSE, window,
+                               gdk_atom_intern_static_string ("_NET_WM_STATE_HIDDEN"),
+                               GDK_NONE);
     }
 }
 
@@ -3600,17 +3807,116 @@ gdk_x11_window_unmaximize (GdkWindow *window)
 }
 
 static void
-gdk_x11_window_fullscreen (GdkWindow *window)
+gdk_x11_window_apply_fullscreen_mode (GdkWindow *window)
 {
   if (GDK_WINDOW_DESTROYED (window) ||
       !WINDOW_IS_TOPLEVEL_OR_FOREIGN (window))
     return;
 
+  /* _NET_WM_FULLSCREEN_MONITORS gives an indication to the window manager as
+   * to which monitors so span across when the window is fullscreen, but it's
+   * not a state in itself so this would have no effect if the window is not
+   * mapped.
+   */
+
   if (GDK_WINDOW_IS_MAPPED (window))
-    gdk_wmspec_change_state (TRUE, window,
-                            gdk_atom_intern_static_string ("_NET_WM_STATE_FULLSCREEN"),
-                             GDK_NONE);
+    {
+      XClientMessageEvent xclient;
+      gint                gdk_monitors[4];
+      gint                i;
+
+      memset (&xclient, 0, sizeof (xclient));
+      xclient.type = ClientMessage;
+      xclient.window = GDK_WINDOW_XID (window);
+      xclient.display = GDK_WINDOW_XDISPLAY (window);
+      xclient.format = 32;
+
+      switch (window->fullscreen_mode)
+       {
+       case GDK_FULLSCREEN_ON_CURRENT_MONITOR:
+
+         /* FIXME: This is not part of the EWMH spec!
+          *
+          * There is no documented mechanism to remove the property
+          * _NET_WM_FULLSCREEN_MONITORS once set, so we use use a set of
+          * invalid, largest possible value.
+          *
+          * When given values larger than actual possible monitor values, most
+          * window managers who support the _NET_WM_FULLSCREEN_MONITORS spec
+          * will simply unset _NET_WM_FULLSCREEN_MONITORS and revert to their
+          * default behavior.
+          *
+          * Successfully tested on mutter/metacity, kwin, compiz and xfwm4.
+          *
+          * Note, this (non documented) mechanism is unlikely to be an issue
+          * as it's used only for transitionning back from "all monitors" to
+          * "current monitor" mode.
+          *
+          * Applications who don't change the default mode won't trigger this
+          * mechanism.
+          */
+         for (i = 0; i < 4; ++i)
+           xclient.data.l[i] = G_MAXLONG;
+
+         break;
+
+       case GDK_FULLSCREEN_ON_ALL_MONITORS:
+
+         _gdk_x11_screen_get_edge_monitors (GDK_WINDOW_SCREEN (window),
+                                            &gdk_monitors[0],
+                                            &gdk_monitors[1],
+                                            &gdk_monitors[2],
+                                            &gdk_monitors[3]);
+         /* Translate all 4 monitors from the GDK set into XINERAMA indices */
+         for (i = 0; i < 4; ++i)
+           {
+             xclient.data.l[i] = _gdk_x11_screen_get_xinerama_index (GDK_WINDOW_SCREEN (window),
+                                                                     gdk_monitors[i]);
+             /* Sanity check, if XINERAMA is not available, we could have invalid
+              * negative values for the XINERAMA indices.
+              */
+             if (xclient.data.l[i] < 0)
+               {
+                 g_warning ("gdk_x11_window_apply_fullscreen_mode: Invalid XINERAMA monitor index");
+                 return;
+               }
+           }
+         break;
+
+       default:
+         g_warning ("gdk_x11_window_apply_fullscreen_mode: Unhandled fullscreen mode %d",
+                    window->fullscreen_mode);
+         return;
+       }
+
+      /* Send fullscreen monitors client message */
+      xclient.data.l[4] = 1; /* source indication */
+      xclient.message_type = gdk_x11_get_xatom_by_name_for_display (GDK_WINDOW_DISPLAY (window),
+                                                                   "_NET_WM_FULLSCREEN_MONITORS");
+      XSendEvent (GDK_WINDOW_XDISPLAY (window), GDK_WINDOW_XROOTWIN (window), False,
+                  SubstructureRedirectMask | SubstructureNotifyMask,
+                  (XEvent *)&xclient);
+    }
+}
 
+static void
+gdk_x11_window_fullscreen (GdkWindow *window)
+{
+  if (GDK_WINDOW_DESTROYED (window) ||
+      !WINDOW_IS_TOPLEVEL_OR_FOREIGN (window))
+    return;
+
+  if (GDK_WINDOW_IS_MAPPED (window))
+    {
+      gdk_wmspec_change_state (TRUE, window,
+                              gdk_atom_intern_static_string ("_NET_WM_STATE_FULLSCREEN"),
+                               GDK_NONE);
+      /* Actual XRandR layout may have change since we computed the fullscreen
+       * monitors in GDK_FULLSCREEN_ON_ALL_MONITORS mode.
+       */
+      if (window->fullscreen_mode == GDK_FULLSCREEN_ON_ALL_MONITORS)
+       gdk_x11_window_apply_fullscreen_mode (window);
+    }
   else
     gdk_synthesize_window_state (window,
                                  0,
@@ -3890,17 +4196,24 @@ _gdk_x11_xwindow_get_shape (Display *xdisplay,
   shape = NULL;
   rn = 0;
 
-  xrl = XShapeGetRectangles (xdisplay,
-                            window,
-                            shape_type, &rn, &ord);
+  /* Note that XShapeGetRectangles returns NULL in two situations:
+   * - the server doesn't support the SHAPE extension
+   * - the shape is empty
+   *
+   * Since we can't discriminate these here, we always return
+   * an empty shape. It is the callers responsibility to check
+   * whether the server supports the SHAPE extensions beforehand.
+   */
+  xrl = XShapeGetRectangles (xdisplay, window, shape_type, &rn, &ord);
 
-  if (xrl == NULL || rn == 0)
+  if (rn == 0)
     return cairo_region_create (); /* Empty */
 
   if (ord != YXBanded)
     {
       /* This really shouldn't happen with any xserver, as they
-        generally convert regions to YXBanded internally */
+       * generally convert regions to YXBanded internally
+       */
       g_warning ("non YXBanded shape masks not supported");
       XFree (xrl);
       return NULL;
@@ -3915,10 +4228,10 @@ _gdk_x11_xwindow_get_shape (Display *xdisplay,
       rl[i].height = xrl[i].height;
     }
   XFree (xrl);
-  
+
   shape = cairo_region_create_rectangles (rl, rn);
   g_free (rl);
-  
+
   return shape;
 }
 
@@ -3940,7 +4253,7 @@ gdk_x11_window_get_input_shape (GdkWindow *window)
 {
 #if defined(ShapeInput)
   if (!GDK_WINDOW_DESTROYED (window) &&
-      gdk_display_supports_shapes (GDK_WINDOW_DISPLAY (window)))
+      gdk_display_supports_input_shapes (GDK_WINDOW_DISPLAY (window)))
     return _gdk_x11_xwindow_get_shape (GDK_WINDOW_XDISPLAY (window),
                                        GDK_WINDOW_XID (window),
                                        ShapeInput);
@@ -4011,21 +4324,30 @@ gdk_window_x11_set_static_gravities (GdkWindow *window,
   return TRUE;
 }
 
+/* From the WM spec */
+#define _NET_WM_MOVERESIZE_SIZE_TOPLEFT      0
+#define _NET_WM_MOVERESIZE_SIZE_TOP          1
+#define _NET_WM_MOVERESIZE_SIZE_TOPRIGHT     2
+#define _NET_WM_MOVERESIZE_SIZE_RIGHT        3
+#define _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT  4
+#define _NET_WM_MOVERESIZE_SIZE_BOTTOM       5
+#define _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT   6
+#define _NET_WM_MOVERESIZE_SIZE_LEFT         7
+#define _NET_WM_MOVERESIZE_MOVE              8   /* movement only */
+#define _NET_WM_MOVERESIZE_SIZE_KEYBOARD     9   /* size via keyboard */
+#define _NET_WM_MOVERESIZE_MOVE_KEYBOARD    10   /* move via keyboard */
+#define _NET_WM_MOVERESIZE_CANCEL           11   /* cancel operation */
+
 static void
-wmspec_moveresize (GdkWindow *window,
-                   gint       direction,
-                   GdkDevice *device,
-                   gint       root_x,
-                   gint       root_y,
-                   guint32    timestamp)     
+wmspec_send_message (GdkDisplay *display,
+                     GdkWindow  *window,
+                     gint        root_x,
+                     gint        root_y,
+                     gint        action,
+                     gint        button)
 {
-  GdkDisplay *display = GDK_WINDOW_DISPLAY (window);
-  
   XClientMessageEvent xclient;
 
-  /* Release passive grab */
-  gdk_device_ungrab (device, timestamp);
-
   memset (&xclient, 0, sizeof (xclient));
   xclient.type = ClientMessage;
   xclient.window = GDK_WINDOW_XID (window);
@@ -4034,49 +4356,67 @@ wmspec_moveresize (GdkWindow *window,
   xclient.format = 32;
   xclient.data.l[0] = root_x;
   xclient.data.l[1] = root_y;
-  xclient.data.l[2] = direction;
-  xclient.data.l[3] = 0;
-  xclient.data.l[4] = 0;
-  
+  xclient.data.l[2] = action;
+  xclient.data.l[3] = button;
+  xclient.data.l[4] = 1;  /* source indication */
+
   XSendEvent (GDK_DISPLAY_XDISPLAY (display), GDK_WINDOW_XROOTWIN (window), False,
-             SubstructureRedirectMask | SubstructureNotifyMask,
-             (XEvent *)&xclient);
+              SubstructureRedirectMask | SubstructureNotifyMask,
+              (XEvent *)&xclient);
 }
 
-typedef struct _MoveResizeData MoveResizeData;
+static void
+handle_wmspec_button_release (GdkDisplay *display,
+                              XEvent     *xevent)
+{
+  GdkX11Display *display_x11 = GDK_X11_DISPLAY (display);
+  GdkWindow *window;
 
-struct _MoveResizeData
+#if defined (HAVE_XGENERICEVENTS) && defined (XINPUT_2)
+  XIEvent *xiev = (XIEvent *) xevent->xcookie.data;
+  XIDeviceEvent *xidev = (XIDeviceEvent *) xiev;
+
+  if (xevent->xany.type == GenericEvent)
+    window = gdk_x11_window_lookup_for_display (display, xidev->event);
+  else
+#endif
+    window = gdk_x11_window_lookup_for_display (display, xevent->xany.window);
+
+  if (display_x11->wm_moveresize_button != 0 && window != NULL)
+    {
+      if ((xevent->xany.type == ButtonRelease &&
+           xevent->xbutton.button == display_x11->wm_moveresize_button)
+#if defined (HAVE_XGENERICEVENTS) && defined (XINPUT_2)
+          ||
+          (xevent->xany.type == GenericEvent &&
+           xiev->evtype == XI_ButtonRelease &&
+           xidev->detail == display_x11->wm_moveresize_button)
+#endif
+          )
+        {
+          display_x11->wm_moveresize_button = 0;
+          wmspec_send_message (display, window, 0, 0, _NET_WM_MOVERESIZE_CANCEL, 0);
+        }
+    }
+}
+
+static void
+wmspec_moveresize (GdkWindow *window,
+                   gint       direction,
+                   GdkDevice *device,
+                   gint       button,
+                   gint       root_x,
+                   gint       root_y,
+                   guint32    timestamp)
 {
-  GdkDisplay *display;
-  
-  GdkWindow *moveresize_window;
-  GdkWindow *moveresize_emulation_window;
-  gboolean is_resize;
-  GdkWindowEdge resize_edge;
-  GdkDevice *device;
-  gint moveresize_button;
-  gint moveresize_x;
-  gint moveresize_y;
-  gint moveresize_orig_x;
-  gint moveresize_orig_y;
-  gint moveresize_orig_width;
-  gint moveresize_orig_height;
-  GdkWindowHints moveresize_geom_mask;
-  GdkGeometry moveresize_geometry;
-  Time moveresize_process_time;
-  XEvent *moveresize_pending_event;
-};
+  GdkDisplay *display = GDK_WINDOW_DISPLAY (window);
 
-/* From the WM spec */
-#define _NET_WM_MOVERESIZE_SIZE_TOPLEFT      0
-#define _NET_WM_MOVERESIZE_SIZE_TOP          1
-#define _NET_WM_MOVERESIZE_SIZE_TOPRIGHT     2
-#define _NET_WM_MOVERESIZE_SIZE_RIGHT        3
-#define _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT  4
-#define _NET_WM_MOVERESIZE_SIZE_BOTTOM       5
-#define _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT   6
-#define _NET_WM_MOVERESIZE_SIZE_LEFT         7
-#define _NET_WM_MOVERESIZE_MOVE              8
+  /* Release passive grab */
+  gdk_device_ungrab (device, timestamp);
+  GDK_X11_DISPLAY (display)->wm_moveresize_button = button;
+
+  wmspec_send_message (display, window, root_x, root_y, direction, button);
+}
 
 static void
 wmspec_resize_drag (GdkWindow     *window,
@@ -4132,9 +4472,33 @@ wmspec_resize_drag (GdkWindow     *window,
       return;
     }
   
-  wmspec_moveresize (window, direction, device, root_x, root_y, timestamp);
+  wmspec_moveresize (window, direction, device, button, root_x, root_y, timestamp);
 }
 
+typedef struct _MoveResizeData MoveResizeData;
+
+struct _MoveResizeData
+{
+  GdkDisplay *display;
+
+  GdkWindow *moveresize_window;
+  GdkWindow *moveresize_emulation_window;
+  gboolean is_resize;
+  GdkWindowEdge resize_edge;
+  GdkDevice *device;
+  gint moveresize_button;
+  gint moveresize_x;
+  gint moveresize_y;
+  gint moveresize_orig_x;
+  gint moveresize_orig_y;
+  gint moveresize_orig_width;
+  gint moveresize_orig_height;
+  GdkWindowHints moveresize_geom_mask;
+  GdkGeometry moveresize_geometry;
+  Time moveresize_process_time;
+  XEvent *moveresize_pending_event;
+};
+
 static MoveResizeData *
 get_move_resize_data (GdkDisplay *display,
                      gboolean    create)
@@ -4315,7 +4679,10 @@ _gdk_x11_moveresize_handle_event (XEvent *event)
   MoveResizeData *mv_resize = get_move_resize_data (display, FALSE);
 
   if (!mv_resize || !mv_resize->moveresize_window)
-    return FALSE;
+    {
+      handle_wmspec_button_release (display, event);
+      return FALSE;
+    }
 
   button_mask = GDK_BUTTON1_MASK << (mv_resize->moveresize_button - 1);
 
@@ -4358,7 +4725,7 @@ _gdk_x11_moveresize_handle_event (XEvent *event)
         finish_drag (mv_resize);
       break;
 
-#ifdef HAVE_XGENERICEVENTS
+#if defined (HAVE_XGENERICEVENTS) && defined (XINPUT_2)
     case GenericEvent:
       {
         /* we just assume this is an XI2 event */
@@ -4617,8 +4984,8 @@ gdk_x11_window_begin_move_drag (GdkWindow *window,
 
   if (gdk_x11_screen_supports_net_wm_hint (GDK_WINDOW_SCREEN (window),
                                           gdk_atom_intern_static_string ("_NET_WM_MOVERESIZE")))
-    wmspec_moveresize (window, _NET_WM_MOVERESIZE_MOVE, device, root_x, root_y,
-                      timestamp);
+    wmspec_moveresize (window, _NET_WM_MOVERESIZE_MOVE,
+                       device, button, root_x, root_y, timestamp);
   else
     emulate_move_drag (window, device, button, root_x, root_y, timestamp);
 }
@@ -4664,13 +5031,21 @@ gdk_x11_window_configure_finished (GdkWindow *window)
 
       if (toplevel && toplevel->update_counter != None &&
          GDK_X11_DISPLAY (display)->use_sync &&
-         !XSyncValueIsZero (toplevel->current_counter_value))
+         toplevel->configure_counter_value != 0)
        {
-         XSyncSetCounter (GDK_WINDOW_XDISPLAY (window), 
-                          toplevel->update_counter,
-                          toplevel->current_counter_value);
-         
-         XSyncIntToValue (&toplevel->current_counter_value, 0);
+         set_sync_counter (GDK_WINDOW_XDISPLAY (window),
+                           toplevel->update_counter,
+                           toplevel->configure_counter_value);
+
+         toplevel->current_counter_value = toplevel->configure_counter_value;
+         if ((toplevel->current_counter_value % 2) == 1)
+           toplevel->current_counter_value += 1;
+
+         toplevel->configure_counter_value = 0;
+
+         set_sync_counter (GDK_WINDOW_XDISPLAY (window),
+                           toplevel->extended_update_counter,
+                           toplevel->current_counter_value);
        }
     }
 #endif
@@ -4777,6 +5152,11 @@ _gdk_x11_display_before_process_all_updates (GdkDisplay *display)
 void
 _gdk_x11_display_after_process_all_updates (GdkDisplay *display)
 {
+  /* Sync after all drawing, otherwise the client can get "ahead" of
+     the server rendering during animations, such that we fill up
+     the Xserver pipes with sync rendering ops not letting other
+     clients (including the VM) do anything. */
+  XSync (GDK_DISPLAY_XDISPLAY (display), FALSE);
 }
 
 static Bool
@@ -4867,10 +5247,6 @@ gdk_x11_window_get_xid (GdkWindow *window)
   return GDK_WINDOW_IMPL_X11 (window->impl)->xid;
 }
 
-extern GdkDragContext * _gdk_x11_window_drag_begin (GdkWindow *window,
-                                                    GdkDevice *device,
-                                                    GList     *targets);
-
 static void
 gdk_window_impl_x11_class_init (GdkWindowImplX11Class *klass)
 {
@@ -4934,6 +5310,7 @@ gdk_window_impl_x11_class_init (GdkWindowImplX11Class *klass)
   impl_class->maximize = gdk_x11_window_maximize;
   impl_class->unmaximize = gdk_x11_window_unmaximize;
   impl_class->fullscreen = gdk_x11_window_fullscreen;
+  impl_class->apply_fullscreen_mode = gdk_x11_window_apply_fullscreen_mode;
   impl_class->unfullscreen = gdk_x11_window_unfullscreen;
   impl_class->set_keep_above = gdk_x11_window_set_keep_above;
   impl_class->set_keep_below = gdk_x11_window_set_keep_below;