[mutter] frame-clock: Make it possible to drive timelines
- From: Georges Basile Stavracas Neto <gbsneto src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [mutter] frame-clock: Make it possible to drive timelines
- Date: Thu, 2 Jul 2020 20:50:47 +0000 (UTC)
commit c302f4d3792600c1b0628ef7757e832aeb7988fa
Author: Jonas Ã…dahl <jadahl gmail com>
Date: Wed Mar 25 18:16:39 2020 +0100
frame-clock: Make it possible to drive timelines
Add API to add and remove ClutterTimeline objects to the frame clock.
Just as the legacy master clock, having a timeline added to the frame
clock causes the frame clock to continuously reschedule updates until
the timeline is removed.
ClutterTimeline is adapted to be able to be driven by a
ClutterFrameClock. This is done by adding a 'frame-clock' property, and
if set, the timeline will add and remove itself to the frame clock
instead of the master clock.
https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1285
clutter/clutter/clutter-frame-clock.c | 75 ++++++++++++++++++++++-
clutter/clutter/clutter-frame-clock.h | 6 ++
clutter/clutter/clutter-timeline.c | 111 +++++++++++++++++++++++++++++++---
clutter/clutter/clutter-timeline.h | 11 ++++
clutter/clutter/clutter-types.h | 1 +
5 files changed, 196 insertions(+), 8 deletions(-)
---
diff --git a/clutter/clutter/clutter-frame-clock.c b/clutter/clutter/clutter-frame-clock.c
index 95b6249550..88c07cc254 100644
--- a/clutter/clutter/clutter-frame-clock.c
+++ b/clutter/clutter/clutter-frame-clock.c
@@ -20,6 +20,7 @@
#include "clutter/clutter-frame-clock.h"
#include "clutter/clutter-main.h"
+#include "clutter/clutter-timeline-private.h"
#include "cogl/cogl-trace.h"
static inline uint64_t
@@ -80,15 +81,83 @@ struct _ClutterFrameClock
gboolean pending_reschedule_now;
int inhibit_count;
+
+ GList *timelines;
};
G_DEFINE_TYPE (ClutterFrameClock, clutter_frame_clock,
G_TYPE_OBJECT)
+void
+clutter_frame_clock_add_timeline (ClutterFrameClock *frame_clock,
+ ClutterTimeline *timeline)
+{
+ gboolean is_first;
+
+ if (g_list_find (frame_clock->timelines, timeline))
+ return;
+
+ is_first = !frame_clock->timelines;
+
+ frame_clock->timelines = g_list_prepend (frame_clock->timelines, timeline);
+
+ if (is_first)
+ clutter_frame_clock_schedule_update (frame_clock);
+}
+
+void
+clutter_frame_clock_remove_timeline (ClutterFrameClock *frame_clock,
+ ClutterTimeline *timeline)
+{
+ frame_clock->timelines = g_list_remove (frame_clock->timelines, timeline);
+}
+
+static void
+advance_timelines (ClutterFrameClock *frame_clock,
+ int64_t time_us)
+{
+ GList *timelines;
+ GList *l;
+
+ /* we protect ourselves from timelines being removed during
+ * the advancement by other timelines by copying the list of
+ * timelines, taking a reference on them, iterating over the
+ * copied list and then releasing the reference.
+ *
+ * we cannot simply take a reference on the timelines and still
+ * use the list held by the master clock because the do_tick()
+ * might result in the creation of a new timeline, which gets
+ * added at the end of the list with no reference increase and
+ * thus gets disposed at the end of the iteration.
+ *
+ * this implies that a newly added timeline will not be advanced
+ * by this clock iteration, which is perfectly fine since we're
+ * in its first cycle.
+ *
+ * we also cannot steal the frame clock timelines list because
+ * a timeline might be removed as the direct result of do_tick()
+ * and remove_timeline() would not find the timeline, failing
+ * and leaving a dangling pointer behind.
+ */
+
+ timelines = g_list_copy (frame_clock->timelines);
+ g_list_foreach (timelines, (GFunc) g_object_ref, NULL);
+
+ for (l = timelines; l; l = l->next)
+ {
+ ClutterTimeline *timeline = l->data;
+
+ _clutter_timeline_do_tick (timeline, time_us / 1000);
+ }
+
+ g_list_free_full (timelines, g_object_unref);
+}
+
static void
maybe_reschedule_update (ClutterFrameClock *frame_clock)
{
- if (frame_clock->pending_reschedule)
+ if (frame_clock->pending_reschedule ||
+ frame_clock->timelines)
{
frame_clock->pending_reschedule = FALSE;
@@ -331,6 +400,10 @@ clutter_frame_clock_dispatch (ClutterFrameClock *frame_clock,
}
COGL_TRACE_END (ClutterFrameClockEvents);
+ COGL_TRACE_BEGIN (ClutterFrameClockTimelines, "Frame Clock (timelines)");
+ advance_timelines (frame_clock, time_us);
+ COGL_TRACE_END (ClutterFrameClockTimelines);
+
COGL_TRACE_BEGIN (ClutterFrameClockFrame, "Frame Clock (frame)");
result = frame_clock->listener.iface->frame (frame_clock,
frame_count,
diff --git a/clutter/clutter/clutter-frame-clock.h b/clutter/clutter/clutter-frame-clock.h
index 221c393f4a..6f70546d2a 100644
--- a/clutter/clutter/clutter-frame-clock.h
+++ b/clutter/clutter/clutter-frame-clock.h
@@ -68,4 +68,10 @@ void clutter_frame_clock_inhibit (ClutterFrameClock *frame_clock);
CLUTTER_EXPORT
void clutter_frame_clock_uninhibit (ClutterFrameClock *frame_clock);
+void clutter_frame_clock_add_timeline (ClutterFrameClock *frame_clock,
+ ClutterTimeline *timeline);
+
+void clutter_frame_clock_remove_timeline (ClutterFrameClock *frame_clock,
+ ClutterTimeline *timeline);
+
#endif /* CLUTTER_FRAME_CLOCK_H */
diff --git a/clutter/clutter/clutter-timeline.c b/clutter/clutter/clutter-timeline.c
index da9a0da4c8..bc2e744e4b 100644
--- a/clutter/clutter/clutter-timeline.c
+++ b/clutter/clutter/clutter-timeline.c
@@ -99,6 +99,7 @@
#include "clutter-debug.h"
#include "clutter-easing.h"
#include "clutter-enum-types.h"
+#include "clutter-frame-clock.h"
#include "clutter-main.h"
#include "clutter-marshal.h"
#include "clutter-master-clock.h"
@@ -110,6 +111,8 @@ struct _ClutterTimelinePrivate
{
ClutterTimelineDirection direction;
+ ClutterFrameClock *frame_clock;
+
guint delay_id;
/* The total length in milliseconds of this timeline */
@@ -177,6 +180,7 @@ enum
PROP_AUTO_REVERSE,
PROP_REPEAT_COUNT,
PROP_PROGRESS_MODE,
+ PROP_FRAME_CLOCK,
PROP_LAST
};
@@ -453,6 +457,10 @@ clutter_timeline_set_property (GObject *object,
clutter_timeline_set_progress_mode (timeline, g_value_get_enum (value));
break;
+ case PROP_FRAME_CLOCK:
+ clutter_timeline_set_frame_clock (timeline, g_value_get_object (value));
+ break;
+
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -494,6 +502,10 @@ clutter_timeline_get_property (GObject *object,
g_value_set_enum (value, priv->progress_mode);
break;
+ case PROP_FRAME_CLOCK:
+ g_value_set_object (value, priv->frame_clock);
+ break;
+
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -505,17 +517,27 @@ clutter_timeline_finalize (GObject *object)
{
ClutterTimeline *self = CLUTTER_TIMELINE (object);
ClutterTimelinePrivate *priv = self->priv;
- ClutterMasterClock *master_clock;
if (priv->markers_by_name)
g_hash_table_destroy (priv->markers_by_name);
if (priv->is_playing)
{
- master_clock = _clutter_master_clock_get_default ();
- _clutter_master_clock_remove_timeline (master_clock, self);
+ if (priv->frame_clock)
+ {
+ clutter_frame_clock_remove_timeline (priv->frame_clock, self);
+ }
+ else
+ {
+ ClutterMasterClock *master_clock;
+
+ master_clock = _clutter_master_clock_get_default ();
+ _clutter_master_clock_remove_timeline (master_clock, self);
+ }
}
+ g_clear_object (&priv->frame_clock);
+
G_OBJECT_CLASS (clutter_timeline_parent_class)->finalize (object);
}
@@ -643,6 +665,18 @@ clutter_timeline_class_init (ClutterTimelineClass *klass)
CLUTTER_LINEAR,
CLUTTER_PARAM_READWRITE);
+ /**
+ * ClutterTimeline:frame-clock:
+ *
+ * The frame clock driving the timeline.
+ */
+ obj_props[PROP_FRAME_CLOCK] =
+ g_param_spec_object ("frame-clock",
+ "Frame clock",
+ "Frame clock driving the timeline",
+ CLUTTER_TYPE_FRAME_CLOCK,
+ G_PARAM_CONSTRUCT | CLUTTER_PARAM_READWRITE);
+
object_class->dispose = clutter_timeline_dispose;
object_class->finalize = clutter_timeline_finalize;
object_class->set_property = clutter_timeline_set_property;
@@ -925,7 +959,6 @@ set_is_playing (ClutterTimeline *timeline,
gboolean is_playing)
{
ClutterTimelinePrivate *priv = timeline->priv;
- ClutterMasterClock *master_clock;
is_playing = !!is_playing;
@@ -934,15 +967,29 @@ set_is_playing (ClutterTimeline *timeline,
priv->is_playing = is_playing;
- master_clock = _clutter_master_clock_get_default ();
if (priv->is_playing)
{
priv->waiting_first_tick = TRUE;
priv->current_repeat = 0;
- _clutter_master_clock_add_timeline (master_clock, timeline);
+ }
+
+ if (priv->frame_clock)
+ {
+ if (priv->is_playing)
+ clutter_frame_clock_add_timeline (priv->frame_clock, timeline);
+ else
+ clutter_frame_clock_remove_timeline (priv->frame_clock, timeline);
}
else
- _clutter_master_clock_remove_timeline (master_clock, timeline);
+ {
+ ClutterMasterClock *master_clock;
+
+ master_clock = _clutter_master_clock_get_default ();
+ if (priv->is_playing)
+ _clutter_master_clock_add_timeline (master_clock, timeline);
+ else
+ _clutter_master_clock_remove_timeline (master_clock, timeline);
+ }
}
static gboolean
@@ -1339,6 +1386,26 @@ clutter_timeline_new (guint duration_ms)
NULL);
}
+/**
+ * clutter_timeline_new_for_frame_clock:
+ * @frame_clock: The #ClutterFrameClock the timeline is driven by
+ * @duration_ms: Duration of the timeline in milliseconds
+ *
+ * Creates a new #ClutterTimeline with a duration of @duration milli seconds.
+ *
+ * Return value: the newly created #ClutterTimeline instance. Use
+ * g_object_unref() when done using it
+ */
+ClutterTimeline *
+clutter_timeline_new_for_frame_clock (ClutterFrameClock *frame_clock,
+ unsigned int duration_ms)
+{
+ return g_object_new (CLUTTER_TYPE_TIMELINE,
+ "duration", duration_ms,
+ "frame-clock", frame_clock,
+ NULL);
+}
+
/**
* clutter_timeline_get_delay:
* @timeline: a #ClutterTimeline
@@ -2419,3 +2486,33 @@ clutter_timeline_get_cubic_bezier_progress (ClutterTimeline *timeline,
return TRUE;
}
+
+/**
+ * clutter_timeline_get_frame_clock: (skip)
+ */
+ClutterFrameClock *
+clutter_timeline_get_frame_clock (ClutterTimeline *timeline)
+{
+ g_return_val_if_fail (CLUTTER_IS_TIMELINE (timeline), NULL);
+
+ return timeline->priv->frame_clock;
+}
+
+void
+clutter_timeline_set_frame_clock (ClutterTimeline *timeline,
+ ClutterFrameClock *frame_clock)
+{
+ ClutterTimelinePrivate *priv;
+
+ g_return_if_fail (CLUTTER_IS_TIMELINE (timeline));
+
+ priv = timeline->priv;
+
+ if (priv->frame_clock == frame_clock)
+ return;
+
+ g_set_object (&priv->frame_clock, frame_clock);
+
+ g_object_notify_by_pspec (G_OBJECT (timeline),
+ obj_props[PROP_FRAME_CLOCK]);
+}
diff --git a/clutter/clutter/clutter-timeline.h b/clutter/clutter/clutter-timeline.h
index d6a5dde7b2..265c15318e 100644
--- a/clutter/clutter/clutter-timeline.h
+++ b/clutter/clutter/clutter-timeline.h
@@ -121,6 +121,10 @@ GType clutter_timeline_get_type (void) G_GNUC_CONST;
CLUTTER_EXPORT
ClutterTimeline * clutter_timeline_new (guint
duration_ms);
+CLUTTER_EXPORT
+ClutterTimeline * clutter_timeline_new_for_frame_clock (ClutterFrameClock
*frame_clock,
+ unsigned int
duration_ms);
+
CLUTTER_EXPORT
guint clutter_timeline_get_duration (ClutterTimeline
*timeline);
CLUTTER_EXPORT
@@ -221,6 +225,13 @@ gint64 clutter_timeline_get_duration_hint
CLUTTER_EXPORT
gint clutter_timeline_get_current_repeat (ClutterTimeline
*timeline);
+CLUTTER_EXPORT
+ClutterFrameClock * clutter_timeline_get_frame_clock (ClutterTimeline
*timeline);
+
+CLUTTER_EXPORT
+void clutter_timeline_set_frame_clock (ClutterTimeline
*timeline,
+ ClutterFrameClock
*frame_clock);
+
G_END_DECLS
#endif /* _CLUTTER_TIMELINE_H__ */
diff --git a/clutter/clutter/clutter-types.h b/clutter/clutter/clutter-types.h
index 0c6400d93e..b982532755 100644
--- a/clutter/clutter/clutter-types.h
+++ b/clutter/clutter/clutter-types.h
@@ -57,6 +57,7 @@ typedef struct _ClutterActorIter ClutterActorIter;
typedef struct _ClutterPaintNode ClutterPaintNode;
typedef struct _ClutterContent ClutterContent; /* dummy */
typedef struct _ClutterScrollActor ClutterScrollActor;
+typedef struct _ClutterFrameClock ClutterFrameClock;
typedef struct _ClutterInterval ClutterInterval;
typedef struct _ClutterAnimatable ClutterAnimatable; /* dummy */
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]