diff options
author | Owen W. Taylor <otaylor@fishsoup.net> | 2012-09-26 10:28:06 -0400 |
---|---|---|
committer | Owen W. Taylor <otaylor@fishsoup.net> | 2013-02-14 17:19:49 -0500 |
commit | a69285da08a2a61d5fd817ee8ccb88a6b6deaef6 (patch) | |
tree | c4fdef3922ea6eca4d1e16ef320b3a6760cbe09b /gdk/gdkframeclockidle.c | |
parent | 05386b44e04bc23e6cd68b74dd9047b874a2020b (diff) | |
download | gtk+-a69285da08a2a61d5fd817ee8ccb88a6b6deaef6.tar.gz |
Compress motion synchronized with the paint cycle
When we have pending motion events, instead of delivering them
directly, request the new FLUSH_EVENTS phase of the frame clock.
This allows us to compress repeated motion events sent to the
same window.
In the FLUSH_EVENTS phase, which occur at priority GDK_PRIORITY_EVENTS + 1,
we deliver any pending motion events then turn off event delivery
until the end of the next frame. Turning off event delivery means
that we'll reliably paint the compressed motion events even if more
have arrived.
Add a motion-compression test case which demonstrates behavior when
an application takes too long handle motion events. It is unusable
without this patch but behaves fine with the patch.
https://bugzilla.gnome.org/show_bug.cgi?id=685460
Diffstat (limited to 'gdk/gdkframeclockidle.c')
-rw-r--r-- | gdk/gdkframeclockidle.c | 121 |
1 files changed, 99 insertions, 22 deletions
diff --git a/gdk/gdkframeclockidle.c b/gdk/gdkframeclockidle.c index bf68fd1b64..ff4b479ae4 100644 --- a/gdk/gdkframeclockidle.c +++ b/gdk/gdkframeclockidle.c @@ -39,13 +39,15 @@ struct _GdkFrameClockIdlePrivate guint64 frame_time; guint64 min_next_frame_time; - guint idle_id; + guint flush_idle_id; + guint paint_idle_id; guint freeze_count; GdkFrameClockPhase requested; GdkFrameClockPhase phase; }; +static gboolean gdk_frame_clock_flush_idle (void *data); static gboolean gdk_frame_clock_paint_idle (void *data); static void gdk_frame_clock_idle_finalize (GObject *object); @@ -144,7 +146,7 @@ maybe_start_idle (GdkFrameClockIdle *clock_idle) { GdkFrameClockIdlePrivate *priv = clock_idle->priv; - if (priv->idle_id == 0 && priv->freeze_count == 0 && priv->requested != 0) + if (priv->freeze_count == 0) { guint min_interval = 0; @@ -155,41 +157,89 @@ maybe_start_idle (GdkFrameClockIdle *clock_idle) min_interval = (min_interval_us + 500) / 1000; } - priv->idle_id = gdk_threads_add_timeout_full (GDK_PRIORITY_REDRAW, - min_interval, - gdk_frame_clock_paint_idle, - g_object_ref (clock_idle), - (GDestroyNotify) g_object_unref); + if (priv->flush_idle_id == 0 && + (priv->requested & GDK_FRAME_CLOCK_PHASE_FLUSH_EVENTS) != 0) + { + priv->flush_idle_id = gdk_threads_add_timeout_full (GDK_PRIORITY_EVENTS + 1, + min_interval, + gdk_frame_clock_flush_idle, + g_object_ref (clock_idle), + (GDestroyNotify) g_object_unref); + } + + if (priv->paint_idle_id == 0 && + (priv->requested & ~GDK_FRAME_CLOCK_PHASE_FLUSH_EVENTS) != 0) + { + priv->paint_idle_id = gdk_threads_add_timeout_full (GDK_PRIORITY_REDRAW, + min_interval, + gdk_frame_clock_paint_idle, + g_object_ref (clock_idle), + (GDestroyNotify) g_object_unref); - gdk_frame_clock_frame_requested (GDK_FRAME_CLOCK (clock_idle)); + gdk_frame_clock_frame_requested (GDK_FRAME_CLOCK (clock_idle)); + } } } static gboolean +gdk_frame_clock_flush_idle (void *data) +{ + GdkFrameClock *clock = GDK_FRAME_CLOCK (data); + GdkFrameClockIdle *clock_idle = GDK_FRAME_CLOCK_IDLE (clock); + GdkFrameClockIdlePrivate *priv = clock_idle->priv; + + priv->flush_idle_id = 0; + + if (priv->phase != GDK_FRAME_CLOCK_PHASE_NONE) + return FALSE; + + priv->phase = GDK_FRAME_CLOCK_PHASE_FLUSH_EVENTS; + priv->requested &= ~GDK_FRAME_CLOCK_PHASE_FLUSH_EVENTS; + + g_signal_emit_by_name (G_OBJECT (clock), "flush-events"); + + if ((priv->requested & ~GDK_FRAME_CLOCK_PHASE_FLUSH_EVENTS) != 0) + priv->phase = GDK_FRAME_CLOCK_PHASE_BEFORE_PAINT; + else + priv->phase = GDK_FRAME_CLOCK_PHASE_NONE; + + return FALSE; +} + +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; + gboolean skip_to_resume_events; - priv->idle_id = 0; + priv->paint_idle_id = 0; + + skip_to_resume_events = + (priv->requested & ~(GDK_FRAME_CLOCK_PHASE_FLUSH_EVENTS | GDK_FRAME_CLOCK_PHASE_RESUME_EVENTS)) == 0; switch (priv->phase) { + case GDK_FRAME_CLOCK_PHASE_FLUSH_EVENTS: + break; case GDK_FRAME_CLOCK_PHASE_NONE: case GDK_FRAME_CLOCK_PHASE_BEFORE_PAINT: if (priv->freeze_count == 0) { priv->frame_time = compute_frame_time (clock_idle); - 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, + priv->phase = GDK_FRAME_CLOCK_PHASE_BEFORE_PAINT; + /* We always emit ::before-paint and ::after-paint if + * any of the intermediate phases are requested and * 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_UPDATE; + if (!skip_to_resume_events) + { + priv->requested &= ~GDK_FRAME_CLOCK_PHASE_BEFORE_PAINT; + g_signal_emit_by_name (G_OBJECT (clock), "before-paint"); + } + priv->phase = GDK_FRAME_CLOCK_PHASE_UPDATE; } case GDK_FRAME_CLOCK_PHASE_UPDATE: if (priv->freeze_count == 0) @@ -224,9 +274,24 @@ gdk_frame_clock_paint_idle (void *data) 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 */ + if (!skip_to_resume_events) + { + 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_RESUME_EVENTS; + } + case GDK_FRAME_CLOCK_PHASE_RESUME_EVENTS: + if (priv->freeze_count == 0) + { + if (priv->requested & GDK_FRAME_CLOCK_PHASE_RESUME_EVENTS) + { + priv->requested &= ~GDK_FRAME_CLOCK_PHASE_RESUME_EVENTS; + g_signal_emit_by_name (G_OBJECT (clock), "resume-events"); + } + /* the ::resume-event phase doesn't get repeated on freeze/thaw */ priv->phase = GDK_FRAME_CLOCK_PHASE_NONE; } } @@ -276,10 +341,15 @@ gdk_frame_clock_idle_freeze (GdkFrameClock *clock) if (priv->freeze_count == 1) { - if (priv->idle_id) + if (priv->flush_idle_id) { - g_source_remove (priv->idle_id); - priv->idle_id = 0; + g_source_remove (priv->flush_idle_id); + priv->flush_idle_id = 0; + } + if (priv->paint_idle_id) + { + g_source_remove (priv->paint_idle_id); + priv->paint_idle_id = 0; } } } @@ -294,7 +364,14 @@ gdk_frame_clock_idle_thaw (GdkFrameClock *clock) priv->freeze_count--; if (priv->freeze_count == 0) - maybe_start_idle (clock_idle); + { + maybe_start_idle (clock_idle); + /* If nothing is requested so we didn't start an idle, we need + * to skip to the end of the state chain, since the idle won't + * run and do it for us. */ + if (priv->paint_idle_id == 0) + priv->phase = GDK_FRAME_CLOCK_PHASE_NONE; + } } static void |