]> 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 4f99b027f57e78b981963e8ac7d7c8e10e1dc8ed..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);
@@ -128,6 +128,27 @@ static void        gdk_window_impl_x11_finalize   (GObject            *object);
     (( time1 < time2 ) && ( time2 - time1 > ((guint32)-1)/2 ))     \
   )
 
+struct _GdkX11Window {
+  GdkWindow parent;
+};
+
+struct _GdkX11WindowClass {
+  GdkWindowClass parent_class;
+};
+
+G_DEFINE_TYPE (GdkX11Window, gdk_x11_window, GDK_TYPE_WINDOW)
+
+static void
+gdk_x11_window_class_init (GdkX11WindowClass *x11_window_class)
+{
+}
+
+static void
+gdk_x11_window_init (GdkX11Window *x11_window)
+{
+}
+
+
 G_DEFINE_TYPE (GdkWindowImplX11, gdk_window_impl_x11, GDK_TYPE_WINDOW_IMPL)
 
 static void
@@ -151,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;
 }
@@ -176,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 *
  *****************************************************/
@@ -219,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);
@@ -238,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))
@@ -252,7 +411,7 @@ gdk_window_impl_x11_finalize (GObject *object)
   g_free (impl->toplevel);
 
   if (impl->cursor)
-    gdk_cursor_unref (impl->cursor);
+    g_object_unref (impl->cursor);
 
   g_hash_table_destroy (impl->device_cursor);
 
@@ -312,7 +471,7 @@ _gdk_x11_window_create_bitmap_surface (GdkWindow *window,
                           width, height, 1);
   surface = cairo_xlib_surface_create_for_bitmap (GDK_WINDOW_XDISPLAY (window),
                                                   pixmap,
-                                                  GDK_SCREEN_X11 (GDK_WINDOW_SCREEN (window))->xscreen,
+                                                  GDK_X11_SCREEN (GDK_WINDOW_SCREEN (window))->xscreen,
                                                   width, height);
   attach_free_pixmap_handler (surface, GDK_WINDOW_DISPLAY (window), pixmap);
 
@@ -454,13 +613,13 @@ _gdk_x11_screen_init_root_window (GdkScreen *screen)
 {
   GdkWindow *window;
   GdkWindowImplX11 *impl;
-  GdkScreenX11 *screen_x11;
+  GdkX11Screen *x11_screen;
 
-  screen_x11 = GDK_SCREEN_X11 (screen);
+  x11_screen = GDK_X11_SCREEN (screen);
 
-  g_assert (screen_x11->root_window == NULL);
+  g_assert (x11_screen->root_window == NULL);
 
-  window = screen_x11->root_window = g_object_new (GDK_TYPE_WINDOW, NULL);
+  window = x11_screen->root_window = _gdk_display_create_window (gdk_screen_get_display (screen));
 
   window->impl = g_object_new (GDK_TYPE_WINDOW_IMPL_X11, NULL);
   window->impl_window = window;
@@ -468,28 +627,28 @@ _gdk_x11_screen_init_root_window (GdkScreen *screen)
 
   impl = GDK_WINDOW_IMPL_X11 (window->impl);
   
-  impl->xid = screen_x11->xroot_window;
+  impl->xid = x11_screen->xroot_window;
   impl->wrapper = window;
   
   window->window_type = GDK_WINDOW_ROOT;
-  window->depth = DefaultDepthOfScreen (screen_x11->xscreen);
+  window->depth = DefaultDepthOfScreen (x11_screen->xscreen);
 
   window->x = 0;
   window->y = 0;
   window->abs_x = 0;
   window->abs_y = 0;
-  window->width = WidthOfScreen (screen_x11->xscreen);
-  window->height = HeightOfScreen (screen_x11->xscreen);
+  window->width = WidthOfScreen (x11_screen->xscreen);
+  window->height = HeightOfScreen (x11_screen->xscreen);
   window->viewable = TRUE;
 
   /* see init_randr_support() in gdkscreen-x11.c */
   window->event_mask = GDK_STRUCTURE_MASK;
 
-  _gdk_window_update_size (screen_x11->root_window);
+  _gdk_window_update_size (x11_screen->root_window);
 
-  _gdk_x11_display_add_window (screen_x11->display,
-                               &screen_x11->xroot_window,
-                               screen_x11->root_window);
+  _gdk_x11_display_add_window (x11_screen->display,
+                               &x11_screen->xroot_window,
+                               x11_screen->root_window);
 }
 
 static void
@@ -504,7 +663,7 @@ set_wm_protocols (GdkWindow *window)
   protocols[n++] = gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_PING");
 
 #ifdef HAVE_XSYNC
-  if (GDK_DISPLAY_X11 (display)->use_sync)
+  if (GDK_X11_DISPLAY (display)->use_sync)
     protocols[n++] = gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_SYNC_REQUEST");
 #endif
   
@@ -528,7 +687,7 @@ get_default_title (void)
 static void
 check_leader_window_title (GdkDisplay *display)
 {
-  GdkDisplayX11 *display_x11 = GDK_DISPLAY_X11 (display);
+  GdkX11Display *display_x11 = GDK_X11_DISPLAY (display);
 
   if (display_x11->leader_window && !display_x11->leader_window_title_set)
     {
@@ -544,13 +703,13 @@ static Window
 create_focus_window (GdkDisplay *display,
                     XID         parent)
 {
-  GdkDisplayX11 *display_x11;
+  GdkX11Display *display_x11;
   GdkEventMask event_mask;
   Display *xdisplay;
   Window focus_window;
 
   xdisplay = GDK_DISPLAY_XDISPLAY (display);
-  display_x11 = GDK_DISPLAY_X11 (display);
+  display_x11 = GDK_X11_DISPLAY (display);
 
   focus_window = XCreateSimpleWindow (xdisplay, parent,
                                       -1, -1, 1, 1, 0,
@@ -579,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_DISPLAY_X11 (display)->use_sync)
+         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
@@ -615,7 +777,7 @@ setup_toplevel_window (GdkWindow *window,
   GdkDisplay *display = gdk_window_get_display (window);
   Display *xdisplay = GDK_WINDOW_XDISPLAY (window);
   XID xid = GDK_WINDOW_XID (window);
-  GdkScreenX11 *screen_x11 = GDK_SCREEN_X11 (GDK_WINDOW_SCREEN (parent));
+  GdkX11Screen *x11_screen = GDK_X11_SCREEN (GDK_WINDOW_SCREEN (parent));
   XSizeHints size_hints;
   long pid;
   Window leader_window;
@@ -628,12 +790,12 @@ setup_toplevel_window (GdkWindow *window,
        * press events so they don't get sent to child windows.
        */
       toplevel->focus_window = create_focus_window (display, xid);
-      _gdk_x11_display_add_window (screen_x11->display,
+      _gdk_x11_display_add_window (x11_screen->display,
                                    &toplevel->focus_window,
                                    window);
     }
 
-  check_leader_window_title (screen_x11->display);
+  check_leader_window_title (x11_screen->display);
 
   /* FIXME: Is there any point in doing this? Do any WM's pay
    * attention to PSize, and even if they do, is this the
@@ -650,31 +812,48 @@ setup_toplevel_window (GdkWindow *window,
   
   pid = getpid ();
   XChangeProperty (xdisplay, xid,
-                  gdk_x11_get_xatom_by_name_for_display (screen_x11->display, "_NET_WM_PID"),
+                  gdk_x11_get_xatom_by_name_for_display (x11_screen->display, "_NET_WM_PID"),
                   XA_CARDINAL, 32,
                   PropModeReplace,
                   (guchar *)&pid, 1);
 
-  leader_window = GDK_DISPLAY_X11 (screen_x11->display)->leader_window;
+  leader_window = GDK_X11_DISPLAY (x11_screen->display)->leader_window;
   if (!leader_window)
     leader_window = xid;
   XChangeProperty (xdisplay, xid, 
-                  gdk_x11_get_xatom_by_name_for_display (screen_x11->display, "WM_CLIENT_LEADER"),
+                  gdk_x11_get_xatom_by_name_for_display (x11_screen->display, "WM_CLIENT_LEADER"),
                   XA_WINDOW, 32, PropModeReplace,
                   (guchar *) &leader_window, 1);
 
   if (toplevel->focus_window != None)
     XChangeProperty (xdisplay, xid, 
-                     gdk_x11_get_xatom_by_name_for_display (screen_x11->display, "_NET_WM_USER_TIME_WINDOW"),
+                     gdk_x11_get_xatom_by_name_for_display (x11_screen->display, "_NET_WM_USER_TIME_WINDOW"),
                      XA_WINDOW, 32, PropModeReplace,
                      (guchar *) &toplevel->focus_window, 1);
 
   if (!window->focus_on_map)
     gdk_x11_window_set_user_time (window, 0);
-  else if (GDK_DISPLAY_X11 (screen_x11->display)->user_time != 0)
-    gdk_x11_window_set_user_time (window, GDK_DISPLAY_X11 (screen_x11->display)->user_time);
+  else if (GDK_X11_DISPLAY (x11_screen->display)->user_time != 0)
+    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
@@ -687,8 +866,9 @@ _gdk_x11_display_create_window_impl (GdkDisplay    *display,
                                      gint           attributes_mask)
 {
   GdkWindowImplX11 *impl;
-  GdkScreenX11 *screen_x11;
-  GdkDisplayX11 *display_x11;
+  GdkX11Screen *x11_screen;
+  GdkX11Display *display_x11;
+  GdkFrameClock *clock;
 
   Window xparent;
   Visual *xvisual;
@@ -701,15 +881,15 @@ _gdk_x11_display_create_window_impl (GdkDisplay    *display,
   unsigned int class;
   const char *title;
 
-  display_x11 = GDK_DISPLAY_X11 (display);
+  display_x11 = GDK_X11_DISPLAY (display);
   xparent = GDK_WINDOW_XID (real_parent);
-  screen_x11 = GDK_SCREEN_X11 (screen);
+  x11_screen = GDK_X11_SCREEN (screen);
 
   impl = g_object_new (GDK_TYPE_WINDOW_IMPL_X11, NULL);
   window->impl = GDK_WINDOW_IMPL (impl);
   impl->wrapper = GDK_WINDOW (window);
 
-  xdisplay = screen_x11->xdisplay;
+  xdisplay = x11_screen->xdisplay;
 
   xattributes_mask = 0;
 
@@ -748,9 +928,9 @@ _gdk_x11_display_create_window_impl (GdkDisplay    *display,
     {
       class = InputOutput;
 
-      xattributes.background_pixel = BlackPixel (xdisplay, screen_x11->screen_num);
+      xattributes.background_pixel = BlackPixel (xdisplay, x11_screen->screen_num);
 
-      xattributes.border_pixel = BlackPixel (xdisplay, screen_x11->screen_num);
+      xattributes.border_pixel = BlackPixel (xdisplay, x11_screen->screen_num);
       xattributes_mask |= CWBorderPixel | CWBackPixel;
 
       if (window->guffaw_gravity)
@@ -797,7 +977,7 @@ _gdk_x11_display_create_window_impl (GdkDisplay    *display,
                              xattributes_mask, &xattributes);
 
   g_object_ref (window);
-  _gdk_x11_display_add_window (screen_x11->display, &impl->xid, window);
+  _gdk_x11_display_add_window (x11_screen->display, &impl->xid, window);
 
   switch (GDK_WINDOW_TYPE (window))
     {
@@ -833,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
@@ -855,7 +1045,9 @@ x_event_mask_to_gdk_event_mask (long mask)
  * @display: the #GdkDisplay where the window handle comes from.
  * @window: an XLib <type>Window</type>
  *
- * Wraps a native window in a #GdkWindow.
+ * Wraps a native window in a #GdkWindow. The function will try to
+ * look up the window using gdk_x11_window_lookup_for_display() first.
+ * If it does not find it there, it will create a new window.
  *
  * This may fail if the window has been destroyed. If the window
  * was already known to GDK, a new reference to the existing
@@ -874,7 +1066,7 @@ gdk_x11_window_foreign_new_for_display (GdkDisplay *display,
   GdkScreen *screen;
   GdkWindow *win;
   GdkWindowImplX11 *impl;
-  GdkDisplayX11 *display_x11;
+  GdkX11Display *display_x11;
   XWindowAttributes attrs;
   Window root, parent;
   Window *children = NULL;
@@ -883,7 +1075,7 @@ gdk_x11_window_foreign_new_for_display (GdkDisplay *display,
 
   g_return_val_if_fail (GDK_IS_DISPLAY (display), NULL);
 
-  display_x11 = GDK_DISPLAY_X11 (display);
+  display_x11 = GDK_X11_DISPLAY (display);
 
   if ((win = gdk_x11_window_lookup_for_display (display, window)) != NULL)
     return g_object_ref (win);
@@ -906,7 +1098,7 @@ gdk_x11_window_foreign_new_for_display (GdkDisplay *display,
 
   screen = _gdk_x11_display_screen_for_xrootwin (display, root);
 
-  win = g_object_new (GDK_TYPE_WINDOW, NULL);
+  win = _gdk_display_create_window (display);
   win->impl = g_object_new (GDK_TYPE_WINDOW_IMPL_X11, NULL);
   win->impl_window = win;
   win->visual = gdk_x11_screen_lookup_visual (screen,
@@ -976,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
 }
@@ -1085,6 +1277,32 @@ gdk_x11_window_destroy_notify (GdkWindow *window)
   g_object_unref (window);
 }
 
+static GdkDragProtocol
+gdk_x11_window_get_drag_protocol (GdkWindow *window,
+                                  GdkWindow **target)
+{
+  GdkDragProtocol protocol;
+  GdkDisplay *display;
+  guint version;
+  Window xid;
+
+  display = gdk_window_get_display (window);
+  xid = _gdk_x11_display_get_drag_protocol (display,
+                                            GDK_WINDOW_XID (window->impl_window),
+                                            &protocol,
+                                            &version);
+
+  if (target)
+    {
+      if (xid != None)
+        *target = gdk_x11_window_foreign_new_for_display (display, xid);
+      else
+        *target = NULL;
+    }
+
+  return protocol;
+}
+
 static void
 update_wm_hints (GdkWindow *window,
                 gboolean   force)
@@ -1127,7 +1345,7 @@ update_wm_hints (GdkWindow *window,
       wm_hints.window_group = GDK_WINDOW_XID (toplevel->group_leader);
     }
   else
-    wm_hints.window_group = GDK_DISPLAY_X11 (display)->leader_window;
+    wm_hints.window_group = GDK_X11_DISPLAY (display)->leader_window;
 
   if (toplevel->urgency_hint)
     wm_hints.flags |= XUrgencyHint;
@@ -1223,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,
@@ -1262,7 +1488,7 @@ static void
 gdk_window_x11_show (GdkWindow *window, gboolean already_mapped)
 {
   GdkDisplay *display;
-  GdkDisplayX11 *display_x11;
+  GdkX11Display *display_x11;
   GdkToplevelX11 *toplevel;
   GdkWindowImplX11 *impl = GDK_WINDOW_IMPL_X11 (window->impl);
   Display *xdisplay = GDK_WINDOW_XDISPLAY (window);
@@ -1275,7 +1501,7 @@ gdk_window_x11_show (GdkWindow *window, gboolean already_mapped)
   if (WINDOW_IS_TOPLEVEL (window))
     {
       display = gdk_window_get_display (window);
-      display_x11 = GDK_DISPLAY_X11 (display);
+      display_x11 = GDK_X11_DISPLAY (display);
       toplevel = _gdk_x11_window_get_toplevel (window);
       
       if (toplevel->user_time != 0 &&
@@ -1296,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
@@ -1407,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);
@@ -1436,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,
@@ -1479,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);
@@ -1650,7 +1889,7 @@ gdk_window_x11_lower (GdkWindow *window)
 
 /**
  * gdk_x11_window_move_to_current_desktop:
- * @window: a #GdkWindow
+ * @window: (type GdkX11Window): a #GdkWindow
  * 
  * Moves the window to the correct workspace when running under a 
  * window manager that supports multiple workspaces, as described
@@ -1680,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;
@@ -1716,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;
@@ -1755,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;
@@ -1772,11 +2013,14 @@ gdk_x11_window_focus (GdkWindow *window,
       XRaiseWindow (GDK_DISPLAY_XDISPLAY (display), GDK_WINDOW_XID (window));
 
       /* There is no way of knowing reliably whether we are viewable;
-       * _gdk_x11_set_input_focus_safe() traps errors asynchronously.
+       * so trap errors asynchronously around the XSetInputFocus call
        */
-      _gdk_x11_set_input_focus_safe (display, GDK_WINDOW_XID (window),
-                                    RevertToParent,
-                                    timestamp);
+      gdk_x11_display_error_trap_push (display);
+      XSetInputFocus (GDK_DISPLAY_XDISPLAY (display),
+                      GDK_WINDOW_XID (window),
+                      RevertToParent,
+                      timestamp);
+      gdk_x11_display_error_trap_pop_ignored (display);
     }
 }
 
@@ -1936,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,
@@ -2223,7 +2467,7 @@ set_text_property (GdkDisplay  *display,
   if (utf8_is_latin1 (utf8_str))
     {
       prop_type = XA_STRING;
-      prop_text = gdk_utf8_to_string_target (utf8_str);
+      prop_text = _gdk_x11_display_utf8_to_string_target (display, utf8_str);
       prop_length = prop_text ? strlen (prop_text) : 0;
       prop_format = 8;
       is_compound_text = FALSE;
@@ -2656,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,
@@ -2703,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,
@@ -2811,12 +3059,12 @@ gdk_window_x11_set_events (GdkWindow    *window,
   
   if (!GDK_WINDOW_DESTROYED (window))
     {
-      GdkDisplayX11 *display_x11;
+      GdkX11Display *display_x11;
 
       if (GDK_WINDOW_XID (window) != GDK_WINDOW_XROOTWIN (window))
         xevent_mask = StructureNotifyMask | PropertyChangeMask;
 
-      display_x11 = GDK_DISPLAY_X11 (gdk_window_get_display (window));
+      display_x11 = GDK_X11_DISPLAY (gdk_window_get_display (window));
       gdk_x11_event_source_select_events ((GdkEventSource *) display_x11->event_source,
                                           GDK_WINDOW_XID (window), event_mask,
                                           xevent_mask);
@@ -2971,7 +3219,7 @@ gdk_x11_window_set_focus_on_map (GdkWindow *window,
 
 /**
  * gdk_x11_window_set_user_time:
- * @window: A toplevel #GdkWindow
+ * @window: (type GdkX11Window): A toplevel #GdkWindow
  * @timestamp: An XServer timestamp to which the property should be set
  *
  * The application can use this call to update the _NET_WM_USER_TIME
@@ -2994,7 +3242,7 @@ gdk_x11_window_set_user_time (GdkWindow *window,
                               guint32    timestamp)
 {
   GdkDisplay *display;
-  GdkDisplayX11 *display_x11;
+  GdkX11Display *display_x11;
   GdkToplevelX11 *toplevel;
   glong timestamp_long = (glong)timestamp;
   Window xid;
@@ -3004,7 +3252,7 @@ gdk_x11_window_set_user_time (GdkWindow *window,
     return;
 
   display = gdk_window_get_display (window);
-  display_x11 = GDK_DISPLAY_X11 (display);
+  display_x11 = GDK_X11_DISPLAY (display);
   toplevel = _gdk_x11_window_get_toplevel (window);
 
   if (!toplevel)
@@ -3034,6 +3282,113 @@ gdk_x11_window_set_user_time (GdkWindow *window,
     toplevel->user_time = timestamp_long;
 }
 
+/**
+ * gdk_x11_window_set_utf8_property:
+ * @window: (type GdkX11Window): a #GdkWindow
+ * @name: Property name, will be interned as an X atom
+ * @value: (allow-none): Property value, or %NULL to delete
+ *
+ * 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,
+                                  const gchar *name,
+                                  const gchar *value)
+{
+  GdkDisplay *display;
+
+  if (!WINDOW_IS_TOPLEVEL (window))
+    return;
+
+  display = gdk_window_get_display (window);
+
+  if (value != NULL)
+    {
+      XChangeProperty (GDK_DISPLAY_XDISPLAY (display),
+                       GDK_WINDOW_XID (window),
+                       gdk_x11_get_xatom_by_name_for_display (display, name),
+                       gdk_x11_get_xatom_by_name_for_display (display, "UTF8_STRING"), 8,
+                       PropModeReplace, (guchar *)value, strlen (value));
+    }
+  else
+    {
+      XDeleteProperty (GDK_DISPLAY_XDISPLAY (display),
+                       GDK_WINDOW_XID (window),
+                       gdk_x11_get_xatom_by_name_for_display (display, name));
+    }
+}
+
+/**
+ * gdk_x11_window_set_hide_titlebar_when_maximized:
+ * @window: (type GdkX11Window): a #GdkWindow
+ * @hide_titlebar_when_maximized: whether to hide the titlebar when
+ *                                maximized
+ *
+ * Set a hint for the window manager, requesting that the titlebar
+ * should be hidden when the window is maximized.
+ *
+ * Note that this property is automatically updated by GTK+, so this
+ * function should only be used by applications which do not use GTK+
+ * to create toplevel windows.
+ *
+ * Since: 3.4
+ */
+void
+gdk_x11_window_set_hide_titlebar_when_maximized (GdkWindow *window,
+                                                 gboolean   hide_titlebar_when_maximized)
+{
+  GdkDisplay *display;
+
+  if (!WINDOW_IS_TOPLEVEL (window))
+    return;
+
+  display = gdk_window_get_display (window);
+
+  if (hide_titlebar_when_maximized)
+    {
+      guint32 hide = 1;
+      XChangeProperty (GDK_DISPLAY_XDISPLAY (display),
+                       GDK_WINDOW_XID (window),
+                       gdk_x11_get_xatom_by_name_for_display (display, "_GTK_HIDE_TITLEBAR_WHEN_MAXIMIZED"),
+                       XA_CARDINAL, 32,
+                       PropModeReplace, (guchar *)&hide, 1);
+    }
+  else
+    {
+      XDeleteProperty (GDK_DISPLAY_XDISPLAY (display),
+                       GDK_WINDOW_XID (window),
+                       gdk_x11_get_xatom_by_name_for_display (display, "_GTK_HIDE_TITLEBAR_WHEN_MAXIMIZED"));
+    }
+}
+
+/**
+ * gdk_x11_window_set_theme_variant:
+ * @window: (type GdkX11Window): a #GdkWindow
+ * @variant: the theme variant to export
+ *
+ * GTK+ applications can request a dark theme variant. In order to
+ * make other applications - namely window managers using GTK+ for
+ * themeing - aware of this choice, GTK+ uses this function to
+ * export the requested theme variant as _GTK_THEME_VARIANT property
+ * on toplevel windows.
+ *
+ * Note that this property is automatically updated by GTK+, so this
+ * function should only be used by applications which do not use GTK+
+ * to create toplevel windows.
+ *
+ * Since: 3.2
+ */
+void
+gdk_x11_window_set_theme_variant (GdkWindow *window,
+                                  char      *variant)
+{
+  return gdk_x11_window_set_utf8_property (window, "_GTK_THEME_VARIANT", variant);
+}
+
 #define GDK_SELECTION_MAX_SIZE(display)                                 \
   MIN(262144,                                                           \
       XExtendedMaxRequestSize (GDK_DISPLAY_XDISPLAY (display)) == 0     \
@@ -3311,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);
     }
 }
 
@@ -3324,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
     {
@@ -3331,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);
     }
 }
 
@@ -3443,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,
@@ -3733,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;
@@ -3758,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;
 }
 
@@ -3783,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);
@@ -3854,20 +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,
-                   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_display_pointer_ungrab (display, timestamp);
-
   memset (&xclient, 0, sizeof (xclient));
   xclient.type = ClientMessage;
   xclient.window = GDK_WINDOW_XID (window);
@@ -3876,52 +4356,72 @@ 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;
-  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,
                     GdkWindowEdge  edge,
+                    GdkDevice     *device,
                     gint           button,
                     gint           root_x,
                     gint           root_y,
@@ -3972,9 +4472,33 @@ wmspec_resize_drag (GdkWindow     *window,
       return;
     }
   
-  wmspec_moveresize (window, direction, 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)
@@ -4155,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);
 
@@ -4197,6 +4724,33 @@ _gdk_x11_moveresize_handle_event (XEvent *event)
       if (event->xbutton.button == mv_resize->moveresize_button)
         finish_drag (mv_resize);
       break;
+
+#if defined (HAVE_XGENERICEVENTS) && defined (XINPUT_2)
+    case GenericEvent:
+      {
+        /* we just assume this is an XI2 event */
+        XIEvent *ev = (XIEvent *) event->xcookie.data;
+        XIDeviceEvent *xev = (XIDeviceEvent *)ev;
+        gint state;
+        switch (ev->evtype)
+          {
+          case XI_Motion:
+            update_pos (mv_resize, xev->root_x, xev->root_y);
+            state = _gdk_x11_device_xi2_translate_state (&xev->mods, &xev->buttons, &xev->group);
+            if ((state & button_mask) == 0)
+            finish_drag (mv_resize);
+            break;
+
+          case XI_ButtonRelease:
+            update_pos (mv_resize, xev->root_x, xev->root_y);
+            if (xev->detail == mv_resize->moveresize_button)
+              finish_drag (mv_resize);
+            break;
+          }
+      }
+      break;
+#endif
+
     }
   return TRUE;
 }
@@ -4250,13 +4804,13 @@ create_moveresize_window (MoveResizeData *mv_resize,
 
   gdk_window_show (mv_resize->moveresize_emulation_window);
 
-  status = gdk_pointer_grab (mv_resize->moveresize_emulation_window,
-                             FALSE,
-                             GDK_BUTTON_RELEASE_MASK |
-                             GDK_POINTER_MOTION_MASK,
-                             NULL,
-                             NULL,
-                             timestamp);
+  status = gdk_device_grab (mv_resize->device,
+                            mv_resize->moveresize_emulation_window,
+                            GDK_OWNERSHIP_NONE,
+                            FALSE,
+                            GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK,
+                            NULL,
+                            timestamp);
 
   if (status != GDK_GRAB_SUCCESS)
     {
@@ -4344,6 +4898,7 @@ calculate_unmoving_origin (MoveResizeData *mv_resize)
 static void
 emulate_resize_drag (GdkWindow     *window,
                      GdkWindowEdge  edge,
+                     GdkDevice     *device,
                      gint           button,
                      gint           root_x,
                      gint           root_y,
@@ -4354,6 +4909,7 @@ emulate_resize_drag (GdkWindow     *window,
   mv_resize->is_resize = TRUE;
   mv_resize->moveresize_button = button;
   mv_resize->resize_edge = edge;
+  mv_resize->device = device;
   mv_resize->moveresize_x = root_x;
   mv_resize->moveresize_y = root_y;
   mv_resize->moveresize_window = g_object_ref (window);
@@ -4373,6 +4929,7 @@ emulate_resize_drag (GdkWindow     *window,
 
 static void
 emulate_move_drag (GdkWindow     *window,
+                   GdkDevice     *device,
                    gint           button,
                    gint           root_x,
                    gint           root_y,
@@ -4381,6 +4938,7 @@ emulate_move_drag (GdkWindow     *window,
   MoveResizeData *mv_resize = get_move_resize_data (GDK_WINDOW_DISPLAY (window), TRUE);
   
   mv_resize->is_resize = FALSE;
+  mv_resize->device = device;
   mv_resize->moveresize_button = button;
   mv_resize->moveresize_x = root_x;
   mv_resize->moveresize_y = root_y;
@@ -4394,11 +4952,12 @@ emulate_move_drag (GdkWindow     *window,
 
 static void
 gdk_x11_window_begin_resize_drag (GdkWindow     *window,
-                                 GdkWindowEdge  edge,
-                                 gint           button,
-                                 gint           root_x,
-                                 gint           root_y,
-                                 guint32        timestamp)
+                                  GdkWindowEdge  edge,
+                                  GdkDevice     *device,
+                                  gint           button,
+                                  gint           root_x,
+                                  gint           root_y,
+                                  guint32        timestamp)
 {
   if (GDK_WINDOW_DESTROYED (window) ||
       !WINDOW_IS_TOPLEVEL_OR_FOREIGN (window))
@@ -4406,13 +4965,14 @@ gdk_x11_window_begin_resize_drag (GdkWindow     *window,
 
   if (gdk_x11_screen_supports_net_wm_hint (GDK_WINDOW_SCREEN (window),
                                           gdk_atom_intern_static_string ("_NET_WM_MOVERESIZE")))
-    wmspec_resize_drag (window, edge, button, root_x, root_y, timestamp);
+    wmspec_resize_drag (window, edge, device, button, root_x, root_y, timestamp);
   else
-    emulate_resize_drag (window, edge, button, root_x, root_y, timestamp);
+    emulate_resize_drag (window, edge, device, button, root_x, root_y, timestamp);
 }
 
 static void
 gdk_x11_window_begin_move_drag (GdkWindow *window,
+                                GdkDevice *device,
                                gint       button,
                                gint       root_x,
                                gint       root_y,
@@ -4424,10 +4984,10 @@ 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, root_x, root_y,
-                      timestamp);
+    wmspec_moveresize (window, _NET_WM_MOVERESIZE_MOVE,
+                       device, button, root_x, root_y, timestamp);
   else
-    emulate_move_drag (window, button, root_x, root_y, timestamp);
+    emulate_move_drag (window, device, button, root_x, root_y, timestamp);
 }
 
 static void
@@ -4470,14 +5030,22 @@ gdk_x11_window_configure_finished (GdkWindow *window)
       GdkToplevelX11 *toplevel = _gdk_x11_window_get_toplevel (window);
 
       if (toplevel && toplevel->update_counter != None &&
-         GDK_DISPLAY_X11 (display)->use_sync &&
-         !XSyncValueIsZero (toplevel->current_counter_value))
+         GDK_X11_DISPLAY (display)->use_sync &&
+         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
@@ -4491,7 +5059,7 @@ gdk_x11_window_beep (GdkWindow *window)
   display = GDK_WINDOW_DISPLAY (window);
 
 #ifdef HAVE_XKB
-  if (GDK_DISPLAY_X11 (display)->use_xkb)
+  if (GDK_X11_DISPLAY (display)->use_xkb)
     {
       XkbBell (GDK_DISPLAY_XDISPLAY (display),
                GDK_WINDOW_XID (window),
@@ -4584,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
@@ -4605,9 +5178,10 @@ timestamp_predicate (Display *display,
 
 /**
  * gdk_x11_get_server_time:
- * @window: a #GdkWindow, used for communication with the server.
- *          The window must have GDK_PROPERTY_CHANGE_MASK in its
- *          events mask or a hang will result.
+ * @window: (type GdkX11Window): a #GdkWindow, used for communication
+ *          with the server.  The window must have
+ *          GDK_PROPERTY_CHANGE_MASK in its events mask or a hang will
+ *          result.
  *
  * Routine to get the current X server time stamp.
  *
@@ -4643,7 +5217,7 @@ gdk_x11_get_server_time (GdkWindow *window)
 
 /**
  * gdk_x11_window_get_xid:
- * @window: a native #GdkWindow.
+ * @window: (type GdkX11Window): a native #GdkWindow.
  * 
  * Returns the X resource (window) belonging to a #GdkWindow.
  * 
@@ -4673,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)
 {
@@ -4740,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;
@@ -4756,6 +5327,7 @@ gdk_window_impl_x11_class_init (GdkWindowImplX11Class *klass)
   impl_class->set_opacity = gdk_x11_window_set_opacity;
   impl_class->set_composited = gdk_x11_window_set_composited;
   impl_class->destroy_notify = gdk_x11_window_destroy_notify;
+  impl_class->get_drag_protocol = gdk_x11_window_get_drag_protocol;
   impl_class->register_dnd = _gdk_x11_window_register_dnd;
   impl_class->drag_begin = _gdk_x11_window_drag_begin;
   impl_class->process_updates_recurse = gdk_x11_window_process_updates_recurse;