diff --git a/ChangeLog b/ChangeLog index 09a02a8ee..c59992ee2 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,14 @@ +2006-10-23 Matthew Allum + + * clutter/clutter-alpha.h: + * clutter/clutter-behaviour.c: + * clutter/clutter-behaviour.h: + * clutter/clutter-behaviours.c: + * clutter/clutter-behaviours.h: + * examples/behave.c: + Behaviours now only 'driven' by ClutterAlpha, not any object/prop. + Add simple Clutter path behaviour. + 2006-10-03 Matthew Allum * configure.ac: diff --git a/clutter/clutter-alpha.h b/clutter/clutter-alpha.h index 8ebc849cb..142aed529 100644 --- a/clutter/clutter-alpha.h +++ b/clutter/clutter-alpha.h @@ -80,8 +80,11 @@ struct _ClutterAlphaClass void (*_clutter_alpha_5) (void); }; + #define CLUTTER_ALPHA_MAX_ALPHA 0xffff +GType clutter_alpha_get_type (void) G_GNUC_CONST; + ClutterAlpha * clutter_alpha_new (ClutterTimeline *timeline, ClutterAlphaFunc func); diff --git a/clutter/clutter-behaviour.c b/clutter/clutter-behaviour.c index 3e100d2b7..dc93621e0 100644 --- a/clutter/clutter-behaviour.c +++ b/clutter/clutter-behaviour.c @@ -33,31 +33,26 @@ #include "clutter-actor.h" #include "clutter-behaviour.h" -#include "clutter-marshal.h" G_DEFINE_TYPE (ClutterBehaviour, clutter_behaviour, G_TYPE_OBJECT); struct ClutterBehaviourPrivate { - GObject *object; - GParamSpec *param_spec; - guint notify_id; - GSList *actors; + ClutterAlpha *alpha; + guint notify_id; + GSList *actors; }; enum { PROP_0, - PROP_OBJECT, - PROP_PROPERTY + PROP_ALPHA }; enum { - SIGNAL_PROPERTY_CHANGE, SIGNAL_LAST }; -static guint signals[SIGNAL_LAST]; #define CLUTTER_BEHAVIOUR_GET_PRIVATE(obj) \ (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \ @@ -73,7 +68,7 @@ _clutter_behaviour_dispose (GObject *object) { /* FIXME: remove all actors */ - clutter_behaviour_set_object (self, NULL); + clutter_behaviour_set_alpha (self, NULL); } G_OBJECT_CLASS (clutter_behaviour_parent_class)->dispose (object); @@ -105,11 +100,8 @@ _clutter_behaviour_set_property (GObject *object, switch (prop_id) { - case PROP_OBJECT: - clutter_behaviour_set_object (behaviour, g_value_get_object (value)); - break; - case PROP_PROPERTY: - clutter_behaviour_set_property (behaviour, g_value_get_string (value)); + case PROP_ALPHA: + clutter_behaviour_set_alpha (behaviour, g_value_get_object (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); @@ -131,11 +123,8 @@ _clutter_behaviour_get_property (GObject *object, switch (prop_id) { - case PROP_OBJECT: - g_value_set_object (value, priv->object); - break; - case PROP_PROPERTY: - g_value_set_string (value, priv->param_spec->name); + case PROP_ALPHA: + g_value_set_object (value, priv->alpha); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); @@ -157,31 +146,13 @@ clutter_behaviour_class_init (ClutterBehaviourClass *klass) object_class->get_property = _clutter_behaviour_get_property; g_object_class_install_property - (object_class, PROP_OBJECT, - g_param_spec_object ("object", - "Object", - "Object whose property to monitor", - G_TYPE_OBJECT, + (object_class, PROP_ALPHA, + g_param_spec_object ("alpha", + "Alpha", + "Alpha Object to drive the behaviour", + CLUTTER_TYPE_ALPHA, G_PARAM_CONSTRUCT | G_PARAM_READWRITE)); - g_object_class_install_property - (object_class, PROP_PROPERTY, - g_param_spec_string ("property", - "Property", - "Property to monitor", - NULL, - G_PARAM_CONSTRUCT | G_PARAM_READWRITE)); - - signals[SIGNAL_PROPERTY_CHANGE] = - g_signal_new ("property-change", - G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (ClutterBehaviourClass, property_change), - NULL, NULL, - clutter_marshal_VOID__OBJECT_POINTER, - G_TYPE_NONE, - 2, G_TYPE_OBJECT, G_TYPE_POINTER); - g_type_class_add_private (object_class, sizeof (ClutterBehaviourPrivate)); } @@ -246,54 +217,10 @@ clutter_behaviour_actors_foreach (ClutterBehaviour *behave, g_slist_foreach (behave->priv->actors, func, userdata); } -GObject* -clutter_behaviour_get_object (ClutterBehaviour *behave) +ClutterAlpha* +clutter_behaviour_get_alpha (ClutterBehaviour *behave) { - return behave->priv->object; -} - -void -clutter_behaviour_set_object (ClutterBehaviour *behave, - GObject *object) -{ - ClutterBehaviourPrivate *priv; - const char *property; - - priv = CLUTTER_BEHAVIOUR_GET_PRIVATE(behave); - - if (priv->object) - { - property = clutter_behaviour_get_property (behave); - clutter_behaviour_set_property (behave, NULL); - - g_object_unref(priv->object); - priv->object = NULL; - } - else - property = NULL; - - if (object) - { - priv->object = g_object_ref(object); - - if (property) - clutter_behaviour_set_property (behave, property); - } -} - -const char * -clutter_behaviour_get_property (ClutterBehaviour *behave) -{ - if (behave->priv->param_spec) - return behave->priv->param_spec->name; - else - return NULL; -} - -GParamSpec * -clutter_behaviour_get_param_spec (ClutterBehaviour *behave) -{ - return behave->priv->param_spec; + return behave->priv->alpha; } static void @@ -301,47 +228,39 @@ notify_cb (GObject *object, GParamSpec *param_spec, ClutterBehaviour *behave) { - g_signal_emit (behave, - signals[SIGNAL_PROPERTY_CHANGE], - 0, - object, - param_spec); + ClutterBehaviourClass *class; + + class = CLUTTER_BEHAVIOUR_GET_CLASS(behave); + + if (class->alpha_notify) + class->alpha_notify (behave); } void -clutter_behaviour_set_property (ClutterBehaviour *behave, - const char *property) +clutter_behaviour_set_alpha (ClutterBehaviour *behave, + ClutterAlpha *alpha) { - g_return_if_fail (behave->priv->object); - if (behave->priv->notify_id) { - g_signal_handler_disconnect (behave->priv->object, + g_signal_handler_disconnect (behave->priv->alpha, behave->priv->notify_id); behave->priv->notify_id = 0; } - behave->priv->param_spec = NULL; - - if (property) + if (behave->priv->alpha) { - guint signal_id; - GClosure *closure; + g_object_unref (behave->priv->alpha); + behave->priv->alpha = NULL; + } - behave->priv->param_spec = - g_object_class_find_property (G_OBJECT_GET_CLASS (behave->priv->object), - property); - g_return_if_fail (behave->priv->param_spec); + if (alpha) + { + behave->priv->alpha = alpha; + g_object_ref (behave->priv->alpha); - signal_id = g_signal_lookup ("notify", - G_OBJECT_TYPE (behave->priv->object)); - closure = g_cclosure_new ((GCallback) notify_cb, behave, NULL); - - behave->priv->notify_id = - g_signal_connect_closure_by_id (behave->priv->object, - signal_id, - g_quark_from_string (property), - closure, - FALSE); + behave->priv->notify_id = g_signal_connect (behave->priv->alpha, + "notify::alpha", + G_CALLBACK(notify_cb), + behave); } } diff --git a/clutter/clutter-behaviour.h b/clutter/clutter-behaviour.h index f426d53cc..d83cbc1d3 100644 --- a/clutter/clutter-behaviour.h +++ b/clutter/clutter-behaviour.h @@ -2,6 +2,7 @@ #define _HAVE_CLUTTER_BEHAVIOUR_H #include +#include "clutter-alpha.h" G_BEGIN_DECLS @@ -41,9 +42,7 @@ struct _ClutterBehaviourClass { GObjectClass parent_class; - void (* property_change) (ClutterBehaviour *behave, - GObject *object, - GParamSpec *param_spec); + void (*alpha_notify) (ClutterBehaviour *behave); }; GType clutter_behaviour_get_type (void); @@ -66,22 +65,12 @@ clutter_behaviour_actors_foreach (ClutterBehaviour *behave, GFunc func, gpointer userdata); -void -clutter_behaviour_set_object (ClutterBehaviour *behave, - GObject *object); - -GObject* -clutter_behaviour_get_object (ClutterBehaviour *behave); +ClutterAlpha* +clutter_behaviour_get_alpha (ClutterBehaviour *behave); void -clutter_behaviour_set_property (ClutterBehaviour *behave, - const char *property); - -const char* -clutter_behaviour_get_property (ClutterBehaviour *behave); - -GParamSpec* -clutter_behaviour_get_param_spec (ClutterBehaviour *behave); +clutter_behaviour_set_alpha (ClutterBehaviour *behave, + ClutterAlpha *alpha); G_END_DECLS diff --git a/clutter/clutter-behaviours.c b/clutter/clutter-behaviours.c index 35e225689..47a497379 100644 --- a/clutter/clutter-behaviours.c +++ b/clutter/clutter-behaviours.c @@ -37,13 +37,50 @@ #include "clutter-enum-types.h" #include "clutter-main.h" +#include + +static ClutterKnot * +clutter_knot_copy (const ClutterKnot *knot) +{ + ClutterKnot *copy; + + copy = g_slice_new0 (ClutterKnot); + + *copy = *knot; + + return copy; +} + +static void +clutter_knot_free (ClutterKnot *knot) +{ + if (G_LIKELY (knot)) + { + g_slice_free (ClutterKnot, knot); + } +} + +GType +clutter_knot_get_type (void) +{ + static GType our_type = 0; + + if (G_UNLIKELY (!our_type)) + our_type = g_boxed_type_register_static + ("ClutterKnot", + (GBoxedCopyFunc) clutter_knot_copy, + (GBoxedFreeFunc) clutter_knot_free); + return our_type; +} + + G_DEFINE_TYPE (ClutterBehaviourPath, \ clutter_behaviour_path, \ CLUTTER_TYPE_BEHAVIOUR); struct ClutterBehaviourPathPrivate { - gint x1, y1, x2, y2; + GSList *knots; }; #define CLUTTER_BEHAVIOUR_PATH_GET_PRIVATE(obj) \ @@ -51,6 +88,9 @@ struct ClutterBehaviourPathPrivate CLUTTER_TYPE_BEHAVIOUR_PATH, \ ClutterBehaviourPathPrivate)) +static void +clutter_behaviour_path_alpha_notify (ClutterBehaviour *behave); + static void clutter_behaviour_path_dispose (GObject *object) { @@ -58,7 +98,7 @@ clutter_behaviour_path_dispose (GObject *object) if (self->priv) { - /* FIXME: remove all actors */ + /* FIXME: unref knots */ } G_OBJECT_CLASS (clutter_behaviour_path_parent_class)->dispose (object); @@ -82,13 +122,17 @@ clutter_behaviour_path_finalize (GObject *object) static void clutter_behaviour_path_class_init (ClutterBehaviourPathClass *klass) { - GObjectClass *object_class; + GObjectClass *object_class; + ClutterBehaviourClass *behave_class; object_class = (GObjectClass*) klass; + behave_class = (ClutterBehaviourClass*) klass; object_class->finalize = clutter_behaviour_path_finalize; object_class->dispose = clutter_behaviour_path_dispose; + behave_class->alpha_notify = clutter_behaviour_path_alpha_notify; + g_type_class_add_private (object_class, sizeof (ClutterBehaviourPathPrivate)); } @@ -100,48 +144,218 @@ clutter_behaviour_path_init (ClutterBehaviourPath *self) self->priv = priv = CLUTTER_BEHAVIOUR_PATH_GET_PRIVATE (self); } -/* +static void +interpolate (const ClutterKnot *begin, + const ClutterKnot *end, + ClutterKnot *out, + double t) +{ + /* FIXME: fixed point */ + out->x = begin->x + t * (end->x - begin->x); + out->y = begin->y + t * (end->y - begin->y); +} -function line(x0, x1, y0, y1) - boolean steep := abs(y1 - y0) > abs(x1 - x0) - if steep then - swap(x0, y0) - swap(x1, y1) - if x0 > x1 then - swap(x0, x1) - swap(y0, y1) - int deltax := x1 - x0 - int deltay := abs(y1 - y0) - int error := 0 - int ystep - int y := y0 - if y0 < y1 then ystep := 1 else ystep := -1 - for x from x0 to x1 - if steep then plot(y,x) else plot(x,y) - error := error + deltay - if 2 +static gint +node_distance (const ClutterKnot *begin, const ClutterKnot *end) +{ + g_return_val_if_fail (begin != NULL, 0); + g_return_val_if_fail (end != NULL, 0); - */ + /* FIXME: need fixed point here */ + return sqrt ((end->x - begin->x) * (end->x - begin->x) + + (end->y - begin->y) * (end->y - begin->y)); +} + +static gint +path_total_length (ClutterBehaviourPath *behave) +{ + GSList *l; + gint len = 0; + + for (l = behave->priv->knots; l != NULL; l = l->next) + if (l->next) + len += node_distance (l->data, l->next->data); + + return len; +} + +static void +actor_apply_knot_foreach (ClutterActor *actor, + ClutterKnot *knot) +{ + clutter_actor_set_position (actor, knot->x, knot->y); +} + +static void +path_alpha_to_position (ClutterBehaviourPath *behave) +{ + guint32 alpha; + GSList *l; + gint total_len, offset, dist_to_next, dist = 0; + + /* FIXME: Optimise. Much of the data used here can be pre-generated + * ( total_len, dist between knots ) when knots are added/removed. + */ + + /* Calculation as follows: + * o Get total length of path + * o Find the offset on path where alpha val corresponds to + * o Figure out between which knots this offset lies. + * o Interpolate new co-ords via dist between these knots + * o Apply to actors. + */ + + alpha = clutter_alpha_get_alpha + (clutter_behaviour_get_alpha (CLUTTER_BEHAVIOUR(behave))); + + total_len = path_total_length (behave); + + offset = (alpha * total_len) / CLUTTER_ALPHA_MAX_ALPHA; + + if (offset == 0) + { + clutter_behaviour_actors_foreach (CLUTTER_BEHAVIOUR(behave), + (GFunc)actor_apply_knot_foreach, + behave->priv->knots->data); + return; + } + + for (l = behave->priv->knots; l != NULL; l = l->next) + if (l->next) + { + dist_to_next = node_distance (l->data, l->next->data); + + if (offset >= dist && offset < (dist + dist_to_next)) + { + ClutterKnot new; + double t; + + /* FIXME: Use fixed */ + t = (double)(offset - dist) / dist_to_next ; + + interpolate (l->data, l->next->data, &new, t); + + clutter_behaviour_actors_foreach (CLUTTER_BEHAVIOUR(behave), + (GFunc)actor_apply_knot_foreach, + &new); + return; + } + + dist += dist_to_next; + } +} + +static void +clutter_behaviour_path_alpha_notify (ClutterBehaviour *behave) +{ + path_alpha_to_position (CLUTTER_BEHAVIOUR_PATH(behave)); +} ClutterBehaviour* -clutter_behaviour_path_new (GObject *object, - const char *property, - gint x1, - gint y1, - gint x2, - gint y2) +clutter_behaviour_path_new (ClutterAlpha *alpha, + const ClutterKnot *knots, + guint n_knots) { ClutterBehaviourPath *behave; - + gint i; + behave = g_object_new (CLUTTER_TYPE_BEHAVIOUR_PATH, - "object", object, - "property", property, + "alpha", alpha, NULL); + for (i = 0; i < n_knots; i++) + { + ClutterKnot knot = knots[i]; + clutter_path_behaviour_append_knot (behave, &knot); + } + return CLUTTER_BEHAVIOUR(behave); } -/* opacity */ +GSList* +clutter_path_behaviour_get_knots (ClutterBehaviourPath *behave) +{ + GSList *retval, *l; + + g_return_val_if_fail (CLUTTER_IS_BEHAVIOUR_PATH (behave), NULL); + + retval = NULL; + for (l = behave->priv->knots; l != NULL; l = l->next) + retval = g_slist_prepend (retval, l->data); + + return g_slist_reverse (retval); +} + +void +clutter_path_behaviour_append_knot (ClutterBehaviourPath *pathb, + const ClutterKnot *knot) +{ + ClutterBehaviourPathPrivate *priv; + + g_return_if_fail (knot != NULL); + + priv = pathb->priv; + + priv->knots = g_slist_append (priv->knots, + clutter_knot_copy (knot)); +} + +void +clutter_path_behaviour_append_knots_valist (ClutterBehaviourPath *pathb, + const ClutterKnot *first_knot, + va_list args) +{ + const ClutterKnot * knot; + + knot = first_knot; + while (knot) + { + clutter_path_behaviour_append_knot (pathb, knot); + knot = va_arg (args, ClutterKnot*); + } +} + +void +clutter_path_behavior_append_knots (ClutterBehaviourPath *pathb, + const ClutterKnot *first_knot, + ...) +{ + va_list args; + + g_return_if_fail (first_knot != NULL); + + va_start (args, first_knot); + clutter_path_behaviour_append_knots_valist (pathb, first_knot, args); + va_end (args); +} + +void +clutter_path_behavior_remove_knot (ClutterBehaviourPath *behave, + guint index) +{ + /* FIXME: implement */ +} + +ClutterKnot* +clutter_path_behavior_get_knot (ClutterBehaviourPath *behave, + guint index) +{ + /* FIXME: implement */ +} + +void +clutter_path_behavior_insert_knot (ClutterBehaviourPath *behave, + ClutterKnot *knot, + guint index) +{ + /* FIXME: implement */ +} + + +/* + * ====================== Opacity ============================ + */ + G_DEFINE_TYPE (ClutterBehaviourOpacity, \ clutter_behaviour_opacity, \ @@ -166,20 +380,14 @@ clutter_behaviour_opacity_frame_foreach (ClutterActor *actor, guint8 opacity; ClutterBehaviourOpacityPrivate *priv; ClutterBehaviour *_behave; - GParamSpec *pspec; priv = CLUTTER_BEHAVIOUR_OPACITY_GET_PRIVATE (behave); _behave = CLUTTER_BEHAVIOUR (behave); - pspec = clutter_behaviour_get_param_spec (_behave); - - g_object_get (clutter_behaviour_get_object (_behave), - pspec->name, - &alpha, - NULL); + alpha = clutter_alpha_get_alpha (clutter_behaviour_get_alpha (_behave)); opacity = (alpha * (priv->opacity_end - priv->opacity_start)) - / ((GParamSpecUInt *) pspec)->maximum; + / CLUTTER_ALPHA_MAX_ALPHA; opacity += priv->opacity_start; @@ -189,12 +397,8 @@ clutter_behaviour_opacity_frame_foreach (ClutterActor *actor, } static void -clutter_behaviour_property_change (ClutterBehaviour *behave, - GObject *object, - GParamSpec *param_spec) +clutter_behaviour_alpha_notify (ClutterBehaviour *behave) { - g_return_if_fail (param_spec->value_type == G_TYPE_UINT); - clutter_behaviour_actors_foreach (behave, (GFunc)clutter_behaviour_opacity_frame_foreach, @@ -235,7 +439,7 @@ clutter_behaviour_opacity_class_init (ClutterBehaviourOpacityClass *klass) behave_class = (ClutterBehaviourClass*) klass; - behave_class->property_change = clutter_behaviour_property_change; + behave_class->alpha_notify = clutter_behaviour_alpha_notify; g_type_class_add_private (object_class, sizeof (ClutterBehaviourOpacityPrivate)); } @@ -249,16 +453,14 @@ clutter_behaviour_opacity_init (ClutterBehaviourOpacity *self) } ClutterBehaviour* -clutter_behaviour_opacity_new (GObject *object, - const char *property, - guint8 opacity_start, - guint8 opacity_end) +clutter_behaviour_opacity_new (ClutterAlpha *alpha, + guint8 opacity_start, + guint8 opacity_end) { ClutterBehaviourOpacity *behave; behave = g_object_new (CLUTTER_TYPE_BEHAVIOUR_OPACITY, - "object", object, - "property", property, + "alpha", alpha, NULL); behave->priv->opacity_start = opacity_start; @@ -267,13 +469,3 @@ clutter_behaviour_opacity_new (GObject *object, return CLUTTER_BEHAVIOUR(behave); } -ClutterBehaviour* -clutter_behaviour_opacity_new_from_alpha (ClutterAlpha *alpha, - guint8 opacity_start, - guint8 opacity_end) -{ - return clutter_behaviour_opacity_new (G_OBJECT (alpha), - "alpha", - opacity_start, - opacity_end); -} diff --git a/clutter/clutter-behaviours.h b/clutter/clutter-behaviours.h index 8e5743efd..3eda56ef0 100644 --- a/clutter/clutter-behaviours.h +++ b/clutter/clutter-behaviours.h @@ -7,6 +7,14 @@ G_BEGIN_DECLS +typedef struct _ClutterKnot ClutterKnot; + +struct _ClutterKnot +{ + gint x,y; + /* FIXME: optionally include bezier control points also ? */ +}; + #define CLUTTER_TYPE_BEHAVIOUR_PATH clutter_behaviour_path_get_type() #define CLUTTER_BEHAVIOUR_PATH(obj) \ @@ -47,12 +55,26 @@ struct _ClutterBehaviourPathClass GType clutter_behaviour_path_get_type (void); ClutterBehaviour* -clutter_behaviour_path_new (GObject *object, - const char *property, - gint x1, - gint y1, - gint x2, - gint y2); +clutter_behaviour_path_new (ClutterAlpha *alpha, + const ClutterKnot *knots, + guint n_knots); + +GSList* +clutter_path_behaviour_get_knots (ClutterBehaviourPath *behave); + +void +clutter_path_behaviour_append_knot (ClutterBehaviourPath *pathb, + const ClutterKnot *knot); + +void +clutter_path_behaviour_append_knots_valist (ClutterBehaviourPath *pathb, + const ClutterKnot *first_knot, + va_list args); + +void +clutter_path_behavior_append_knots (ClutterBehaviourPath *pathb, + const ClutterKnot *first_knot, + ...); /* opacity */ @@ -96,15 +118,9 @@ struct _ClutterBehaviourOpacityClass GType clutter_behaviour_opacity_get_type (void); ClutterBehaviour* -clutter_behaviour_opacity_new (GObject *object, - const char *property, - guint8 opacity_start, - guint8 opacity_end); - -ClutterBehaviour* -clutter_behaviour_opacity_new_from_alpha (ClutterAlpha *alpha, - guint8 opacity_start, - guint8 opacity_end); +clutter_behaviour_opacity_new (ClutterAlpha *alpha, + guint8 opacity_start, + guint8 opacity_end); G_END_DECLS diff --git a/examples/behave.c b/examples/behave.c index 666cd8629..269137b30 100644 --- a/examples/behave.c +++ b/examples/behave.c @@ -10,6 +10,8 @@ main (int argc, char *argv[]) ClutterColor stage_color = { 0xcc, 0xcc, 0xcc, 0xff }; GdkPixbuf *pixbuf; + ClutterKnot knots[] = {{ 100, 100 }, { 100, 200 }, { 200, 200 }, + { 200, 100 }, {100, 100 }}; clutter_init (&argc, &argv); @@ -29,19 +31,23 @@ main (int argc, char *argv[]) clutter_group_add (CLUTTER_GROUP (stage), hand); /* Make a timeline */ - timeline = clutter_timeline_new (100, 30); /* num frames, fps */ + timeline = clutter_timeline_new (100, 60); /* num frames, fps */ g_object_set(timeline, "loop", TRUE, 0); /* Set an alpha func to power behaviour - ramp is constant rise/fall */ alpha = clutter_alpha_new (timeline, CLUTTER_ALPHA_RAMP); - /* Create a behaviour for that time line */ - behave = clutter_behaviour_opacity_new_from_alpha (alpha, 0X33, 0xff); + /* Create a behaviour for that alpha */ + behave = clutter_behaviour_opacity_new (alpha, 0X33, 0xff); /* Apply it to our actor */ clutter_behaviour_apply (behave, hand); - /* start the timeline */ + /* Make a path behaviour and apply that too */ + behave = clutter_behaviour_path_new (alpha, knots, 5); + clutter_behaviour_apply (behave, hand); + + /* start the timeline and thus the animations */ clutter_timeline_start (timeline); clutter_group_show_all (CLUTTER_GROUP (stage));