[gtk+/wip/frame-synchronization: 814/857] Add GdkFrameClock
- From: Owen Taylor <otaylor src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtk+/wip/frame-synchronization: 814/857] Add GdkFrameClock
- Date: Wed, 13 Feb 2013 06:11:57 +0000 (UTC)
commit 68559ee790962ab9e933502c19ef1701ee5e454f
Author: Owen W. Taylor <otaylor fishsoup net>
Date: Wed Oct 3 18:34:01 2012 -0400
Add GdkFrameClock
Add an object GdkFrameClock that we associate with a GdkWindow.
This tracks when the window needs to be repainted, and will also
be used for other operations in the future like relayout and
updating animations.
Based on a patch from Havoc Pennington:
https://mail.gnome.org/archives/gtk-devel-list/2010-October/msg00004.html
https://bugzilla.gnome.org/show_bug.cgi?id=685460
gdk/Makefile.am | 4 +
gdk/gdkframeclock.c | 287 +++++++++++++++++++++++++++++++++++++++++++++++
gdk/gdkframeclock.h | 77 +++++++++++++
gdk/gdkframeclockidle.c | 182 ++++++++++++++++++++++++++++++
gdk/gdkframeclockidle.h | 68 +++++++++++
gdk/gdkinternals.h | 2 +
gdk/gdkwindow.c | 278 +++++++++++++++++++++++++++++++++++++---------
gdk/gdkwindow.h | 6 +
gtk/gtkwidget.c | 48 ++++++++
gtk/gtkwidget.h | 2 +
10 files changed, 902 insertions(+), 52 deletions(-)
---
diff --git a/gdk/Makefile.am b/gdk/Makefile.am
index a04f7a3..a265bca 100644
--- a/gdk/Makefile.am
+++ b/gdk/Makefile.am
@@ -80,6 +80,7 @@ gdk_public_h_sources = \
gdkkeysyms-compat.h \
gdkmain.h \
gdkpango.h \
+ gdkframeclock.h \
gdkpixbuf.h \
gdkprivate.h \
gdkproperty.h \
@@ -101,6 +102,7 @@ gdk_private_headers = \
gdkdisplaymanagerprivate.h \
gdkdisplayprivate.h \
gdkdndprivate.h \
+ gdkframeclockidle.h \
gdkscreenprivate.h \
gdkinternals.h \
gdkintl.h \
@@ -125,6 +127,8 @@ gdk_c_sources = \
gdkkeys.c \
gdkkeyuni.c \
gdkoffscreenwindow.c \
+ gdkframeclock.c \
+ gdkframeclockidle.c \
gdkpango.c \
gdkpixbuf-drawable.c \
gdkrectangle.c \
diff --git a/gdk/gdkframeclock.c b/gdk/gdkframeclock.c
new file mode 100644
index 0000000..6b23ce6
--- /dev/null
+++ b/gdk/gdkframeclock.c
@@ -0,0 +1,287 @@
+/* GDK - The GIMP Drawing Kit
+ * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
+ *
+ * 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, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/*
+ * Modified by the GTK+ Team and others 1997-2010. See the AUTHORS
+ * file for a list of people on the GTK+ Team. See the ChangeLog
+ * files for a list of changes. These files are distributed with
+ * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
+ */
+
+#include "config.h"
+
+#include "gdkframeclock.h"
+
+/**
+ * SECTION:frameclock
+ * @Short_description: Frame clock syncs painting to a window or display
+ * @Title: Frame clock
+ *
+ * A #GdkFrameClock tells the application when to repaint a window.
+ * This may be synced to the vertical refresh rate of the monitor, for
+ * example. Even when the frame clock uses a simple timer rather than
+ * a hardware-based vertical sync, the frame clock helps because it
+ * ensures everything paints at the same time (reducing the total
+ * number of frames). The frame clock can also automatically stop
+ * painting when it knows the frames will not be visible, or scale back
+ * animation framerates.
+ *
+ * #GdkFrameClock is designed to be compatible with an OpenGL-based
+ * implementation or with mozRequestAnimationFrame in Firefox,
+ * for example.
+ *
+ * A frame clock is idle until someone requests a frame with
+ * gdk_frame_clock_request_frame(). At that time, the frame clock
+ * emits its GdkFrameClock:frame-requested signal if no frame was
+ * already pending.
+ *
+ * At some later time after the frame is requested, the frame clock
+ * MAY indicate that a frame should be painted. To paint a frame the
+ * clock will: Emit GdkFrameClock:before-paint; update the frame time
+ * in the default handler for GdkFrameClock:before-paint; emit
+ * GdkFrameClock:paint; emit GdkFrameClock:after-paint. The app
+ * should paint in a handler for the paint signal.
+ *
+ * If a given frame is not painted (the clock is idle), the frame time
+ * should still update to a conceptual "last frame." i.e. the frame
+ * time will keep moving forward roughly with wall clock time.
+ *
+ * The frame time is in milliseconds. However, it should not be
+ * thought of as having any particular relationship to wall clock
+ * time. Unlike wall clock time, it "snaps" to conceptual frame times
+ * so is low-resolution; it is guaranteed to never move backward (so
+ * say you reset your computer clock, the frame clock will not reset);
+ * and the frame clock is allowed to drift. For example nicer
+ * results when painting with vertical refresh sync may be obtained by
+ * painting as rapidly as possible, but always incrementing the frame
+ * time by the frame length on each frame. This results in a frame
+ * time that doesn't have a lot to do with wall clock time.
+ */
+
+G_DEFINE_INTERFACE (GdkFrameClock, gdk_frame_clock, G_TYPE_OBJECT)
+
+enum {
+ FRAME_REQUESTED,
+ BEFORE_PAINT,
+ PAINT,
+ AFTER_PAINT,
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL];
+
+static void
+gdk_frame_clock_default_init (GdkFrameClockInterface *iface)
+{
+ /**
+ * GdkFrameClock::frame-requested:
+ * @clock: the frame clock emitting the signal
+ *
+ * This signal is emitted when a frame is not pending, and
+ * gdk_frame_clock_request_frame() is called to request a frame.
+ */
+ signals[FRAME_REQUESTED] =
+ g_signal_new (g_intern_static_string ("frame-requested"),
+ GDK_TYPE_FRAME_CLOCK,
+ G_SIGNAL_RUN_LAST,
+ 0,
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ /**
+ * GdkFrameClock::before-paint:
+ * @clock: the frame clock emitting the signal
+ *
+ * This signal is emitted immediately before the paint signal and
+ * indicates that the frame time has been updated, and signal
+ * handlers should perform any preparatory work before painting.
+ */
+ signals[BEFORE_PAINT] =
+ g_signal_new (g_intern_static_string ("before-paint"),
+ GDK_TYPE_FRAME_CLOCK,
+ G_SIGNAL_RUN_LAST,
+ 0,
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ /**
+ * GdkFrameClock::paint:
+ * @clock: the frame clock emitting the signal
+ *
+ * Signal handlers for this signal should paint the window, screen,
+ * or whatever they normally paint.
+ */
+ signals[PAINT] =
+ g_signal_new (g_intern_static_string ("paint"),
+ GDK_TYPE_FRAME_CLOCK,
+ G_SIGNAL_RUN_LAST,
+ 0,
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ /**
+ * GdkFrameClock::after-paint:
+ * @clock: the frame clock emitting the signal
+ *
+ * This signal is emitted immediately after the paint signal and
+ * allows signal handlers to do anything they'd like to do after
+ * painting has been completed. This is a relatively good time to do
+ * "expensive" processing in order to get it done in between frames.
+ */
+ signals[AFTER_PAINT] =
+ g_signal_new (g_intern_static_string ("after-paint"),
+ GDK_TYPE_FRAME_CLOCK,
+ G_SIGNAL_RUN_LAST,
+ 0,
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+}
+
+/**
+ * gdk_frame_clock_get_frame_time:
+ * @clock: the clock
+ *
+ * Gets the time that should currently be used for animations. Inside
+ * a paint, it's the time used to compute the animation position of
+ * everything in a frame. Outside a paint, it's the time of the
+ * conceptual "previous frame," which may be either the actual
+ * previous frame time, or if that's too old, an updated time.
+ *
+ * The returned time has no relationship to wall clock time. It
+ * increases roughly at 1 millisecond per wall clock millisecond, and
+ * it never decreases, but its value is only meaningful relative to
+ * previous frame clock times.
+ *
+ *
+ * Since: 3.0
+ * Return value: a timestamp in milliseconds
+ */
+guint64
+gdk_frame_clock_get_frame_time (GdkFrameClock *clock)
+{
+ g_return_val_if_fail (GDK_IS_FRAME_CLOCK (clock), 0);
+
+ return GDK_FRAME_CLOCK_GET_IFACE (clock)->get_frame_time (clock);
+}
+
+/**
+ * gdk_frame_clock_request_frame:
+ * @clock: the clock
+ *
+ * Asks the frame clock to paint a frame. The frame
+ * may or may not ever be painted (the frame clock may
+ * stop itself for whatever reason), but the goal in
+ * normal circumstances would be to paint the frame
+ * at the next expected frame time. For example
+ * if the clock is running at 60fps the frame would
+ * ideally be painted within 1000/60=16 milliseconds.
+ *
+ * Since: 3.0
+ */
+void
+gdk_frame_clock_request_frame (GdkFrameClock *clock)
+{
+ g_return_if_fail (GDK_IS_FRAME_CLOCK (clock));
+
+ GDK_FRAME_CLOCK_GET_IFACE (clock)->request_frame (clock);
+}
+
+/**
+ * gdk_frame_clock_get_frame_requested:
+ * @clock: the clock
+ *
+ * Gets whether a frame paint has been requested but has not been
+ * performed.
+ *
+ *
+ * Since: 3.0
+ * Return value: TRUE if a frame paint is pending
+ */
+gboolean
+gdk_frame_clock_get_frame_requested (GdkFrameClock *clock)
+{
+ g_return_val_if_fail (GDK_IS_FRAME_CLOCK (clock), FALSE);
+
+ return GDK_FRAME_CLOCK_GET_IFACE (clock)->get_frame_requested (clock);
+}
+
+/**
+ * gdk_frame_clock_get_frame_time_val:
+ * @clock: the clock
+ * @timeval: #GTimeVal to fill in with frame time
+ *
+ * Like gdk_frame_clock_get_frame_time() but returns the time as a
+ * #GTimeVal which may be handy with some APIs (such as
+ * #GdkPixbufAnimation).
+ */
+void
+gdk_frame_clock_get_frame_time_val (GdkFrameClock *clock,
+ GTimeVal *timeval)
+{
+ guint64 time_ms;
+
+ g_return_if_fail (GDK_IS_FRAME_CLOCK (clock));
+
+ time_ms = gdk_frame_clock_get_frame_time (clock);
+
+ timeval->tv_sec = time_ms / 1000;
+ timeval->tv_usec = (time_ms % 1000) * 1000;
+}
+
+/**
+ * gdk_frame_clock_frame_requested:
+ * @clock: the clock
+ *
+ * Emits the frame-requested signal. Used in implementations of the
+ * #GdkFrameClock interface.
+ */
+void
+gdk_frame_clock_frame_requested (GdkFrameClock *clock)
+{
+ g_return_if_fail (GDK_IS_FRAME_CLOCK (clock));
+
+ g_signal_emit (G_OBJECT (clock),
+ signals[FRAME_REQUESTED], 0);
+}
+
+/**
+ * gdk_frame_clock_paint:
+ * @clock: the clock
+ *
+ * Emits the before-paint, paint, and after-paint signals. Used in
+ * implementations of the #GdkFrameClock interface.
+ */
+void
+gdk_frame_clock_paint (GdkFrameClock *clock)
+{
+ g_return_if_fail (GDK_IS_FRAME_CLOCK (clock));
+
+ g_signal_emit (G_OBJECT (clock),
+ signals[BEFORE_PAINT], 0);
+
+ g_signal_emit (G_OBJECT (clock),
+ signals[PAINT], 0);
+
+ g_signal_emit (G_OBJECT (clock),
+ signals[AFTER_PAINT], 0);
+}
diff --git a/gdk/gdkframeclock.h b/gdk/gdkframeclock.h
new file mode 100644
index 0000000..62124e2
--- /dev/null
+++ b/gdk/gdkframeclock.h
@@ -0,0 +1,77 @@
+/* GDK - The GIMP Drawing Kit
+ * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
+ *
+ * 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, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/*
+ * Modified by the GTK+ Team and others 1997-2010. See the AUTHORS
+ * file for a list of people on the GTK+ Team. See the ChangeLog
+ * files for a list of changes. These files are distributed with
+ * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
+ */
+
+#if !defined (__GDK_H_INSIDE__) && !defined (GDK_COMPILATION)
+#error "Only <gdk/gdk.h> can be included directly."
+#endif
+
+#ifndef __GDK_FRAME_CLOCK_H__
+#define __GDK_FRAME_CLOCK_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define GDK_TYPE_FRAME_CLOCK (gdk_frame_clock_get_type ())
+#define GDK_FRAME_CLOCK(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GDK_TYPE_FRAME_CLOCK, GdkFrameClock))
+#define GDK_IS_FRAME_CLOCK(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GDK_TYPE_FRAME_CLOCK))
+#define GDK_FRAME_CLOCK_GET_IFACE(inst) (G_TYPE_INSTANCE_GET_INTERFACE ((inst), GDK_TYPE_FRAME_CLOCK, GdkFrameClockInterface))
+
+typedef struct _GdkFrameClock GdkFrameClock;
+typedef struct _GdkFrameClockInterface GdkFrameClockInterface;
+
+struct _GdkFrameClockInterface
+{
+ GTypeInterface base_iface;
+
+ guint64 (* get_frame_time) (GdkFrameClock *clock);
+ void (* request_frame) (GdkFrameClock *clock);
+ gboolean (* get_frame_requested) (GdkFrameClock *clock);
+
+ /* signals */
+ /* void (* frame_requested) (GdkFrameClock *clock); */
+ /* void (* before_paint) (GdkFrameClock *clock); */
+ /* void (* paint) (GdkFrameClock *clock); */
+ /* void (* after_paint) (GdkFrameClock *clock); */
+};
+
+GType gdk_frame_clock_get_type (void) G_GNUC_CONST;
+
+guint64 gdk_frame_clock_get_frame_time (GdkFrameClock *clock);
+void gdk_frame_clock_request_frame (GdkFrameClock *clock);
+gboolean gdk_frame_clock_get_frame_requested (GdkFrameClock *clock);
+
+/* Convenience API */
+void gdk_frame_clock_get_frame_time_val (GdkFrameClock *clock,
+ GTimeVal *timeval);
+
+/* Signal emitters (used in frame clock implementations) */
+void gdk_frame_clock_frame_requested (GdkFrameClock *clock);
+void gdk_frame_clock_paint (GdkFrameClock *clock);
+
+G_END_DECLS
+
+#endif /* __GDK_FRAME_CLOCK_H__ */
diff --git a/gdk/gdkframeclockidle.c b/gdk/gdkframeclockidle.c
new file mode 100644
index 0000000..ff20632
--- /dev/null
+++ b/gdk/gdkframeclockidle.c
@@ -0,0 +1,182 @@
+/* GDK - The GIMP Drawing Kit
+ * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
+ *
+ * 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, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/*
+ * Modified by the GTK+ Team and others 1997-2010. See the AUTHORS
+ * file for a list of people on the GTK+ Team. See the ChangeLog
+ * files for a list of changes. These files are distributed with
+ * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
+ */
+
+#include "config.h"
+
+#include "gdkframeclockidle.h"
+#include "gdk.h"
+
+struct _GdkFrameClockIdlePrivate
+{
+ GTimer *timer;
+ /* timer_base is used to avoid ever going backward */
+ guint64 timer_base;
+ guint64 frame_time;
+
+ guint idle_id;
+
+ unsigned int in_paint : 1;
+};
+
+static void gdk_frame_clock_idle_finalize (GObject *object);
+static void gdk_frame_clock_idle_interface_init (GdkFrameClockInterface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (GdkFrameClockIdle, gdk_frame_clock_idle, G_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE (GDK_TYPE_FRAME_CLOCK,
+ gdk_frame_clock_idle_interface_init))
+
+static void
+gdk_frame_clock_idle_class_init (GdkFrameClockIdleClass *klass)
+{
+ GObjectClass *gobject_class = (GObjectClass*) klass;
+
+ gobject_class->finalize = gdk_frame_clock_idle_finalize;
+
+ g_type_class_add_private (klass, sizeof (GdkFrameClockIdlePrivate));
+}
+
+static void
+gdk_frame_clock_idle_init (GdkFrameClockIdle *frame_clock_idle)
+{
+ GdkFrameClockIdlePrivate *priv;
+
+ frame_clock_idle->priv = G_TYPE_INSTANCE_GET_PRIVATE (frame_clock_idle,
+ GDK_TYPE_FRAME_CLOCK_IDLE,
+ GdkFrameClockIdlePrivate);
+ priv = frame_clock_idle->priv;
+
+ priv->timer = g_timer_new ();
+}
+
+static void
+gdk_frame_clock_idle_finalize (GObject *object)
+{
+ GdkFrameClockIdlePrivate *priv = GDK_FRAME_CLOCK_IDLE (object)->priv;
+
+ g_timer_destroy (priv->timer);
+
+ G_OBJECT_CLASS (gdk_frame_clock_idle_parent_class)->finalize (object);
+}
+
+static guint64
+compute_frame_time (GdkFrameClockIdle *idle)
+{
+ GdkFrameClockIdlePrivate *priv = idle->priv;
+ guint64 computed_frame_time;
+ guint64 elapsed;
+
+ elapsed = ((guint64) (g_timer_elapsed (priv->timer, NULL) * 1000)) + priv->timer_base;
+ if (elapsed < priv->frame_time)
+ {
+ /* clock went backward. adapt to that by forevermore increasing
+ * timer_base. For now, assume we've gone forward in time 1ms.
+ */
+ /* hmm. just fix GTimer? */
+ computed_frame_time = priv->frame_time + 1;
+ priv->timer_base += (priv->frame_time - elapsed) + 1;
+ }
+ else
+ {
+ computed_frame_time = elapsed;
+ }
+
+ return computed_frame_time;
+}
+
+static guint64
+gdk_frame_clock_idle_get_frame_time (GdkFrameClock *clock)
+{
+ GdkFrameClockIdlePrivate *priv = GDK_FRAME_CLOCK_IDLE (clock)->priv;
+ guint64 computed_frame_time;
+
+ /* can't change frame time during a paint */
+ if (priv->in_paint)
+ return priv->frame_time;
+
+ /* Outside a paint, pick something close to "now" */
+ computed_frame_time = compute_frame_time (GDK_FRAME_CLOCK_IDLE (clock));
+
+ /* 16ms is 60fps. We only update frame time that often because we'd
+ * like to try to keep animations on the same start times.
+ * get_frame_time() would normally be used outside of a paint to
+ * record an animation start time for example.
+ */
+ if ((computed_frame_time - priv->frame_time) > 16)
+ priv->frame_time = computed_frame_time;
+
+ return priv->frame_time;
+}
+
+static gboolean
+gdk_frame_clock_paint_idle (void *data)
+{
+ GdkFrameClock *clock = GDK_FRAME_CLOCK (data);
+ GdkFrameClockIdle *clock_idle = GDK_FRAME_CLOCK_IDLE (clock);
+ GdkFrameClockIdlePrivate *priv = clock_idle->priv;
+
+ priv->idle_id = 0;
+
+ priv->in_paint = TRUE;
+ priv->frame_time = compute_frame_time (clock_idle);
+
+ gdk_frame_clock_paint (clock);
+
+ priv->in_paint = FALSE;
+
+ return FALSE;
+}
+
+static void
+gdk_frame_clock_idle_request_frame (GdkFrameClock *clock)
+{
+ GdkFrameClockIdlePrivate *priv = GDK_FRAME_CLOCK_IDLE (clock)->priv;
+
+ if (priv->idle_id == 0)
+ {
+ priv->idle_id = gdk_threads_add_idle_full (GDK_PRIORITY_REDRAW,
+ gdk_frame_clock_paint_idle,
+ g_object_ref (clock),
+ (GDestroyNotify) g_object_unref);
+
+ gdk_frame_clock_frame_requested (clock);
+ }
+}
+
+static gboolean
+gdk_frame_clock_idle_get_frame_requested (GdkFrameClock *clock)
+{
+ GdkFrameClockIdlePrivate *priv = GDK_FRAME_CLOCK_IDLE (clock)->priv;
+
+ return priv->idle_id != 0;
+}
+
+static void
+gdk_frame_clock_idle_interface_init (GdkFrameClockInterface *iface)
+{
+ iface->get_frame_time = gdk_frame_clock_idle_get_frame_time;
+ iface->request_frame = gdk_frame_clock_idle_request_frame;
+ iface->get_frame_requested = gdk_frame_clock_idle_get_frame_requested;
+}
diff --git a/gdk/gdkframeclockidle.h b/gdk/gdkframeclockidle.h
new file mode 100644
index 0000000..f4815a9
--- /dev/null
+++ b/gdk/gdkframeclockidle.h
@@ -0,0 +1,68 @@
+/* GDK - The GIMP Drawing Kit
+ * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
+ *
+ * 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, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/*
+ * Modified by the GTK+ Team and others 1997-2010. See the AUTHORS
+ * file for a list of people on the GTK+ Team. See the ChangeLog
+ * files for a list of changes. These files are distributed with
+ * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
+ */
+
+/* Uninstalled header, internal to GDK */
+
+#ifndef __GDK_FRAME_CLOCK_IDLE_H__
+#define __GDK_FRAME_CLOCK_IDLE_H__
+
+#include <gdk/gdkframeclock.h>
+
+G_BEGIN_DECLS
+
+#define GDK_TYPE_FRAME_CLOCK_IDLE (gdk_frame_clock_idle_get_type ())
+#define GDK_FRAME_CLOCK_IDLE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GDK_TYPE_FRAME_CLOCK_IDLE, GdkFrameClockIdle))
+#define GDK_FRAME_CLOCK_IDLE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDK_TYPE_FRAME_CLOCK_IDLE, GdkFrameClockIdleClass))
+#define GDK_IS_FRAME_CLOCK_IDLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GDK_TYPE_FRAME_CLOCK_IDLE))
+#define GDK_IS_FRAME_CLOCK_IDLE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDK_TYPE_FRAME_CLOCK_IDLE))
+#define GDK_FRAME_CLOCK_IDLE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GDK_TYPE_FRAME_CLOCK_IDLE, GdkFrameClockIdleClass))
+
+
+typedef struct _GdkFrameClockIdle GdkFrameClockIdle;
+typedef struct _GdkFrameClockIdlePrivate GdkFrameClockIdlePrivate;
+typedef struct _GdkFrameClockIdleClass GdkFrameClockIdleClass;
+
+struct _GdkFrameClockIdle
+{
+ GObject parent_instance;
+
+ /*< private >*/
+ GdkFrameClockIdlePrivate *priv;
+};
+
+struct _GdkFrameClockIdleClass
+{
+ GObjectClass parent_class;
+};
+
+GType gdk_frame_clock_idle_get_type (void) G_GNUC_CONST;
+
+void _gdk_frame_clock_idle_freeze_updates (GdkFrameClockIdle *clock_idle);
+void _gdk_frame_clock_idle_thaw_updates (GdkFrameClockIdle *clock_idle);
+
+G_END_DECLS
+
+#endif /* __GDK_FRAME_CLOCK_IDLE_H__ */
diff --git a/gdk/gdkinternals.h b/gdk/gdkinternals.h
index d150414..fb561e6 100644
--- a/gdk/gdkinternals.h
+++ b/gdk/gdkinternals.h
@@ -265,6 +265,8 @@ struct _GdkWindow
gulong device_changed_handler_id;
guint num_offscreen_children;
+
+ GdkFrameClock *frame_clock; /* NULL to use from parent or default */
};
#define GDK_WINDOW_TYPE(d) (((GDK_WINDOW (d)))->window_type)
diff --git a/gdk/gdkwindow.c b/gdk/gdkwindow.c
index 0bc984d..f82ad16 100644
--- a/gdk/gdkwindow.c
+++ b/gdk/gdkwindow.c
@@ -37,6 +37,7 @@
#include "gdkdeviceprivate.h"
#include "gdkvisualprivate.h"
#include "gdkmarshalers.h"
+#include "gdkframeclockidle.h"
#include "gdkwindowimpl.h"
#include <math.h>
@@ -169,7 +170,8 @@ enum {
enum {
PROP_0,
- PROP_CURSOR
+ PROP_CURSOR,
+ PROP_FRAME_CLOCK
};
typedef enum {
@@ -238,6 +240,10 @@ static void gdk_window_invalidate_rect_full (GdkWindow *window,
static void _gdk_window_propagate_has_alpha_background (GdkWindow *window);
static cairo_surface_t *gdk_window_ref_impl_surface (GdkWindow *window);
+static void gdk_window_process_all_updates_internal (gboolean default_clock_only);
+
+static void gdk_ensure_default_frame_clock (void);
+
static guint signals[LAST_SIGNAL] = { 0 };
static gpointer parent_class = NULL;
@@ -389,6 +395,23 @@ gdk_window_class_init (GdkWindowClass *klass)
G_PARAM_READWRITE));
/**
+ * GdkWindow:paint-clock:
+ *
+ * The frame clock for a #GdkWindow, see #GdkFrameClock
+ *
+ * The frame clock remains the same for the lifetime of the window.
+ *
+ * Since: 3.0
+ */
+ g_object_class_install_property (object_class,
+ PROP_FRAME_CLOCK,
+ g_param_spec_object ("paint-clock",
+ P_("Frame clock"),
+ P_("Frame clock"),
+ GDK_TYPE_FRAME_CLOCK,
+ G_PARAM_READWRITE));
+
+ /**
* GdkWindow::pick-embedded-child:
* @window: the window on which the signal is emitted
* @x: x coordinate in the window
@@ -600,6 +623,10 @@ gdk_window_set_property (GObject *object,
gdk_window_set_cursor (window, g_value_get_object (value));
break;
+ case PROP_FRAME_CLOCK:
+ gdk_window_set_frame_clock (window, g_value_get_object (value));
+ break;
+
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -620,6 +647,10 @@ gdk_window_get_property (GObject *object,
g_value_set_object (value, gdk_window_get_cursor (window));
break;
+ case PROP_FRAME_CLOCK:
+ g_value_set_object (value, gdk_window_get_frame_clock (window));
+ break;
+
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -3778,7 +3809,7 @@ gdk_cairo_create (GdkWindow *window)
/* Code for dirty-region queueing
*/
static GSList *update_windows = NULL;
-static guint update_idle = 0;
+static GdkFrameClock *_gdk_default_frame_clock = NULL;
static gboolean debug_updates = FALSE;
static inline gboolean
@@ -3877,12 +3908,25 @@ gdk_window_remove_update_window (GdkWindow *window)
update_windows = g_slist_remove (update_windows, window);
}
-static gboolean
-gdk_window_update_idle (gpointer data)
+static void
+gdk_window_paint_default_clock_updates (gpointer data)
{
- gdk_window_process_all_updates ();
+ gdk_window_process_all_updates_internal (TRUE);
+}
- return FALSE;
+static void
+gdk_ensure_default_frame_clock (void)
+{
+ if (_gdk_default_frame_clock == NULL)
+ {
+ _gdk_default_frame_clock = g_object_new (GDK_TYPE_FRAME_CLOCK_IDLE,
+ NULL);
+
+ g_signal_connect (G_OBJECT (_gdk_default_frame_clock),
+ "paint",
+ G_CALLBACK (gdk_window_paint_default_clock_updates),
+ NULL);
+ }
}
static gboolean
@@ -3903,11 +3947,7 @@ gdk_window_schedule_update (GdkWindow *window)
gdk_window_is_toplevel_frozen (window)))
return;
- if (!update_idle)
- update_idle =
- gdk_threads_add_idle_full (GDK_PRIORITY_REDRAW,
- gdk_window_update_idle,
- NULL, NULL);
+ gdk_frame_clock_request_frame (gdk_window_get_frame_clock (window));
}
void
@@ -4220,6 +4260,19 @@ after_process_all_updates (void)
g_slist_free (displays);
}
+/**
+ * gdk_window_process_all_updates:
+ *
+ * Calls gdk_window_process_updates() for all windows (see #GdkWindow)
+ * in the application.
+ *
+ **/
+void
+gdk_window_process_all_updates (void)
+{
+ gdk_window_process_all_updates_internal (FALSE);
+}
+
/* Currently it is not possible to override
* gdk_window_process_all_updates in the same manner as
* gdk_window_process_updates and gdk_window_invalidate_maybe_recurse
@@ -4230,15 +4283,8 @@ after_process_all_updates (void)
* displays and call the mehod.
*/
-/**
- * gdk_window_process_all_updates:
- *
- * Calls gdk_window_process_updates() for all windows (see #GdkWindow)
- * in the application.
- *
- **/
-void
-gdk_window_process_all_updates (void)
+static void
+gdk_window_process_all_updates_internal (gboolean default_clock_only)
{
GSList *old_update_windows = update_windows;
GSList *tmp_list = update_windows;
@@ -4250,18 +4296,13 @@ gdk_window_process_all_updates (void)
/* We can't do this now since that would recurse, so
delay it until after the recursion is done. */
got_recursive_update = TRUE;
- update_idle = 0;
return;
}
in_process_all_updates = TRUE;
got_recursive_update = FALSE;
- if (update_idle)
- g_source_remove (update_idle);
-
update_windows = NULL;
- update_idle = 0;
before_process_all_updates ();
@@ -4274,7 +4315,8 @@ gdk_window_process_all_updates (void)
if (!GDK_WINDOW_DESTROYED (window))
{
if (window->update_freeze_count ||
- gdk_window_is_toplevel_frozen (window))
+ gdk_window_is_toplevel_frozen (window) ||
+ (default_clock_only && window->frame_clock != NULL))
gdk_window_add_update_window (window);
else
gdk_window_process_updates_internal (window);
@@ -4296,31 +4338,20 @@ gdk_window_process_all_updates (void)
redraw now so that it eventually happens,
otherwise we could miss an update if nothing
else schedules an update. */
- if (got_recursive_update && !update_idle)
- update_idle =
- gdk_threads_add_idle_full (GDK_PRIORITY_REDRAW,
- gdk_window_update_idle,
- NULL, NULL);
+ if (got_recursive_update)
+ gdk_window_schedule_update (NULL);
}
-/**
- * gdk_window_process_updates:
- * @window: a #GdkWindow
- * @update_children: whether to also process updates for child windows
- *
- * Sends one or more expose events to @window. The areas in each
- * expose event will cover the entire update area for the window (see
- * gdk_window_invalidate_region() for details). Normally GDK calls
- * gdk_window_process_all_updates() on your behalf, so there's no
- * need to call this function unless you want to force expose events
- * to be delivered immediately and synchronously (vs. the usual
- * case, where GDK delivers them in an idle handler). Occasionally
- * this is useful to produce nicer scrolling behavior, for example.
- *
- **/
-void
-gdk_window_process_updates (GdkWindow *window,
- gboolean update_children)
+
+enum {
+ PROCESS_UPDATES_NO_RECURSE,
+ PROCESS_UPDATES_WITH_ALL_CHILDREN,
+ PROCESS_UPDATES_WITH_SAME_CLOCK_CHILDREN
+};
+
+static void
+gdk_window_process_updates_with_mode (GdkWindow *window,
+ int recurse_mode)
{
GdkWindow *impl_window;
@@ -4346,7 +4377,7 @@ gdk_window_process_updates (GdkWindow *window,
gdk_window_remove_update_window ((GdkWindow *)impl_window);
}
- if (update_children)
+ if (recurse_mode != PROCESS_UPDATES_NO_RECURSE)
{
/* process updates in reverse stacking order so composition or
* painting over achieves the desired effect for offscreen windows
@@ -4358,8 +4389,14 @@ gdk_window_process_updates (GdkWindow *window,
for (node = g_list_last (children); node; node = node->prev)
{
- gdk_window_process_updates (node->data, TRUE);
- g_object_unref (node->data);
+ GdkWindow *child = node->data;
+ if (recurse_mode == PROCESS_UPDATES_WITH_ALL_CHILDREN ||
+ (recurse_mode == PROCESS_UPDATES_WITH_SAME_CLOCK_CHILDREN &&
+ child->frame_clock == NULL))
+ {
+ gdk_window_process_updates (child, TRUE);
+ }
+ g_object_unref (child);
}
g_list_free (children);
@@ -4368,6 +4405,33 @@ gdk_window_process_updates (GdkWindow *window,
g_object_unref (window);
}
+/**
+ * gdk_window_process_updates:
+ * @window: a #GdkWindow
+ * @update_children: whether to also process updates for child windows
+ *
+ * Sends one or more expose events to @window. The areas in each
+ * expose event will cover the entire update area for the window (see
+ * gdk_window_invalidate_region() for details). Normally GDK calls
+ * gdk_window_process_all_updates() on your behalf, so there's no
+ * need to call this function unless you want to force expose events
+ * to be delivered immediately and synchronously (vs. the usual
+ * case, where GDK delivers them in an idle handler). Occasionally
+ * this is useful to produce nicer scrolling behavior, for example.
+ *
+ **/
+void
+gdk_window_process_updates (GdkWindow *window,
+ gboolean update_children)
+{
+ g_return_if_fail (GDK_IS_WINDOW (window));
+
+ return gdk_window_process_updates_with_mode (window,
+ update_children ?
+ PROCESS_UPDATES_WITH_ALL_CHILDREN :
+ PROCESS_UPDATES_NO_RECURSE);
+}
+
static void
gdk_window_invalidate_rect_full (GdkWindow *window,
const GdkRectangle *rect,
@@ -11535,3 +11599,113 @@ gdk_property_delete (GdkWindow *window,
{
GDK_WINDOW_IMPL_GET_CLASS (window->impl)->delete_property (window, property);
}
+
+static void
+gdk_window_paint_on_clock (GdkFrameClock *clock,
+ void *data)
+{
+ GdkWindow *window;
+
+ window = GDK_WINDOW (data);
+
+ /* Update window and any children on the same clock.
+ */
+ gdk_window_process_updates_with_mode (window, PROCESS_UPDATES_WITH_SAME_CLOCK_CHILDREN);
+}
+
+/**
+ * gdk_window_set_frame_clock:
+ * @window: window to set frame clock on
+ * @clock: the clock
+ *
+ * Sets the frame clock for the window. The frame clock for a window
+ * cannot be changed while the window is mapped. Set the frame
+ * clock to #NULL to use the default frame clock. (By default the
+ * frame clock comes from the window's parent or is a global default
+ * frame clock.)
+ *
+ * Since: 3.0
+ */
+void
+gdk_window_set_frame_clock (GdkWindow *window,
+ GdkFrameClock *clock)
+{
+ g_return_if_fail (GDK_IS_WINDOW (window));
+ g_return_if_fail (clock == NULL || GDK_IS_FRAME_CLOCK (clock));
+ g_return_if_fail (!GDK_WINDOW_IS_MAPPED (window));
+
+ if (clock == window->frame_clock)
+ return;
+
+ /* If we are using our parent's clock, then the parent will repaint
+ * us when that clock fires. If we are using the default clock, then
+ * it does a gdk_window_process_all_updates() which will repaint us
+ * when the clock fires. If we are using our own clock, then we have
+ * to connect to "paint" on it ourselves and paint ourselves and
+ * any child windows.
+ */
+
+ if (clock)
+ {
+ g_object_ref (clock);
+ g_signal_connect (G_OBJECT (clock),
+ "paint",
+ G_CALLBACK (gdk_window_paint_on_clock),
+ window);
+ }
+
+ if (window->frame_clock)
+ {
+ g_signal_handlers_disconnect_by_func (G_OBJECT (window->frame_clock),
+ G_CALLBACK (gdk_window_paint_on_clock),
+ window);
+ g_object_unref (window->frame_clock);
+ }
+
+ window->frame_clock = clock;
+ g_object_notify (G_OBJECT (window), "paint-clock");
+
+ /* We probably should recurse child windows and emit notify on their
+ * paint-clock properties also, and we should emit notify when a
+ * window is first parented.
+ */
+}
+
+/**
+ * gdk_window_get_frame_clock:
+ * @window: window to get frame clock for
+ *
+ * Gets the frame clock for the window. The frame clock for a window
+ * never changes while the window is mapped. It may be changed at
+ * other times.
+ *
+ * Since: 3.0
+ * Return value: (transfer none): the frame clock
+ */
+GdkFrameClock*
+gdk_window_get_frame_clock (GdkWindow *window)
+{
+ g_return_val_if_fail (GDK_IS_WINDOW (window), NULL);
+
+ if (window->frame_clock != NULL)
+ {
+ /* Frame clock set explicitly on this window */
+ return window->frame_clock;
+ }
+ else
+ {
+ GdkWindow *parent;
+
+ /* parent's frame clock or default */
+ parent = gdk_window_get_effective_parent (window);
+ if (parent != NULL)
+ {
+ return gdk_window_get_frame_clock (parent);
+ }
+ else
+ {
+ gdk_ensure_default_frame_clock ();
+ return _gdk_default_frame_clock;
+ }
+ }
+}
diff --git a/gdk/gdkwindow.h b/gdk/gdkwindow.h
index a4fbac0..a93e5ce 100644
--- a/gdk/gdkwindow.h
+++ b/gdk/gdkwindow.h
@@ -32,6 +32,7 @@
#include <gdk/gdkversionmacros.h>
#include <gdk/gdktypes.h>
#include <gdk/gdkevents.h>
+#include <gdk/gdkframeclock.h>
G_BEGIN_DECLS
@@ -901,6 +902,11 @@ void gdk_window_set_support_multidevice (GdkWindow *window,
gboolean support_multidevice);
gboolean gdk_window_get_support_multidevice (GdkWindow *window);
+/* Frame clock */
+void gdk_window_set_frame_clock (GdkWindow *window,
+ GdkFrameClock *clock);
+GdkFrameClock* gdk_window_get_frame_clock (GdkWindow *window);
+
G_END_DECLS
#endif /* __GDK_WINDOW_H__ */
diff --git a/gtk/gtkwidget.c b/gtk/gtkwidget.c
index d161ee9..a3509c4 100644
--- a/gtk/gtkwidget.c
+++ b/gtk/gtkwidget.c
@@ -4778,6 +4778,54 @@ gtk_widget_queue_resize_no_redraw (GtkWidget *widget)
}
/**
+ * gtk_widget_get_frame_clock:
+ * @widget: a #GtkWidget
+ *
+ * Obtains the frame clock for a widget. The frame clock is a global
+ * "ticker" that can be used to drive animations and repaints. The
+ * most common reason to get the frame clock is to call
+ * gdk_frame_clock_get_frame_time(), in order to get a time to use for
+ * animating. For example you might record the start of the animation
+ * with an initial value from gdk_frame_clock_get_frame_time(), and
+ * then update the animation by calling
+ * gdk_frame_clock_get_frame_time() again during each repaint.
+ *
+ * gdk_frame_clock_request_frame() will result in a new frame on the
+ * clock, but won't necessarily repaint any widgets. To repaint a
+ * widget, you have to use gtk_widget_queue_draw() which invalidates
+ * the widget (thus scheduling it to receive a draw on the next
+ * frame). gtk_widget_queue_draw() will also end up requesting a frame
+ * on the appropriate frame clock.
+ *
+ * A widget's frame clock will not change while the widget is
+ * mapped. Reparenting a widget (which implies a temporary unmap) can
+ * change the widget's frame clock.
+ *
+ * Unrealized widgets do not have a frame clock.
+ *
+ * Since: 3.0
+ * Return value: a #GdkFrameClock (or #NULL if widget is unrealized)
+ */
+GdkFrameClock*
+gtk_widget_get_frame_clock (GtkWidget *widget)
+{
+ g_return_val_if_fail (GTK_IS_WIDGET (widget), 0);
+
+ if (widget->priv->realized)
+ {
+ GdkWindow *window;
+
+ window = gtk_widget_get_window (widget);
+ g_assert (window != NULL);
+ return gdk_window_get_frame_clock (window);
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+/**
* gtk_widget_size_request:
* @widget: a #GtkWidget
* @requisition: (out): a #GtkRequisition to be filled in
diff --git a/gtk/gtkwidget.h b/gtk/gtkwidget.h
index a4c20e8..a8572e6 100644
--- a/gtk/gtkwidget.h
+++ b/gtk/gtkwidget.h
@@ -473,6 +473,8 @@ void gtk_widget_queue_draw_region (GtkWidget *widget,
const cairo_region_t*region);
void gtk_widget_queue_resize (GtkWidget *widget);
void gtk_widget_queue_resize_no_redraw (GtkWidget *widget);
+GdkFrameClock* gtk_widget_get_frame_clock (GtkWidget *widget);
+
GDK_DEPRECATED_IN_3_0_FOR(gtk_widget_get_preferred_size)
void gtk_widget_size_request (GtkWidget *widget,
GtkRequisition *requisition);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]