diff options
-rw-r--r-- | gdk/Makefile.am | 4 | ||||
-rw-r--r-- | gdk/gdkframeclock.c | 287 | ||||
-rw-r--r-- | gdk/gdkframeclock.h | 77 | ||||
-rw-r--r-- | gdk/gdkframeclockidle.c | 182 | ||||
-rw-r--r-- | gdk/gdkframeclockidle.h | 68 | ||||
-rw-r--r-- | gdk/gdkinternals.h | 2 | ||||
-rw-r--r-- | gdk/gdkwindow.c | 278 | ||||
-rw-r--r-- | gdk/gdkwindow.h | 6 | ||||
-rw-r--r-- | gtk/gtkwidget.c | 48 | ||||
-rw-r--r-- | gtk/gtkwidget.h | 2 |
10 files changed, 902 insertions, 52 deletions
diff --git a/gdk/Makefile.am b/gdk/Makefile.am index a04f7a3833..a265bcaf17 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 0000000000..6b23ce6321 --- /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 0000000000..62124e2e7d --- /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 0000000000..ff20632e70 --- /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 0000000000..f4815a9498 --- /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 d150414c55..fb561e6f74 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 0bc984def3..f82ad162b4 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 a4fbac022e..a93e5ced99 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 d161ee9194..a3509c4cc7 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 a4c20e86c2..a8572e69d1 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); |