2008-03-18 Emmanuele Bassi <ebassi@openedhand.com>

* clutter.symbols: Add new symbols

	* clutter/clutter-score.[ch]: Use the newly added marker API
	on the timelines to implement attaching timelines at specific
	points, using either milliseconds or frames.

	* tests/test-score.c (main): Test the new API.
This commit is contained in:
Emmanuele Bassi 2008-03-18 17:54:40 +00:00
parent 43d9d579b4
commit b6b9dd7c65
5 changed files with 478 additions and 248 deletions

View File

@ -1,3 +1,13 @@
2008-03-18 Emmanuele Bassi <ebassi@openedhand.com>
* clutter.symbols: Add new symbols
* clutter/clutter-score.[ch]: Use the newly added marker API
on the timelines to implement attaching timelines at specific
points, using either milliseconds or frames.
* tests/test-score.c (main): Test the new API.
2008-03-18 Emmanuele Bassi <ebassi@openedhand.com>
* clutter/clutter-marshal.list: Add signature for the

View File

@ -442,6 +442,8 @@ clutter_redraw
clutter_rotate_axis_get_type
clutter_rotate_direction_get_type
clutter_score_append
clutter_score_append_at_frame
clutter_score_append_at_time
clutter_score_get_loop
clutter_score_get_timeline
clutter_score_get_type

View File

@ -38,7 +38,7 @@
* For example, this code will start two #ClutterTimeline<!-- -->s after
* a third timeline terminates:
*
* <informalexample><programlisting>
* |[
* ClutterTimeline *timeline_1, *timeline_2, *timeline_3;
* ClutterScore *score;
*
@ -52,12 +52,17 @@
* clutter_score_append (score, timeline_1, timeline_3);
*
* clutter_score_start ();
* </programlisting></informalexample>
* ]|
*
* A #ClutterScore takes a reference on the timelines it manages.
*
* New timelines can be added to the #ClutterScore using
* New timelines can be appended to the #ClutterScore using
* clutter_score_append() and removed using clutter_score_remove().
* Timelines can either be appended at the end of other timelines
* or at the beginning of the score using clutter_score_append().
* Timelines can also be appeneded at a specific position, expressed
* in either millisecond or frame number, of the parent timeline,
* using clutter_score_append_at_time() or clutter_score_append_at_frame().
*
* The score can be cleared using clutter_score_remove_all().
*
@ -81,15 +86,29 @@
#include "clutter-private.h"
#include "clutter-debug.h"
typedef enum {
SCORE_ENTRY_TIME,
SCORE_ENTRY_FRAME,
SCORE_ENTRY_APPEND
} ClutterScoreEntryType;
typedef struct _ClutterScoreEntry ClutterScoreEntry;
struct _ClutterScoreEntry
{
ClutterTimeline *timeline;
guint id;
ClutterScoreEntryType type;
/* signal handler id */
gulong completed_id;
gchar *id;
ClutterTimeline *timeline;
ClutterTimeline *parent;
guint msecs;
guint frame;
/* signal handlers id */
guint complete_id;
guint marker_id;
ClutterScore *score;
@ -130,6 +149,8 @@ enum
LAST_SIGNAL
};
static inline void clutter_score_clear (ClutterScore *score);
G_DEFINE_TYPE (ClutterScore, clutter_score, G_TYPE_OBJECT);
static int score_signals[LAST_SIGNAL] = { 0 };
@ -177,26 +198,14 @@ clutter_score_get_property (GObject *gobject,
static void
clutter_score_finalize (GObject *object)
{
ClutterScorePrivate *priv = CLUTTER_SCORE (object)->priv;
ClutterScore *score = CLUTTER_SCORE (object);
if (priv->running_timelines)
g_hash_table_destroy (priv->running_timelines);
clutter_score_stop (score);
clutter_score_clear (score);
G_OBJECT_CLASS (clutter_score_parent_class)->finalize (object);
}
static void
clutter_score_dispose (GObject *object)
{
ClutterScore *self = CLUTTER_SCORE(object);
ClutterScorePrivate *priv;
priv = self->priv;
G_OBJECT_CLASS (clutter_score_parent_class)->dispose (object);
}
static void
clutter_score_class_init (ClutterScoreClass *klass)
{
@ -205,7 +214,6 @@ clutter_score_class_init (ClutterScoreClass *klass)
gobject_class->set_property = clutter_score_set_property;
gobject_class->get_property = clutter_score_get_property;
gobject_class->finalize = clutter_score_finalize;
gobject_class->dispose = clutter_score_dispose;
g_type_class_add_private (klass, sizeof (ClutterScorePrivate));
@ -411,6 +419,178 @@ clutter_score_is_playing (ClutterScore *score)
return (g_hash_table_size (score->priv->running_timelines) != 0);
}
typedef enum {
FIND_BY_TIMELINE,
FIND_BY_ID,
REMOVE_BY_ID,
LIST_TIMELINES
} TraverseAction;
typedef struct {
TraverseAction action;
ClutterScore *score;
/* parameters */
union {
ClutterTimeline *timeline;
const gchar *id;
ClutterScoreEntry *entry;
} d;
gpointer result;
} TraverseClosure;
static gboolean
destroy_entry (GNode *node,
G_GNUC_UNUSED gpointer data)
{
ClutterScoreEntry *entry = node->data;
if (G_LIKELY (entry != NULL))
{
if (entry->marker_id)
{
g_signal_handler_disconnect (entry->parent, entry->marker_id);
entry->marker_id = 0;
}
if (entry->complete_id)
{
g_signal_handler_disconnect (entry->timeline, entry->complete_id);
entry->complete_id = 0;
}
g_object_unref (entry->timeline);
g_free (entry->id);
g_slice_free (ClutterScoreEntry, entry);
node->data = NULL;
}
/* continue */
return FALSE;
}
/* multi-purpose traversal function for the N-ary tree used by the score */
static gboolean
traverse_children (GNode *node,
gpointer data)
{
TraverseClosure *closure = data;
ClutterScoreEntry *entry = node->data;
gboolean retval = FALSE;
/* root */
if (!entry)
return TRUE;
switch (closure->action)
{
case FIND_BY_TIMELINE:
if (closure->d.timeline == entry->timeline)
{
closure->result = node;
retval = TRUE;
}
break;
case FIND_BY_ID:
if (strcmp (closure->d.id, entry->id) == 0)
{
closure->result = node;
retval = TRUE;
}
break;
case REMOVE_BY_ID:
if (strcmp (closure->d.id, entry->id) == 0)
{
if (entry->complete_id)
{
g_signal_handler_disconnect (entry->timeline, entry->complete_id);
entry->complete_id = 0;
}
if (entry->marker_id)
{
g_signal_handler_disconnect (entry->timeline, entry->marker_id);
entry->marker_id = 0;
}
g_object_unref (entry->timeline);
g_free (entry->id);
g_node_traverse (node,
G_POST_ORDER,
G_TRAVERSE_ALL,
-1,
destroy_entry, NULL);
g_slice_free (ClutterScoreEntry, entry);
closure->result = node;
retval = TRUE;
}
break;
case LIST_TIMELINES:
closure->result = g_slist_prepend (closure->result, entry->timeline);
retval = FALSE;
break;
}
return retval;
}
static GNode *
find_entry_by_timeline (ClutterScore *score,
ClutterTimeline *timeline)
{
ClutterScorePrivate *priv = score->priv;
TraverseClosure closure;
closure.action = FIND_BY_TIMELINE;
closure.score = score;
closure.d.timeline = timeline;
closure.result = NULL;
g_node_traverse (priv->root,
G_POST_ORDER,
G_TRAVERSE_ALL,
-1,
traverse_children, &closure);
if (closure.result)
return closure.result;
return NULL;
}
static GNode *
find_entry_by_id (ClutterScore *score,
const gchar *id)
{
ClutterScorePrivate *priv = score->priv;
TraverseClosure closure;
closure.action = FIND_BY_ID;
closure.score = score;
closure.d.id = id;
closure.result = NULL;
g_node_traverse (priv->root,
G_POST_ORDER,
G_TRAVERSE_ALL,
-1,
traverse_children, &closure);
if (closure.result)
return closure.result;
return NULL;
}
/* forward declaration */
static void start_entry (ClutterScoreEntry *entry);
@ -424,18 +604,42 @@ start_children_entries (GNode *node,
}
static void
on_timeline_finish (ClutterTimeline *timeline,
ClutterScoreEntry *entry)
on_timeline_marker (ClutterTimeline *timeline,
const gchar *marker_name,
gint frame_num,
ClutterScoreEntry *entry)
{
GNode *parent;
CLUTTER_NOTE (SCHEDULER, "timeline [%p] marker ('%s') reached",
entry->timeline,
entry->id);
parent = find_entry_by_timeline (entry->score, timeline);
if (!parent)
return;
/* start every child */
if (parent->children)
{
g_node_children_foreach (parent,
G_TRAVERSE_ALL,
start_children_entries,
NULL);
}
}
static void
on_timeline_completed (ClutterTimeline *timeline,
ClutterScoreEntry *entry)
{
ClutterScorePrivate *priv = entry->score->priv;
g_hash_table_remove (priv->running_timelines,
GINT_TO_POINTER (entry->id));
g_hash_table_remove (priv->running_timelines, entry->id);
g_signal_handler_disconnect (timeline, entry->completed_id);
entry->completed_id = 0;
g_signal_handler_disconnect (timeline, entry->complete_id);
entry->complete_id = 0;
CLUTTER_NOTE (SCHEDULER, "timeline [%p] (%d) completed",
CLUTTER_NOTE (SCHEDULER, "timeline [%p] ('%s') completed",
entry->timeline,
entry->id);
@ -470,21 +674,22 @@ start_entry (ClutterScoreEntry *entry)
{
ClutterScorePrivate *priv = entry->score->priv;
entry->completed_id =
g_signal_connect (entry->timeline,
"completed", G_CALLBACK (on_timeline_finish),
entry);
if (clutter_timeline_is_playing (entry->timeline))
return;
CLUTTER_NOTE (SCHEDULER, "timeline [%p] (%d) started",
entry->complete_id = g_signal_connect (entry->timeline,
"completed",
G_CALLBACK (on_timeline_completed),
entry);
CLUTTER_NOTE (SCHEDULER, "timeline [%p] ('%s') started",
entry->timeline,
entry->id);
if (G_UNLIKELY (priv->running_timelines == NULL))
priv->running_timelines = g_hash_table_new (NULL, NULL);
priv->running_timelines = g_hash_table_new (g_str_hash, g_str_equal);
g_hash_table_insert (priv->running_timelines,
GINT_TO_POINTER (entry->id),
entry);
g_hash_table_insert (priv->running_timelines, entry->id, entry);
clutter_timeline_start (entry->timeline);
@ -499,7 +704,8 @@ foreach_running_timeline_start (gpointer key,
{
ClutterScoreEntry *entry = value;
clutter_timeline_start (entry->timeline);
if (!clutter_timeline_is_playing (entry->timeline))
clutter_timeline_start (entry->timeline);
}
/**
@ -564,11 +770,14 @@ clutter_score_stop (ClutterScore *score)
priv = score->priv;
g_hash_table_foreach_remove (priv->running_timelines,
foreach_running_timeline_stop,
NULL);
g_hash_table_destroy (priv->running_timelines);
priv->running_timelines = NULL;
if (priv->running_timelines)
{
g_hash_table_foreach_remove (priv->running_timelines,
foreach_running_timeline_stop,
NULL);
g_hash_table_destroy (priv->running_timelines);
priv->running_timelines = NULL;
}
}
/**
@ -633,161 +842,23 @@ clutter_score_pause (ClutterScore *score)
g_signal_emit (score, score_signals[PAUSED], 0);
}
typedef enum {
FIND_BY_TIMELINE,
FIND_BY_ID,
REMOVE_BY_ID,
LIST_TIMELINES
} TraverseAction;
typedef struct {
TraverseAction action;
ClutterScore *score;
/* parameters */
union {
ClutterTimeline *timeline;
guint id;
ClutterScoreEntry *entry;
} d;
gpointer result;
} TraverseClosure;
static gboolean
destroy_entry (GNode *node,
G_GNUC_UNUSED gpointer data)
{
ClutterScoreEntry *entry = node->data;
if (G_LIKELY (entry != NULL))
{
if (entry->completed_id)
g_signal_handler_disconnect (entry->timeline, entry->completed_id);
g_object_unref (entry->timeline);
g_slice_free (ClutterScoreEntry, entry);
node->data = NULL;
}
/* continue */
return FALSE;
}
/* multi-purpose traversal function for the N-ary tree used by the score */
static gboolean
traverse_children (GNode *node,
gpointer data)
{
TraverseClosure *closure = data;
ClutterScoreEntry *entry = node->data;
gboolean retval = FALSE;
/* root */
if (!entry)
return TRUE;
switch (closure->action)
{
case FIND_BY_TIMELINE:
if (closure->d.timeline == entry->timeline)
{
closure->result = node;
retval = TRUE;
}
break;
case FIND_BY_ID:
if (closure->d.id == entry->id)
{
closure->result = node;
retval = TRUE;
}
break;
case REMOVE_BY_ID:
if (closure->d.id == entry->id)
{
if (entry->completed_id)
g_signal_handler_disconnect (entry->timeline, entry->completed_id);
g_object_unref (entry->timeline);
g_node_traverse (node,
G_POST_ORDER,
G_TRAVERSE_ALL,
-1,
destroy_entry, NULL);
g_slice_free (ClutterScoreEntry, entry);
closure->result = node;
retval = TRUE;
}
break;
case LIST_TIMELINES:
closure->result = g_slist_prepend (closure->result, entry->timeline);
retval = FALSE;
break;
}
return retval;
}
static GNode *
find_entry_by_timeline (ClutterScore *score,
ClutterTimeline *timeline)
static inline void
clutter_score_clear (ClutterScore *score)
{
ClutterScorePrivate *priv = score->priv;
TraverseClosure closure;
closure.action = FIND_BY_TIMELINE;
closure.score = score;
closure.d.timeline = timeline;
closure.result = NULL;
g_node_traverse (priv->root,
G_POST_ORDER,
G_TRAVERSE_ALL,
-1,
traverse_children, &closure);
if (closure.result)
return closure.result;
return NULL;
}
static GNode *
find_entry_by_id (ClutterScore *score,
guint id)
{
ClutterScorePrivate *priv = score->priv;
TraverseClosure closure;
closure.action = FIND_BY_ID;
closure.score = score;
closure.d.id = id;
closure.result = NULL;
g_node_traverse (priv->root,
G_POST_ORDER,
G_TRAVERSE_ALL,
-1,
traverse_children, &closure);
if (closure.result)
return closure.result;
return NULL;
destroy_entry, NULL);
g_node_destroy (priv->root);
}
/**
* clutter_score_append:
* @score: a #ClutterScore
* @id: a unique string identifying the timeline
* @parent: a #ClutterTimeline in the score or %NULL
* @timeline: a #ClutterTimeline
*
@ -799,21 +870,20 @@ find_entry_by_id (ClutterScore *score,
*
* #ClutterScore will take a reference on @timeline.
*
* Return value: the id of the newly added timeline, to be used with
* clutter_score_get_timeline() and clutter_score_remove().
*
* Since: 0.6
* Since: 0.8
*/
guint
void
clutter_score_append (ClutterScore *score,
const gchar *id,
ClutterTimeline *parent,
ClutterTimeline *timeline)
{
ClutterScorePrivate *priv;
g_return_val_if_fail (CLUTTER_IS_SCORE (score), 0);
g_return_val_if_fail (parent == NULL || CLUTTER_IS_TIMELINE (parent), 0);
g_return_val_if_fail (CLUTTER_IS_TIMELINE (timeline), 0);
g_return_if_fail (CLUTTER_IS_SCORE (score));
g_return_if_fail (id != NULL);
g_return_if_fail (parent == NULL || CLUTTER_IS_TIMELINE (parent));
g_return_if_fail (CLUTTER_IS_TIMELINE (timeline));
priv = score->priv;
@ -822,17 +892,14 @@ clutter_score_append (ClutterScore *score,
ClutterScoreEntry *entry;
entry = g_slice_new (ClutterScoreEntry);
entry->type = SCORE_ENTRY_APPEND;
entry->timeline = g_object_ref (timeline);
entry->id = priv->last_id;
entry->completed_id = 0;
entry->parent = NULL;
entry->id = g_strdup (id);
entry->marker_id = 0;
entry->score = score;
entry->node = g_node_append_data (priv->root, entry);
priv->last_id += 1;
return entry->id;
}
else
{
@ -843,23 +910,159 @@ clutter_score_append (ClutterScore *score,
if (G_UNLIKELY (!node))
{
g_warning ("Unable to find the parent timeline inside the score.");
return 0;
return;
}
entry = g_slice_new (ClutterScoreEntry);
entry->type = SCORE_ENTRY_APPEND;
entry->timeline = g_object_ref (timeline);
entry->id = priv->last_id;
entry->completed_id = 0;
entry->parent = parent;
entry->id = g_strdup (id);
entry->marker_id = 0;
entry->score = score;
entry->node = g_node_append_data (node, entry);
}
}
priv->last_id += 1;
/**
* clutter_score_append_at_time:
* @score: a #ClutterScore
* @id: a unique string identifying the timeline
* @parent: the parent #ClutterTimeline
* @msecs: the time on @parent where the new timeline should
* be appended, in milliseconds
* @timeline: the #ClutterTimeline to append
*
* Appends @timeline at the given position on the @parent timeline,
* expressed in milliseconds.
*
* This function implicitly creates a timeline marker (see
* clutter_timeline_add_marker_at_time()) with the given @id on
* @parent.
*
* If you want to append @timeline at the end of @parent, use
* clutter_score_append().
*
* Since: 0.8
*/
void
clutter_score_append_at_time (ClutterScore *score,
const gchar *id,
ClutterTimeline *parent,
guint msecs,
ClutterTimeline *timeline)
{
ClutterScorePrivate *priv;
GNode *node;
ClutterScoreEntry *entry;
gchar *marker_reached_signal;
return entry->id;
g_return_if_fail (CLUTTER_IS_SCORE (score));
g_return_if_fail (id != NULL);
g_return_if_fail (CLUTTER_IS_TIMELINE (parent));
g_return_if_fail (CLUTTER_IS_TIMELINE (timeline));
priv = score->priv;
node = find_entry_by_timeline (score, parent);
if (G_UNLIKELY (!node))
{
g_warning ("Unable to find the parent timeline inside the score.");
return;
}
return 0;
entry = g_slice_new (ClutterScoreEntry);
entry->type = SCORE_ENTRY_TIME;
entry->timeline = g_object_ref (timeline);
entry->parent = parent;
entry->msecs = msecs;
entry->id = g_strdup (id);
entry->score = score;
clutter_timeline_add_marker_at_time (entry->parent,
entry->id,
entry->msecs);
marker_reached_signal = g_strdup_printf ("marker-reached::%s", entry->id);
entry->marker_id = g_signal_connect (entry->parent,
marker_reached_signal,
G_CALLBACK (on_timeline_marker),
entry);
entry->node = g_node_append_data (node, entry);
g_free (marker_reached_signal);
}
/**
* clutter_score_append_at_frame:
* @score: a #ClutterScore
* @id: a unique string identifying the timeline
* @parent: the parent #ClutterTimeline
* @msecs: the frame of @parent where the new timeline should
* be appended to
* @timeline: the #ClutterTimeline to append
*
* Appends @timeline at the given position on the @parent timeline,
* expressed as a frame number.
*
* This function implicitly creates a timeline marker (see
* clutter_timeline_add_marker_at_frame()) with the given @id
* on @parent.
*
* If you want to append @timeline at the end of @parent, use
* clutter_score_append().
*
* Since: 0.8
*/
void
clutter_score_append_at_frame (ClutterScore *score,
const gchar *id,
ClutterTimeline *parent,
guint frame,
ClutterTimeline *timeline)
{
ClutterScorePrivate *priv;
GNode *node;
ClutterScoreEntry *entry;
gchar *marker_reached_signal;
g_return_if_fail (CLUTTER_IS_SCORE (score));
g_return_if_fail (id != NULL);
g_return_if_fail (CLUTTER_IS_TIMELINE (parent));
g_return_if_fail (CLUTTER_IS_TIMELINE (timeline));
priv = score->priv;
node = find_entry_by_timeline (score, parent);
if (G_UNLIKELY (!node))
{
g_warning ("Unable to find the parent timeline inside the score.");
return;
}
entry = g_slice_new (ClutterScoreEntry);
entry->type = SCORE_ENTRY_TIME;
entry->timeline = g_object_ref (timeline);
entry->parent = parent;
entry->frame = frame;
entry->id = g_strdup (id);
entry->score = score;
clutter_timeline_add_marker_at_frame (entry->parent,
entry->id,
entry->frame);
marker_reached_signal = g_strdup_printf ("marker-reached::%s", entry->id);
entry->marker_id = g_signal_connect (entry->parent,
marker_reached_signal,
G_CALLBACK (on_timeline_marker),
entry);
entry->node = g_node_append_data (node, entry);
g_free (marker_reached_signal);
}
/**
@ -871,11 +1074,11 @@ clutter_score_append (ClutterScore *score,
* the timeline has other timelines attached to it, those are removed
* as well.
*
* Since: 0.6
* Since: 0.8
*/
void
clutter_score_remove (ClutterScore *score,
guint id)
const gchar *id)
{
ClutterScorePrivate *priv;
TraverseClosure closure;
@ -915,17 +1118,13 @@ clutter_score_remove_all (ClutterScore *score)
g_return_if_fail (CLUTTER_IS_SCORE (score));
priv = score->priv;
/* this will take care of the running timelines */
clutter_score_stop (score);
priv = score->priv;
g_node_traverse (priv->root,
G_POST_ORDER,
G_TRAVERSE_ALL,
-1,
destroy_entry, NULL);
g_node_destroy (priv->root);
/* destroy all the contents of the tree */
clutter_score_clear (score);
/* recreate the sentinel */
priv->root = g_node_new (NULL);
@ -941,11 +1140,11 @@ clutter_score_remove_all (ClutterScore *score)
* Return value: the requested timeline, or %NULL. This function does
* not increase the reference count on the returned #ClutterTimeline
*
* Since: 0.6
* Since: 0.8
*/
ClutterTimeline *
clutter_score_get_timeline (ClutterScore *score,
guint id)
const gchar *id)
{
GNode *node;
ClutterScoreEntry *entry;

View File

@ -76,25 +76,37 @@ struct _ClutterScoreClass
GType clutter_score_get_type (void) G_GNUC_CONST;
ClutterScore * clutter_score_new (void);
void clutter_score_set_loop (ClutterScore *score,
gboolean loop);
gboolean clutter_score_get_loop (ClutterScore *score);
guint clutter_score_append (ClutterScore *score,
ClutterTimeline *parent,
ClutterTimeline *timeline);
void clutter_score_remove (ClutterScore *score,
guint id);
void clutter_score_remove_all (ClutterScore *score);
ClutterTimeline *clutter_score_get_timeline (ClutterScore *score,
guint id);
GSList * clutter_score_list_timelines (ClutterScore *score);
void clutter_score_set_loop (ClutterScore *score,
gboolean loop);
gboolean clutter_score_get_loop (ClutterScore *score);
void clutter_score_start (ClutterScore *score);
void clutter_score_stop (ClutterScore *score);
void clutter_score_pause (ClutterScore *score);
void clutter_score_rewind (ClutterScore *score);
gboolean clutter_score_is_playing (ClutterScore *score);
void clutter_score_append_at_time (ClutterScore *score,
const gchar *id,
ClutterTimeline *parent,
guint msecs,
ClutterTimeline *timeline);
void clutter_score_append_at_frame (ClutterScore *score,
const gchar *id,
ClutterTimeline *parent,
guint frame,
ClutterTimeline *timeline);
void clutter_score_append (ClutterScore *score,
const gchar *id,
ClutterTimeline *parent,
ClutterTimeline *timeline);
void clutter_score_remove (ClutterScore *score,
const gchar *id);
void clutter_score_remove_all (ClutterScore *score);
ClutterTimeline *clutter_score_get_timeline (ClutterScore *score,
const gchar *id);
GSList * clutter_score_list_timelines (ClutterScore *score);
void clutter_score_start (ClutterScore *score);
void clutter_score_stop (ClutterScore *score);
void clutter_score_pause (ClutterScore *score);
void clutter_score_rewind (ClutterScore *score);
gboolean clutter_score_is_playing (ClutterScore *score);
G_END_DECLS

View File

@ -42,31 +42,35 @@ main (int argc, char **argv)
ClutterTimeline *timeline_2;
ClutterTimeline *timeline_3;
ClutterTimeline *timeline_4;
ClutterTimeline *timeline_5;
GSList *timelines;
guint t1, t2, t3, t4;
clutter_init (&argc, &argv);
timeline_1 = clutter_timeline_new (10, 120);
timeline_1 = clutter_timeline_new_for_duration (1000);
g_object_set_data_full (G_OBJECT (timeline_1),
"timeline-name", g_strdup ("Timeline 1"),
g_free);
timeline_2 = clutter_timeline_clone (timeline_1);
timeline_2 = clutter_timeline_new_for_duration (1000);
g_object_set_data_full (G_OBJECT (timeline_2),
"timeline-name", g_strdup ("Timeline 2"),
g_free);
timeline_3 = clutter_timeline_clone (timeline_1);
timeline_3 = clutter_timeline_new_for_duration (1000);
g_object_set_data_full (G_OBJECT (timeline_3),
"timeline-name", g_strdup ("Timeline 3"),
g_free);
timeline_4 = clutter_timeline_clone (timeline_1);
timeline_4 = clutter_timeline_new_for_duration (1000);
g_object_set_data_full (G_OBJECT (timeline_4),
"timeline-name", g_strdup ("Timeline 4"),
g_free);
timeline_5 = clutter_timeline_new_for_duration (1000);
g_object_set_data_full (G_OBJECT (timeline_5),
"timeline-name", g_strdup ("Timeline 5"),
g_free);
score = clutter_score_new();
g_signal_connect (score, "timeline-started",
@ -79,13 +83,15 @@ main (int argc, char **argv)
G_CALLBACK (clutter_main_quit),
NULL);
t1 = clutter_score_append (score, NULL, timeline_1);
t2 = clutter_score_append (score, timeline_1, timeline_2);
t3 = clutter_score_append (score, timeline_1, timeline_3);
t4 = clutter_score_append (score, timeline_3, timeline_4);
clutter_score_append (score, "line-0", NULL, timeline_1);
clutter_score_append (score, "line-1", timeline_1, timeline_2);
clutter_score_append (score, "line-2", timeline_1, timeline_3);
clutter_score_append (score, "line-3", timeline_3, timeline_4);
clutter_score_append_at_time (score, "line-4", timeline_2, 500, timeline_5);
timelines = clutter_score_list_timelines (score);
g_assert (4 == g_slist_length (timelines));
g_assert (5 == g_slist_length (timelines));
g_slist_free (timelines);
clutter_score_start (score);
@ -96,6 +102,7 @@ main (int argc, char **argv)
g_object_unref (timeline_2);
g_object_unref (timeline_3);
g_object_unref (timeline_4);
g_object_unref (timeline_5);
g_object_unref (score);
return EXIT_SUCCESS;