]> 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 99d2f7f659779a4eaf877c82939e811cb5e3d5b5..f2c5735e79d5c8d26e6c7d4245c81547bbf2cf37 100644 (file)
@@ -213,6 +213,70 @@ set_sync_counter(Display     *display,
     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)
 {
@@ -226,10 +290,9 @@ gdk_x11_window_begin_frame (GdkWindow *window)
       impl->toplevel->extended_update_counter == None)
     return;
 
-  impl->toplevel->current_counter_value += 1;
-  set_sync_counter(GDK_WINDOW_XDISPLAY (impl->wrapper),
-                  impl->toplevel->extended_update_counter,
-                  impl->toplevel->current_counter_value);
+  impl->toplevel->in_frame = TRUE;
+
+  hook_surface_changed (window);
 }
 
 static void
@@ -242,20 +305,28 @@ gdk_x11_window_end_frame (GdkWindow *window)
   impl = GDK_WINDOW_IMPL_X11 (window->impl);
 
   if (!WINDOW_IS_TOPLEVEL (window) ||
-      impl->toplevel->extended_update_counter == None)
+      impl->toplevel->extended_update_counter == None ||
+      !impl->toplevel->in_frame)
     return;
 
-  impl->toplevel->current_counter_value += 1;
-  set_sync_counter(GDK_WINDOW_XDISPLAY (impl->wrapper),
-                  impl->toplevel->extended_update_counter,
-                  impl->toplevel->current_counter_value);
+  impl->toplevel->in_frame = FALSE;
 
-  if (gdk_x11_screen_supports_net_wm_hint (gdk_window_get_screen (window),
-                                           gdk_atom_intern_static_string ("_NET_WM_FRAME_DRAWN")))
+  if (impl->toplevel->current_counter_value % 2 == 1)
     {
-      impl->toplevel->frame_pending = TRUE;
-      gdk_frame_clock_freeze (gdk_window_get_frame_clock (window));
+      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);
 }
 
 /*****************************************************
@@ -301,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);
@@ -320,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))
@@ -760,6 +837,9 @@ 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
@@ -940,6 +1020,9 @@ _gdk_x11_display_create_window_impl (GdkDisplay    *display,
                     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
@@ -1557,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);
@@ -1586,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,
@@ -1629,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);