[gtk+/wip/frame-synchronization: 836/857] Add GdkFrameHistory and GdkFrameTimings, handle _NET_WM_FRAME_TIMINGS



commit c6966f00cef4f58266e8d16227303ab32734e0fc
Author: Owen W. Taylor <otaylor fishsoup net>
Date:   Wed Nov 14 12:49:06 2012 -0500

    Add GdkFrameHistory and GdkFrameTimings, handle _NET_WM_FRAME_TIMINGS
    
    In order to be able to track statistics about how well we are drawing,
    and in order to be able to do sophisticated things with frame timing
    like predicting per-frame latencies and synchronizing audio with video,
    we need to be able to track exactly when previous frames were drawn
    to the screen.
    
    Information about each frame is stored in a new GdkFrameTimings object.
    A new GdkFrameHistory object is added which keeps a queue of recent
    GdkFrameTimings (this is added to avoid further complicating the
    implementation of GdkFrameClock.)
    
    https://bugzilla.gnome.org/show_bug.cgi?id=685460

 gdk/Makefile.am          |    4 +
 gdk/gdkframeclock.c      |   17 +++++
 gdk/gdkframeclock.h      |    6 ++-
 gdk/gdkframeclockidle.c  |   19 +++++
 gdk/gdkframehistory.c    |  143 ++++++++++++++++++++++++++++++++++++
 gdk/gdkframehistory.h    |   49 +++++++++++++
 gdk/gdkframetimings.c    |  180 ++++++++++++++++++++++++++++++++++++++++++++++
 gdk/gdkframetimings.h    |   63 ++++++++++++++++
 gdk/x11/gdkdisplay-x11.c |  100 ++++++++++++++++++++++----
 gdk/x11/gdkwindow-x11.c  |   10 +++
 10 files changed, 575 insertions(+), 16 deletions(-)
---
diff --git a/gdk/Makefile.am b/gdk/Makefile.am
index a265bca..61aeb4b 100644
--- a/gdk/Makefile.am
+++ b/gdk/Makefile.am
@@ -75,6 +75,8 @@ gdk_public_h_sources = 				\
 	gdkdisplaymanager.h			\
 	gdkdnd.h				\
 	gdkevents.h				\
+	gdkframehistory.h			\
+	gdkframetimings.h			\
 	gdkkeys.h				\
 	gdkkeysyms.h				\
 	gdkkeysyms-compat.h			\
@@ -123,6 +125,8 @@ gdk_c_sources = 				\
 	gdkdisplaymanager.c			\
 	gdkdnd.c				\
 	gdkevents.c     			\
+	gdkframehistory.c			\
+	gdkframetimings.c			\
 	gdkglobals.c				\
 	gdkkeys.c				\
 	gdkkeyuni.c				\
diff --git a/gdk/gdkframeclock.c b/gdk/gdkframeclock.c
index 8c6d202..ecb84bd 100644
--- a/gdk/gdkframeclock.c
+++ b/gdk/gdkframeclock.c
@@ -305,6 +305,23 @@ gdk_frame_clock_thaw (GdkFrameClock *clock)
 }
 
 /**
+ * gdk_frame_clock_get_history:
+ * @clock: the clock
+ *
+ * Gets the #GdkFrameHistory for the frame clock.
+ *
+ * Since: 3.8
+ * Return value: (transfer none): the frame history object
+ */
+GdkFrameHistory *
+gdk_frame_clock_get_history (GdkFrameClock *clock)
+{
+  g_return_val_if_fail (GDK_IS_FRAME_CLOCK (clock), NULL);
+
+  return GDK_FRAME_CLOCK_GET_IFACE (clock)->get_history (clock);
+}
+
+/**
  * gdk_frame_clock_get_requested:
  * @clock: the clock
  *
diff --git a/gdk/gdkframeclock.h b/gdk/gdkframeclock.h
index d75fce9..03b0430 100644
--- a/gdk/gdkframeclock.h
+++ b/gdk/gdkframeclock.h
@@ -31,7 +31,7 @@
 #ifndef __GDK_FRAME_CLOCK_H__
 #define __GDK_FRAME_CLOCK_H__
 
-#include <glib-object.h>
+#include <gdk/gdkframehistory.h>
 
 G_BEGIN_DECLS
 
@@ -87,6 +87,8 @@ struct _GdkFrameClockInterface
   void     (* freeze)              (GdkFrameClock *clock);
   void     (* thaw)                (GdkFrameClock *clock);
 
+  GdkFrameHistory *  (* get_history)   (GdkFrameClock *clock);
+
   /* signals */
   /* void (* frame_requested)    (GdkFrameClock *clock); */
   /* void (* flush_events)       (GdkFrameClock *clock); */
@@ -109,6 +111,8 @@ GdkFrameClockPhase gdk_frame_clock_get_requested (GdkFrameClock      *clock);
 void     gdk_frame_clock_freeze              (GdkFrameClock *clock);
 void     gdk_frame_clock_thaw                (GdkFrameClock *clock);
 
+GdkFrameHistory *gdk_frame_clock_get_history (GdkFrameClock *clock);
+
 /* Convenience API */
 void  gdk_frame_clock_get_frame_time_val (GdkFrameClock  *clock,
                                           GTimeVal       *timeval);
diff --git a/gdk/gdkframeclockidle.c b/gdk/gdkframeclockidle.c
index c8a9036..00d284c 100644
--- a/gdk/gdkframeclockidle.c
+++ b/gdk/gdkframeclockidle.c
@@ -33,6 +33,7 @@
 
 struct _GdkFrameClockIdlePrivate
 {
+  GdkFrameHistory *history;
   GTimer *timer;
   /* timer_base is used to avoid ever going backward */
   guint64 timer_base;
@@ -77,6 +78,7 @@ gdk_frame_clock_idle_init (GdkFrameClockIdle *frame_clock_idle)
                                                         GdkFrameClockIdlePrivate);
   priv = frame_clock_idle->priv;
 
+  priv->history = gdk_frame_history_new ();
   priv->timer = g_timer_new ();
   priv->freeze_count = 0;
 }
@@ -229,7 +231,14 @@ gdk_frame_clock_paint_idle (void *data)
         case GDK_FRAME_CLOCK_PHASE_BEFORE_PAINT:
           if (priv->freeze_count == 0)
             {
+              GdkFrameTimings *timings;
+              gint64 frame_counter;
+
               priv->frame_time = compute_frame_time (clock_idle);
+              gdk_frame_history_begin_frame (priv->history);
+              frame_counter = gdk_frame_history_get_frame_counter (priv->history);
+              timings = gdk_frame_history_get_timings (priv->history, frame_counter);
+              gdk_frame_timings_set_frame_time (timings, priv->frame_time);
 
               priv->phase = GDK_FRAME_CLOCK_PHASE_BEFORE_PAINT;
 
@@ -370,6 +379,15 @@ gdk_frame_clock_idle_thaw (GdkFrameClock *clock)
     }
 }
 
+static GdkFrameHistory *
+gdk_frame_clock_idle_get_history (GdkFrameClock *clock)
+{
+  GdkFrameClockIdle *clock_idle = GDK_FRAME_CLOCK_IDLE (clock);
+  GdkFrameClockIdlePrivate *priv = clock_idle->priv;
+
+  return priv->history;
+}
+
 static void
 gdk_frame_clock_idle_interface_init (GdkFrameClockInterface *iface)
 {
@@ -378,6 +396,7 @@ gdk_frame_clock_idle_interface_init (GdkFrameClockInterface *iface)
   iface->get_requested = gdk_frame_clock_idle_get_requested;
   iface->freeze = gdk_frame_clock_idle_freeze;
   iface->thaw = gdk_frame_clock_idle_thaw;
+  iface->get_history = gdk_frame_clock_idle_get_history;
 }
 
 GdkFrameClock *
diff --git a/gdk/gdkframehistory.c b/gdk/gdkframehistory.c
new file mode 100644
index 0000000..e993e5e
--- /dev/null
+++ b/gdk/gdkframehistory.c
@@ -0,0 +1,143 @@
+/* GDK - The GIMP Drawing Kit
+ * Copyright (C) 2012 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include "gdkframehistory.h"
+
+#define FRAME_HISTORY_MAX_LENGTH 16
+
+struct _GdkFrameHistory
+{
+  GObject parent_instance;
+
+  gint64 frame_counter;
+  gint n_timings;
+  gint current;
+  GdkFrameTimings *timings[FRAME_HISTORY_MAX_LENGTH];
+};
+
+struct _GdkFrameHistoryClass
+{
+  GObjectClass parent_class;
+};
+
+G_DEFINE_TYPE (GdkFrameHistory, gdk_frame_history, G_TYPE_OBJECT)
+
+static void
+gdk_frame_history_finalize (GObject *object)
+{
+  GdkFrameHistory *history = GDK_FRAME_HISTORY (object);
+  int i;
+
+  for (i = 0; i < FRAME_HISTORY_MAX_LENGTH; i++)
+    if (history->timings[i] != 0)
+      gdk_frame_timings_unref (history->timings[i]);
+
+  G_OBJECT_CLASS (gdk_frame_history_parent_class)->finalize (object);
+}
+
+static void
+gdk_frame_history_class_init (GdkFrameHistoryClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->finalize = gdk_frame_history_finalize;
+}
+
+static void
+gdk_frame_history_init (GdkFrameHistory *history)
+{
+  history->frame_counter = -1;
+  history->current = FRAME_HISTORY_MAX_LENGTH - 1;
+}
+
+GdkFrameHistory *
+gdk_frame_history_new (void)
+{
+  return g_object_new (GDK_TYPE_FRAME_HISTORY, NULL);
+}
+
+gint64
+gdk_frame_history_get_frame_counter (GdkFrameHistory *history)
+{
+  g_return_val_if_fail (GDK_IS_FRAME_HISTORY (history), 0);
+
+  return history->frame_counter;
+}
+
+gint64
+gdk_frame_history_get_start (GdkFrameHistory *history)
+{
+  g_return_val_if_fail (GDK_IS_FRAME_HISTORY (history), 0);
+
+  return history->frame_counter + 1 - history->n_timings;
+}
+
+void
+gdk_frame_history_begin_frame (GdkFrameHistory *history)
+{
+  g_return_if_fail (GDK_IS_FRAME_HISTORY (history));
+
+  history->frame_counter++;
+  history->current = (history->current + 1) % FRAME_HISTORY_MAX_LENGTH;
+
+  if (history->n_timings < FRAME_HISTORY_MAX_LENGTH)
+    history->n_timings++;
+  else
+    {
+      gdk_frame_timings_unref(history->timings[history->current]);
+    }
+
+  history->timings[history->current] = gdk_frame_timings_new (history->frame_counter);
+}
+
+GdkFrameTimings *
+gdk_frame_history_get_timings (GdkFrameHistory *history,
+                               gint64           frame_counter)
+{
+  gint pos;
+
+  g_return_val_if_fail (GDK_IS_FRAME_HISTORY (history), NULL);
+
+  if (frame_counter > history->frame_counter)
+    return NULL;
+
+  if (frame_counter <= history->frame_counter - history->n_timings)
+    return NULL;
+
+  pos = (history->current - (history->frame_counter - frame_counter) + FRAME_HISTORY_MAX_LENGTH) % FRAME_HISTORY_MAX_LENGTH;
+
+  return history->timings[pos];
+}
+
+GdkFrameTimings *
+gdk_frame_history_get_last_complete (GdkFrameHistory *history)
+{
+  gint i;
+
+  g_return_val_if_fail (GDK_IS_FRAME_HISTORY (history), NULL);
+
+  for (i = 0; i < history->n_timings; i++)
+    {
+      gint pos = ((history->current - i) + FRAME_HISTORY_MAX_LENGTH) % FRAME_HISTORY_MAX_LENGTH;
+      if (gdk_frame_timings_get_complete (history->timings[pos]))
+        return history->timings[pos];
+    }
+
+  return NULL;
+}
diff --git a/gdk/gdkframehistory.h b/gdk/gdkframehistory.h
new file mode 100644
index 0000000..56e9db9
--- /dev/null
+++ b/gdk/gdkframehistory.h
@@ -0,0 +1,49 @@
+/* GDK - The GIMP Drawing Kit
+ * Copyright (C) 2012 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GDK_H_INSIDE__) && !defined (GDK_COMPILATION)
+#error "Only <gdk/gdk.h> can be included directly."
+#endif
+
+#ifndef __GDK_FRAME_HISTORY_H__
+#define __GDK_FRAME_HISTORY_H__
+
+#include <gdk/gdkframetimings.h>
+
+G_BEGIN_DECLS
+
+typedef struct _GdkFrameHistory      GdkFrameHistory;
+typedef struct _GdkFrameHistoryClass GdkFrameHistoryClass;
+
+#define GDK_TYPE_FRAME_HISTORY  (gdk_frame_history_get_type ())
+#define GDK_FRAME_HISTORY(o)    (G_TYPE_CHECK_INSTANCE_CAST ((o), GDK_TYPE_FRAME_HISTORY, GdkFrameHistory))
+#define GDK_IS_FRAME_HISTORY(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GDK_TYPE_FRAME_HISTORY))
+
+GType            gdk_frame_history_get_type (void) G_GNUC_CONST;
+
+GdkFrameHistory *gdk_frame_history_new (void);
+
+gint64           gdk_frame_history_get_frame_counter (GdkFrameHistory *history);
+gint64           gdk_frame_history_get_start         (GdkFrameHistory *history);
+void             gdk_frame_history_begin_frame       (GdkFrameHistory *history);
+GdkFrameTimings *gdk_frame_history_get_timings       (GdkFrameHistory *history,
+                                                      gint64           frame_counter);
+GdkFrameTimings *gdk_frame_history_get_last_complete (GdkFrameHistory *history);
+
+G_END_DECLS
+
+#endif /* __GDK_FRAME_HISTORY_H__ */
diff --git a/gdk/gdkframetimings.c b/gdk/gdkframetimings.c
new file mode 100644
index 0000000..d9354ed
--- /dev/null
+++ b/gdk/gdkframetimings.c
@@ -0,0 +1,180 @@
+/* GDK - The GIMP Drawing Kit
+ * Copyright (C) 2012 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include "gdkframetimings.h"
+
+struct _GdkFrameTimings
+{
+  guint ref_count;
+
+  gboolean complete;
+  gint64 frame_counter;
+  guint64 cookie;
+  gint64 frame_time;
+  gint64 drawn_time;
+  gint64 presentation_time;
+  gint64 refresh_interval;
+};
+
+G_DEFINE_BOXED_TYPE (GdkFrameTimings, gdk_frame_timings,
+                     gdk_frame_timings_ref,
+                     gdk_frame_timings_unref)
+
+GdkFrameTimings *
+gdk_frame_timings_new (gint64 frame_counter)
+{
+  GdkFrameTimings *timings;
+
+  timings = g_slice_new0 (GdkFrameTimings);
+  timings->ref_count = 1;
+  timings->frame_counter = frame_counter;
+
+  return timings;
+}
+
+GdkFrameTimings *
+gdk_frame_timings_ref (GdkFrameTimings *timings)
+{
+  g_return_val_if_fail (timings != NULL, NULL);
+
+  timings->ref_count++;
+
+  return timings;
+}
+
+void
+gdk_frame_timings_unref (GdkFrameTimings *timings)
+{
+  g_return_if_fail (timings != NULL);
+  g_return_if_fail (timings->ref_count > 0);
+
+  timings->ref_count--;
+  if (timings->ref_count == 0)
+    {
+      g_slice_free (GdkFrameTimings, timings);
+    }
+}
+
+gint64
+gdk_frame_timings_get_frame_counter (GdkFrameTimings *timings)
+{
+  return timings->frame_counter;
+}
+
+guint64
+gdk_frame_timings_get_cookie (GdkFrameTimings *timings)
+{
+  g_return_val_if_fail (timings != NULL, 0);
+
+  return timings->cookie;
+}
+
+void
+gdk_frame_timings_set_cookie (GdkFrameTimings *timings,
+                              guint64          cookie)
+{
+  g_return_if_fail (timings != NULL);
+
+  timings->cookie = cookie;
+}
+
+gboolean
+gdk_frame_timings_get_complete (GdkFrameTimings *timings)
+{
+  g_return_val_if_fail (timings != NULL, FALSE);
+
+  return timings->complete;
+}
+
+void
+gdk_frame_timings_set_complete (GdkFrameTimings *timings,
+                                gboolean         complete)
+{
+  g_return_if_fail (timings != NULL);
+
+  timings->complete = complete;
+}
+
+gint64
+gdk_frame_timings_get_frame_time (GdkFrameTimings *timings)
+{
+  g_return_val_if_fail (timings != NULL, 0);
+
+  return timings->frame_time;
+}
+
+void
+gdk_frame_timings_set_frame_time (GdkFrameTimings *timings,
+                                  gint64           frame_time)
+{
+  g_return_if_fail (timings != NULL);
+
+  timings->frame_time = frame_time;
+}
+
+gint64
+gdk_frame_timings_get_drawn_time (GdkFrameTimings *timings)
+{
+  g_return_val_if_fail (timings != NULL, 0);
+
+  return timings->drawn_time;
+}
+
+void
+gdk_frame_timings_set_drawn_time (GdkFrameTimings *timings,
+                                  gint64           drawn_time)
+{
+  g_return_if_fail (timings != NULL);
+
+  timings->drawn_time = drawn_time;
+}
+
+gint64
+gdk_frame_timings_get_presentation_time (GdkFrameTimings *timings)
+{
+  g_return_val_if_fail (timings != NULL, 0);
+
+  return timings->presentation_time;
+}
+
+void
+gdk_frame_timings_set_presentation_time (GdkFrameTimings *timings,
+                                         gint64           presentation_time)
+{
+  g_return_if_fail (timings != NULL);
+
+  timings->presentation_time = presentation_time;
+}
+
+gint64
+gdk_frame_timings_get_refresh_interval (GdkFrameTimings *timings)
+{
+  g_return_val_if_fail (timings != NULL, 0);
+
+  return timings->refresh_interval;
+}
+
+void
+gdk_frame_timings_set_refresh_interval (GdkFrameTimings *timings,
+                                        gint64           refresh_interval)
+{
+  g_return_if_fail (timings != NULL);
+
+  timings->refresh_interval = refresh_interval;
+}
diff --git a/gdk/gdkframetimings.h b/gdk/gdkframetimings.h
new file mode 100644
index 0000000..7fddbd4
--- /dev/null
+++ b/gdk/gdkframetimings.h
@@ -0,0 +1,63 @@
+/* GDK - The GIMP Drawing Kit
+ * Copyright (C) 2012 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__GDK_H_INSIDE__) && !defined (GDK_COMPILATION)
+#error "Only <gdk/gdk.h> can be included directly."
+#endif
+
+#ifndef __GDK_FRAME_TIMINGS_H__
+#define __GDK_FRAME_TIMINGS_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+typedef struct _GdkFrameTimings GdkFrameTimings;
+
+GType            gdk_frame_timings_get_type (void) G_GNUC_CONST;
+
+GdkFrameTimings *gdk_frame_timings_new (gint64 frame_counter);
+
+GdkFrameTimings *gdk_frame_timings_ref   (GdkFrameTimings *timings);
+void             gdk_frame_timings_unref (GdkFrameTimings *timings);
+
+gint64           gdk_frame_timings_get_frame_counter     (GdkFrameTimings *timings);
+
+guint64          gdk_frame_timings_get_cookie            (GdkFrameTimings *timings);
+void             gdk_frame_timings_set_cookie            (GdkFrameTimings *timings,
+                                                          guint64          cookie);
+
+gboolean         gdk_frame_timings_get_complete          (GdkFrameTimings *timings);
+void             gdk_frame_timings_set_complete          (GdkFrameTimings *timings,
+                                                          gboolean         complete);
+
+gint64           gdk_frame_timings_get_frame_time        (GdkFrameTimings *timings);
+void             gdk_frame_timings_set_frame_time        (GdkFrameTimings *timings,
+                                                          gint64           frame_time);
+gint64           gdk_frame_timings_get_drawn_time        (GdkFrameTimings *timings);
+void             gdk_frame_timings_set_drawn_time        (GdkFrameTimings *timings,
+                                                          gint64           frame_time);
+gint64           gdk_frame_timings_get_presentation_time (GdkFrameTimings *timings);
+void             gdk_frame_timings_set_presentation_time (GdkFrameTimings *timings,
+                                                          gint64           presentation_time);
+gint64           gdk_frame_timings_get_refresh_interval  (GdkFrameTimings *timings);
+void             gdk_frame_timings_set_refresh_interval  (GdkFrameTimings *timings,
+                                                          gint64           refresh_interval);
+
+G_END_DECLS
+
+#endif /* __GDK_FRAME_TIMINGS_H__ */
diff --git a/gdk/x11/gdkdisplay-x11.c b/gdk/x11/gdkdisplay-x11.c
index 4e28f74..ae4aaaf 100644
--- a/gdk/x11/gdkdisplay-x11.c
+++ b/gdk/x11/gdkdisplay-x11.c
@@ -1056,6 +1056,26 @@ gdk_x11_display_translate_event (GdkEventTranslator *translator,
   return return_val;
 }
 
+static GdkFrameTimings *
+find_frame_timings (GdkFrameClock *clock,
+                    guint64        serial)
+{
+  GdkFrameHistory *history = gdk_frame_clock_get_history (clock);
+  gint64 start_frame, end_frame, i;
+
+  start_frame = gdk_frame_history_get_start (history);
+  end_frame = gdk_frame_history_get_frame_counter (history);
+  for (i = end_frame; i >= start_frame; i--)
+    {
+      GdkFrameTimings *timings = gdk_frame_history_get_timings (history, i);
+
+      if (gdk_frame_timings_get_cookie (timings) == serial)
+        return timings;
+    }
+
+  return NULL;
+}
+
 GdkFilterReturn
 _gdk_wm_protocols_filter (GdkXEvent *xev,
 			  GdkEvent  *event,
@@ -1074,6 +1094,71 @@ _gdk_wm_protocols_filter (GdkXEvent *xev,
 
   display = GDK_WINDOW_DISPLAY (win);
 
+  /* This isn't actually WM_PROTOCOLS because that wouldn't leave enough space
+   * in the message for everything that gets stuffed in */
+  if (xevent->xclient.message_type == gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_FRAME_DRAWN"))
+    {
+      GdkWindowImplX11 *window_impl;
+      window_impl = GDK_WINDOW_IMPL_X11 (event->any.window->impl);
+      if (window_impl->toplevel)
+        {
+          guint32 d0 = xevent->xclient.data.l[0];
+          guint32 d1 = xevent->xclient.data.l[1];
+          guint32 d2 = xevent->xclient.data.l[2];
+          guint32 d3 = xevent->xclient.data.l[3];
+
+          guint64 serial = ((guint64)d0 << 32) | d1;
+
+          GdkFrameClock *clock = gdk_window_get_frame_clock (event->any.window);
+          GdkFrameTimings *timings = find_frame_timings (clock, serial);
+
+          if (timings)
+            gdk_frame_timings_set_drawn_time (timings, ((guint64)d2 << 32) | d3);
+
+          if (window_impl->toplevel->frame_pending)
+            {
+              window_impl->toplevel->frame_pending = FALSE;
+              gdk_frame_clock_thaw (clock);
+            }
+        }
+
+      return GDK_FILTER_REMOVE;
+    }
+
+  if (xevent->xclient.message_type == gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_FRAME_TIMINGS"))
+    {
+      GdkWindowImplX11 *window_impl;
+      window_impl = GDK_WINDOW_IMPL_X11 (event->any.window->impl);
+      if (window_impl->toplevel)
+        {
+          guint32 d0 = xevent->xclient.data.l[0];
+          guint32 d1 = xevent->xclient.data.l[1];
+          guint32 d2 = xevent->xclient.data.l[2];
+          guint32 d3 = xevent->xclient.data.l[3];
+
+          guint64 serial = ((guint64)d0 << 32) | d1;
+
+          GdkFrameClock *clock = gdk_window_get_frame_clock (event->any.window);
+          GdkFrameTimings *timings = find_frame_timings (clock, serial);
+
+          if (timings)
+            {
+              gint64 drawn_time = gdk_frame_timings_get_drawn_time (timings);
+              gint32 presentation_time_offset = (gint32)d2;
+              gint32 refresh_interval = d3;
+
+              if (drawn_time && presentation_time_offset)
+                gdk_frame_timings_set_presentation_time (timings,
+                                                         drawn_time + presentation_time_offset);
+
+              if (refresh_interval)
+                gdk_frame_timings_set_refresh_interval (timings, refresh_interval);
+
+              gdk_frame_timings_set_complete (timings, TRUE);
+            }
+        }
+    }
+
   if (xevent->xclient.message_type != gdk_x11_get_xatom_by_name_for_display (display, "WM_PROTOCOLS"))
     return GDK_FILTER_CONTINUE;
 
@@ -1145,21 +1230,6 @@ _gdk_wm_protocols_filter (GdkXEvent *xev,
       return GDK_FILTER_REMOVE;
     }
 
-  else if (atom == gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_FRAME_DRAWN"))
-    {
-      GdkWindowImplX11 *window_impl;
-
-      window_impl = GDK_WINDOW_IMPL_X11 (event->any.window->impl);
-      if (window_impl->toplevel &&
-          window_impl->toplevel->frame_pending)
-        {
-          window_impl->toplevel->frame_pending = FALSE;
-          gdk_frame_clock_thaw (gdk_window_get_frame_clock (event->any.window));
-        }
-
-      return GDK_FILTER_REMOVE;
-    }
-
   return GDK_FILTER_CONTINUE;
 }
 
diff --git a/gdk/x11/gdkwindow-x11.c b/gdk/x11/gdkwindow-x11.c
index f2c5735..874db0a 100644
--- a/gdk/x11/gdkwindow-x11.c
+++ b/gdk/x11/gdkwindow-x11.c
@@ -853,7 +853,17 @@ static void
 on_frame_clock_after_paint (GdkFrameClock *clock,
                             GdkWindow     *window)
 {
+  GdkToplevelX11 *toplevel = _gdk_x11_window_get_toplevel (window);
+  GdkFrameHistory *history = gdk_frame_clock_get_history (clock);
+  gint64 frame_counter = gdk_frame_history_get_frame_counter (history);
+  GdkFrameTimings *timings = gdk_frame_history_get_timings (history, frame_counter);
+
   gdk_x11_window_end_frame (window);
+
+  if (toplevel->frame_pending)
+    gdk_frame_timings_set_cookie (timings, toplevel->current_counter_value);
+  else
+    gdk_frame_timings_set_complete (timings, TRUE);
 }
 
 void


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]