forked from AuroraMiddleware/gtk
1824796bfb
Add the ability to freeze a frame clock, which pauses its operation, then thaw it again later to resume. Initially this is used to implement freezing updates when we are waiting for ConfigureNotify in response to changing the size of a toplevel. We need a per-window clock for this to work properly, so add that for the X11 backend. https://bugzilla.gnome.org/show_bug.cgi?id=685460
284 lines
8.4 KiB
C
284 lines
8.4 KiB
C
/* 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;
|
|
guint freeze_count;
|
|
|
|
GdkFrameClockPhase requested;
|
|
GdkFrameClockPhase phase;
|
|
};
|
|
|
|
static gboolean gdk_frame_clock_paint_idle (void *data);
|
|
|
|
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 ();
|
|
priv->freeze_count = 0;
|
|
}
|
|
|
|
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->phase != GDK_FRAME_CLOCK_PHASE_NONE)
|
|
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 void
|
|
maybe_start_idle (GdkFrameClockIdle *clock_idle)
|
|
{
|
|
GdkFrameClockIdlePrivate *priv = clock_idle->priv;
|
|
|
|
if (priv->idle_id == 0 && priv->freeze_count == 0 && priv->requested != 0)
|
|
{
|
|
priv->idle_id = gdk_threads_add_idle_full (GDK_PRIORITY_REDRAW,
|
|
gdk_frame_clock_paint_idle,
|
|
g_object_ref (clock_idle),
|
|
(GDestroyNotify) g_object_unref);
|
|
|
|
gdk_frame_clock_frame_requested (GDK_FRAME_CLOCK (clock_idle));
|
|
}
|
|
}
|
|
|
|
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->frame_time = compute_frame_time (clock_idle);
|
|
|
|
switch (priv->phase)
|
|
{
|
|
case GDK_FRAME_CLOCK_PHASE_NONE:
|
|
case GDK_FRAME_CLOCK_PHASE_BEFORE_PAINT:
|
|
if (priv->freeze_count == 0)
|
|
{
|
|
priv->phase = GDK_FRAME_CLOCK_PHASE_BEFORE_PAINT;
|
|
priv->requested &= ~GDK_FRAME_CLOCK_PHASE_BEFORE_PAINT;
|
|
/* We always emit ::before-paint and ::after-paint even if
|
|
* not explicitly requested, and unlike other phases,
|
|
* they don't get repeated if you freeze/thaw while
|
|
* in them. */
|
|
g_signal_emit_by_name (G_OBJECT (clock), "before-paint");
|
|
priv->phase = GDK_FRAME_CLOCK_PHASE_LAYOUT;
|
|
}
|
|
case GDK_FRAME_CLOCK_PHASE_LAYOUT:
|
|
if (priv->freeze_count == 0)
|
|
{
|
|
priv->phase = GDK_FRAME_CLOCK_PHASE_LAYOUT;
|
|
if (priv->requested & GDK_FRAME_CLOCK_PHASE_LAYOUT)
|
|
{
|
|
priv->requested &= ~GDK_FRAME_CLOCK_PHASE_LAYOUT;
|
|
g_signal_emit_by_name (G_OBJECT (clock), "layout");
|
|
}
|
|
}
|
|
case GDK_FRAME_CLOCK_PHASE_PAINT:
|
|
if (priv->freeze_count == 0)
|
|
{
|
|
priv->phase = GDK_FRAME_CLOCK_PHASE_PAINT;
|
|
if (priv->requested & GDK_FRAME_CLOCK_PHASE_PAINT)
|
|
{
|
|
priv->requested &= ~GDK_FRAME_CLOCK_PHASE_PAINT;
|
|
g_signal_emit_by_name (G_OBJECT (clock), "paint");
|
|
}
|
|
}
|
|
case GDK_FRAME_CLOCK_PHASE_AFTER_PAINT:
|
|
if (priv->freeze_count == 0)
|
|
{
|
|
priv->phase = GDK_FRAME_CLOCK_PHASE_AFTER_PAINT;
|
|
priv->requested &= ~GDK_FRAME_CLOCK_PHASE_AFTER_PAINT;
|
|
g_signal_emit_by_name (G_OBJECT (clock), "after-paint");
|
|
/* the ::after-paint phase doesn't get repeated on freeze/thaw */
|
|
priv->phase = GDK_FRAME_CLOCK_PHASE_NONE;
|
|
}
|
|
}
|
|
|
|
maybe_start_idle (clock_idle);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static void
|
|
gdk_frame_clock_idle_request_phase (GdkFrameClock *clock,
|
|
GdkFrameClockPhase phase)
|
|
{
|
|
GdkFrameClockIdle *clock_idle = GDK_FRAME_CLOCK_IDLE (clock);
|
|
GdkFrameClockIdlePrivate *priv = clock_idle->priv;
|
|
|
|
priv->requested |= phase;
|
|
maybe_start_idle (clock_idle);
|
|
}
|
|
|
|
static GdkFrameClockPhase
|
|
gdk_frame_clock_idle_get_requested (GdkFrameClock *clock)
|
|
{
|
|
GdkFrameClockIdlePrivate *priv = GDK_FRAME_CLOCK_IDLE (clock)->priv;
|
|
|
|
return priv->requested;
|
|
}
|
|
|
|
static void
|
|
gdk_frame_clock_idle_freeze (GdkFrameClock *clock)
|
|
{
|
|
GdkFrameClockIdlePrivate *priv = GDK_FRAME_CLOCK_IDLE (clock)->priv;
|
|
|
|
priv->freeze_count++;
|
|
|
|
if (priv->freeze_count == 1)
|
|
{
|
|
if (priv->idle_id)
|
|
{
|
|
g_source_remove (priv->idle_id);
|
|
priv->idle_id = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
gdk_frame_clock_idle_thaw (GdkFrameClock *clock)
|
|
{
|
|
GdkFrameClockIdle *clock_idle = GDK_FRAME_CLOCK_IDLE (clock);
|
|
GdkFrameClockIdlePrivate *priv = clock_idle->priv;
|
|
|
|
g_return_if_fail (priv->freeze_count > 0);
|
|
|
|
priv->freeze_count--;
|
|
if (priv->freeze_count == 0)
|
|
maybe_start_idle (clock_idle);
|
|
}
|
|
|
|
static void
|
|
gdk_frame_clock_idle_interface_init (GdkFrameClockInterface *iface)
|
|
{
|
|
iface->get_frame_time = gdk_frame_clock_idle_get_frame_time;
|
|
iface->request_phase = gdk_frame_clock_idle_request_phase;
|
|
iface->get_requested = gdk_frame_clock_idle_get_requested;
|
|
iface->freeze = gdk_frame_clock_idle_freeze;
|
|
iface->thaw = gdk_frame_clock_idle_thaw;
|
|
}
|
|
|
|
GdkFrameClock *
|
|
_gdk_frame_clock_idle_new (void)
|
|
{
|
|
GdkFrameClockIdle *clock;
|
|
|
|
clock = g_object_new (GDK_TYPE_FRAME_CLOCK_IDLE, NULL);
|
|
|
|
return GDK_FRAME_CLOCK (clock);
|
|
}
|