2007-10-01 Emmanuele Bassi <ebassi@openedhand.com>

* clutter/clutter-actor.[ch]: Remove the ::event-after signal
	class handler, as it's not meant to be overridden by sub-classes.

	(clutter_actor_class_init):
	Make every event-related signal return a boolean: return TRUE
	in a signal handler to block the emission. The value is accumulated
	automatically by the signal API.

	(clutter_actor_event): If ::event returns TRUE, skip to emitting
	::event-after. Return the value accumulated by the signal emission
	chain.

	* clutter/clutter-private.h: Rename _clutter_boolean_accumlator().

	* clutter/clutter-main.c (clutter_do_event): If clutter_actor_event()
	returns TRUE then stop the event emission chain from child to parent.

	* clutter/clutter-stage.c (clutter_stage_event): Behave like
	clutter_actor_event().

	(clutter_stage_get_key_focus),
	(clutter_stage_set_key_focus): Avoid a nasty circular reference
	issue: if the actor passed to set_key_focus is NULL then the stage
	has the key focus.

	* tests/test-events.c: Update the events test with the API
	changes.
This commit is contained in:
Emmanuele Bassi 2007-10-02 14:03:36 +00:00
parent 5b4b51ba7a
commit 090c1d11b4
7 changed files with 196 additions and 133 deletions

View File

@ -1,3 +1,33 @@
2007-10-01 Emmanuele Bassi <ebassi@openedhand.com>
* clutter/clutter-actor.[ch]: Remove the ::event-after signal
class handler, as it's not meant to be overridden by sub-classes.
(clutter_actor_class_init):
Make every event-related signal return a boolean: return TRUE
in a signal handler to block the emission. The value is accumulated
automatically by the signal API.
(clutter_actor_event): If ::event returns TRUE, skip to emitting
::event-after. Return the value accumulated by the signal emission
chain.
* clutter/clutter-private.h: Rename _clutter_boolean_accumlator().
* clutter/clutter-main.c (clutter_do_event): If clutter_actor_event()
returns TRUE then stop the event emission chain from child to parent.
* clutter/clutter-stage.c (clutter_stage_event): Behave like
clutter_actor_event().
(clutter_stage_get_key_focus),
(clutter_stage_set_key_focus): Avoid a nasty circular reference
issue: if the actor passed to set_key_focus is NULL then the stage
has the key focus.
* tests/test-events.c: Update the events test with the API
changes.
2007-10-01 Tomas Frydrych <tf@openedhand.com>
* clutter/clutter-actor.c:

View File

@ -1241,6 +1241,9 @@ clutter_actor_class_init (ClutterActorClass *klass)
* The ::event signal is emitted each time and event is received
* by the @actor.
*
* Return value: %TRUE if the event has been handled by the actor,
* or %FALSE to continue the emission.
*
* Since: 0.6
*/
actor_signals[EVENT] =
@ -1248,9 +1251,9 @@ clutter_actor_class_init (ClutterActorClass *klass)
G_TYPE_FROM_CLASS (object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (ClutterActorClass, event),
NULL, NULL,
clutter_marshal_VOID__BOXED,
G_TYPE_NONE, 1,
_clutter_boolean_handled_accumulator, NULL,
clutter_marshal_BOOLEAN__BOXED,
G_TYPE_BOOLEAN, 1,
CLUTTER_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
/**
* ClutterActor::event-after:
@ -1265,8 +1268,8 @@ clutter_actor_class_init (ClutterActorClass *klass)
actor_signals[EVENT_AFTER] =
g_signal_new ("event-after",
G_TYPE_FROM_CLASS (object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (ClutterActorClass, event_after),
0,
0,
NULL, NULL,
clutter_marshal_VOID__BOXED,
G_TYPE_NONE, 1,
@ -1279,6 +1282,9 @@ clutter_actor_class_init (ClutterActorClass *klass)
* The ::button-press-event signal is emitted each time a mouse button
* is pressed on @actor.
*
* Return value: %TRUE if the event has been handled by the actor,
* or %FALSE to continue the emission.
*
* Since: 0.6
*/
actor_signals[BUTTON_PRESS_EVENT] =
@ -1286,9 +1292,9 @@ clutter_actor_class_init (ClutterActorClass *klass)
G_TYPE_FROM_CLASS (object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (ClutterActorClass, button_press_event),
NULL, NULL,
clutter_marshal_VOID__BOXED,
G_TYPE_NONE, 1,
_clutter_boolean_handled_accumulator, NULL,
clutter_marshal_BOOLEAN__BOXED,
G_TYPE_BOOLEAN, 1,
CLUTTER_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
/**
* ClutterActor::button-release-event:
@ -1298,6 +1304,9 @@ clutter_actor_class_init (ClutterActorClass *klass)
* The ::button-release-event signal is emitted each time a mouse button
* is released on @actor.
*
* Return value: %TRUE if the event has been handled by the actor,
* or %FALSE to continue the emission.
*
* Since: 0.6
*/
actor_signals[BUTTON_RELEASE_EVENT] =
@ -1305,9 +1314,9 @@ clutter_actor_class_init (ClutterActorClass *klass)
G_TYPE_FROM_CLASS (object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (ClutterActorClass, button_release_event),
NULL, NULL,
clutter_marshal_VOID__BOXED,
G_TYPE_NONE, 1,
_clutter_boolean_handled_accumulator, NULL,
clutter_marshal_BOOLEAN__BOXED,
G_TYPE_BOOLEAN, 1,
CLUTTER_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
/**
* ClutterActor::scroll-event:
@ -1317,6 +1326,8 @@ clutter_actor_class_init (ClutterActorClass *klass)
* The ::scroll-event signal is emitted each time a the mouse is
* scrolled on @actor
*
* Return value: %TRUE if the event has been handled by the actor,
* or %FALSE to continue the emission.
*
* Since: 0.6
*/
@ -1325,9 +1336,9 @@ clutter_actor_class_init (ClutterActorClass *klass)
G_TYPE_FROM_CLASS (object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (ClutterActorClass, scroll_event),
NULL, NULL,
clutter_marshal_VOID__BOXED,
G_TYPE_NONE, 1,
_clutter_boolean_handled_accumulator, NULL,
clutter_marshal_BOOLEAN__BOXED,
G_TYPE_BOOLEAN, 1,
CLUTTER_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
/**
* ClutterActor::key-press-event:
@ -1337,6 +1348,9 @@ clutter_actor_class_init (ClutterActorClass *klass)
* The ::key-press-event signal is emitted each time a keyboard button
* is pressed on @actor.
*
* Return value: %TRUE if the event has been handled by the actor,
* or %FALSE to continue the emission.
*
* Since: 0.6
*/
actor_signals[KEY_PRESS_EVENT] =
@ -1344,9 +1358,9 @@ clutter_actor_class_init (ClutterActorClass *klass)
G_TYPE_FROM_CLASS (object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (ClutterActorClass, key_press_event),
NULL, NULL,
clutter_marshal_VOID__BOXED,
G_TYPE_NONE, 1,
_clutter_boolean_handled_accumulator, NULL,
clutter_marshal_BOOLEAN__BOXED,
G_TYPE_BOOLEAN, 1,
CLUTTER_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
/**
* ClutterActor::key-release-event:
@ -1356,6 +1370,9 @@ clutter_actor_class_init (ClutterActorClass *klass)
* The ::key-release-event signal is emitted each time a keyboard button
* is released on @actor.
*
* Return value: %TRUE if the event has been handled by the actor,
* or %FALSE to continue the emission.
*
* Since: 0.6
*/
actor_signals[KEY_RELEASE_EVENT] =
@ -1363,9 +1380,9 @@ clutter_actor_class_init (ClutterActorClass *klass)
G_TYPE_FROM_CLASS (object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (ClutterActorClass, key_release_event),
NULL, NULL,
clutter_marshal_VOID__BOXED,
G_TYPE_NONE, 1,
_clutter_boolean_handled_accumulator, NULL,
clutter_marshal_BOOLEAN__BOXED,
G_TYPE_BOOLEAN, 1,
CLUTTER_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
/**
* ClutterActor::motion-event:
@ -1375,6 +1392,9 @@ clutter_actor_class_init (ClutterActorClass *klass)
* The ::motion-event signal is emitted each time the mouse pointer is
* moved on @actor.
*
* Return value: %TRUE if the event has been handled by the actor,
* or %FALSE to continue the emission.
*
* Since: 0.6
*/
actor_signals[MOTION_EVENT] =
@ -1382,9 +1402,9 @@ clutter_actor_class_init (ClutterActorClass *klass)
G_TYPE_FROM_CLASS (object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (ClutterActorClass, motion_event),
NULL, NULL,
clutter_marshal_VOID__BOXED,
G_TYPE_NONE, 1,
_clutter_boolean_handled_accumulator, NULL,
clutter_marshal_BOOLEAN__BOXED,
G_TYPE_BOOLEAN, 1,
CLUTTER_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
/**
@ -2899,7 +2919,7 @@ gboolean
clutter_actor_event (ClutterActor *actor,
ClutterEvent *event)
{
gboolean res = TRUE;
gboolean retval = TRUE;
gint signal_num = -1;
g_return_val_if_fail (CLUTTER_IS_ACTOR (actor), FALSE);
@ -2907,50 +2927,50 @@ clutter_actor_event (ClutterActor *actor,
g_object_ref (actor);
g_signal_emit (actor, actor_signals[EVENT], 0, event);
switch (event->type)
g_signal_emit (actor, actor_signals[EVENT], 0, event, &retval);
if (!retval)
{
case CLUTTER_NOTHING:
break;
case CLUTTER_BUTTON_PRESS:
case CLUTTER_2BUTTON_PRESS:
case CLUTTER_3BUTTON_PRESS:
signal_num = BUTTON_PRESS_EVENT;
break;
case CLUTTER_BUTTON_RELEASE:
signal_num = BUTTON_RELEASE_EVENT;
break;
case CLUTTER_SCROLL:
signal_num = SCROLL_EVENT;
break;
case CLUTTER_KEY_PRESS:
signal_num = KEY_PRESS_EVENT;
break;
case CLUTTER_KEY_RELEASE:
signal_num = KEY_RELEASE_EVENT;
break;
case CLUTTER_MOTION:
signal_num = MOTION_EVENT;
break;
case CLUTTER_DELETE:
case CLUTTER_DESTROY_NOTIFY:
case CLUTTER_CLIENT_MESSAGE:
default:
signal_num = -1;
break;
switch (event->type)
{
case CLUTTER_NOTHING:
break;
case CLUTTER_BUTTON_PRESS:
case CLUTTER_2BUTTON_PRESS:
case CLUTTER_3BUTTON_PRESS:
signal_num = BUTTON_PRESS_EVENT;
break;
case CLUTTER_BUTTON_RELEASE:
signal_num = BUTTON_RELEASE_EVENT;
break;
case CLUTTER_SCROLL:
signal_num = SCROLL_EVENT;
break;
case CLUTTER_KEY_PRESS:
signal_num = KEY_PRESS_EVENT;
break;
case CLUTTER_KEY_RELEASE:
signal_num = KEY_RELEASE_EVENT;
break;
case CLUTTER_MOTION:
signal_num = MOTION_EVENT;
break;
case CLUTTER_DELETE:
case CLUTTER_DESTROY_NOTIFY:
case CLUTTER_CLIENT_MESSAGE:
default:
signal_num = -1;
break;
}
if (signal_num != -1)
g_signal_emit (actor, actor_signals[signal_num], 0, event, &retval);
}
if (signal_num != -1)
{
g_signal_emit (actor, actor_signals[signal_num], 0, event);
g_signal_emit (actor, actor_signals[EVENT_AFTER], 0, event);
res = TRUE;
}
g_signal_emit (actor, actor_signals[EVENT_AFTER], 0, event);
g_object_unref (actor);
return res;
return retval;
}
void

View File

@ -193,24 +193,22 @@ struct _ClutterActorClass
const ClutterColor *color);
/* event signals */
void (* event) (ClutterActor *actor,
ClutterEvent *event);
void (* event_after) (ClutterActor *actor,
ClutterEvent *event);
void (* button_press_event) (ClutterActor *actor,
ClutterButtonEvent *event);
void (* button_release_event) (ClutterActor *actor,
ClutterButtonEvent *event);
void (* scroll_event) (ClutterActor *actor,
ClutterScrollEvent *event);
void (* key_press_event) (ClutterActor *actor,
ClutterKeyEvent *event);
void (* key_release_event) (ClutterActor *actor,
ClutterKeyEvent *event);
void (* motion_event) (ClutterActor *actor,
ClutterMotionEvent *event);
void (* focus_in) (ClutterActor *actor);
void (* focus_out) (ClutterActor *actor);
gboolean (* event) (ClutterActor *actor,
ClutterEvent *event);
gboolean (* button_press_event) (ClutterActor *actor,
ClutterButtonEvent *event);
gboolean (* button_release_event) (ClutterActor *actor,
ClutterButtonEvent *event);
gboolean (* scroll_event) (ClutterActor *actor,
ClutterScrollEvent *event);
gboolean (* key_press_event) (ClutterActor *actor,
ClutterKeyEvent *event);
gboolean (* key_release_event) (ClutterActor *actor,
ClutterKeyEvent *event);
gboolean (* motion_event) (ClutterActor *actor,
ClutterMotionEvent *event);
void (* focus_in) (ClutterActor *actor);
void (* focus_out) (ClutterActor *actor);
/*< private >*/
/* padding for future expansion */

View File

@ -237,12 +237,14 @@ clutter_do_event (ClutterEvent *event)
g_return_if_fail (actor != NULL);
event->key.source = g_object_ref(actor);
event->key.source = g_object_ref (actor);
/* bubble up */
do
{
clutter_actor_event (actor, event);
if (clutter_actor_event (actor, event))
break;
actor = clutter_actor_get_parent (actor);
}
while (actor != NULL);
@ -252,7 +254,7 @@ clutter_do_event (ClutterEvent *event)
if (context->motion_events_per_actor == FALSE)
{
/* Only stage gets motion events */
event->motion.source = g_object_ref(stage);
event->motion.source = g_object_ref (stage);
clutter_actor_event (stage, event);
break;
}
@ -270,10 +272,9 @@ clutter_do_event (ClutterEvent *event)
/* Safety on - probably a release off stage ?
* FIXME: should likely deliver the release somehow - grabs ?
*/
if (x > CLUTTER_STAGE_WIDTH()
|| y > CLUTTER_STAGE_HEIGHT()
|| x < 0
|| y < 0)
if (x > CLUTTER_STAGE_WIDTH () ||
y > CLUTTER_STAGE_HEIGHT() ||
x < 0 || y < 0)
break;
/* Map the event to a reactive actor */
@ -285,9 +286,9 @@ clutter_do_event (ClutterEvent *event)
x, y, actor);
if (event->type == CLUTTER_SCROLL)
event->scroll.source = g_object_ref(actor);
event->scroll.source = g_object_ref (actor);
else
event->button.source = g_object_ref(actor);
event->button.source = g_object_ref (actor);
/* Motion enter leave events */
if (event->type == CLUTTER_MOTION)
@ -317,7 +318,8 @@ clutter_do_event (ClutterEvent *event)
clutter_actor_get_parent (actor) == NULL /* STAGE */ )
{
CLUTTER_NOTE (EVENT, "forwarding event to reactive actor");
clutter_actor_event (actor, event);
if (clutter_actor_event (actor, event))
break;
}
actor = clutter_actor_get_parent (actor);
@ -326,14 +328,14 @@ clutter_do_event (ClutterEvent *event)
break;
case CLUTTER_STAGE_STATE:
/* fullscreen / focus - forward to stage */
clutter_stage_event (CLUTTER_STAGE(stage), event);
clutter_stage_event (CLUTTER_STAGE (stage), event);
break;
case CLUTTER_CLIENT_MESSAGE:
break;
}
}
ClutterActor*
ClutterActor *
_clutter_do_pick (ClutterStage *stage,
gint x,
gint y,
@ -369,7 +371,7 @@ _clutter_do_pick (ClutterStage *stage,
glReadPixels(x, viewport[3] - y, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixel);
if (pixel[0] == 0xff && pixel[1] == 0xff && pixel[2] == 0xff)
return CLUTTER_ACTOR(stage);
return CLUTTER_ACTOR (stage);
cogl_get_bitmasks (&r, &g, &b, NULL);
@ -1196,10 +1198,10 @@ clutter_init (int *argc,
}
gboolean
_clutter_boolean_accumulator (GSignalInvocationHint *ihint,
GValue *return_accu,
const GValue *handler_return,
gpointer dummy)
_clutter_boolean_handled_accumulator (GSignalInvocationHint *ihint,
GValue *return_accu,
const GValue *handler_return,
gpointer dummy)
{
gboolean continue_emission;
gboolean signal_handled;

View File

@ -97,12 +97,6 @@ ClutterMainContext *clutter_context_get_default (void);
#define CLUTTER_PARAM_READWRITE \
G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK |G_PARAM_STATIC_BLURB
/* signal accumulators */
gboolean _clutter_boolean_accumulator (GSignalInvocationHint *ihint,
GValue *return_accu,
const GValue *handler_return,
gpointer dummy);
/* vfuncs implemnted by backend */
GType _clutter_backend_impl_get_type (void);
@ -129,11 +123,20 @@ void _clutter_event_button_generate (ClutterBackend *backend,
void _clutter_feature_init (void);
ClutterActor* _clutter_do_pick (ClutterStage *stage,
ClutterActor *_clutter_do_pick (ClutterStage *stage,
gint x,
gint y,
ClutterPickMode mode);
/* use this function as the accumulator if you have a signal with
* a G_TYPE_BOOLEAN return value; this will stop the emission as
* soon as one handler returns TRUE
*/
gboolean _clutter_boolean_handled_accumulator (GSignalInvocationHint *ihint,
GValue *return_accu,
const GValue *handler_return,
gpointer dummy);
/* Does this need to be private ? */
void clutter_do_event (ClutterEvent *event);

View File

@ -397,8 +397,7 @@ clutter_stage_init (ClutterStage *self)
clutter_actor_set_size (CLUTTER_ACTOR (self), 640, 480);
clutter_actor_set_reactive (CLUTTER_ACTOR (self));
clutter_stage_set_key_focus (self, CLUTTER_ACTOR (self));
clutter_stage_set_key_focus (self, NULL);
}
/**
@ -830,7 +829,8 @@ clutter_stage_event (ClutterStage *stage,
return FALSE;
/* emit raw event */
clutter_actor_event (CLUTTER_ACTOR(stage), event);
if (clutter_actor_event (CLUTTER_ACTOR (stage), event))
return TRUE;
if (event->stage_state.changed_mask & CLUTTER_STAGE_STATE_FULLSCREEN)
{
@ -904,7 +904,7 @@ on_key_focused_weak_notify (gpointer data,
GObject *where_the_object_was)
{
ClutterStagePrivate *priv;
ClutterStage *stage = CLUTTER_STAGE(data);
ClutterStage *stage = CLUTTER_STAGE (data);
g_return_if_fail (CLUTTER_IS_STAGE (stage));
@ -914,12 +914,12 @@ on_key_focused_weak_notify (gpointer data,
/* focused actor has dissapeared - fall back to stage
* FIXME: need some kind of signal dance/block here.
*/
clutter_stage_set_key_focus (stage, CLUTTER_ACTOR(stage));
clutter_stage_set_key_focus (stage, NULL);
}
void
clutter_stage_set_key_focus (ClutterStage *stage,
ClutterActor *actor)
clutter_stage_set_key_focus (ClutterStage *stage,
ClutterActor *actor)
{
ClutterStagePrivate *priv;
@ -933,33 +933,38 @@ clutter_stage_set_key_focus (ClutterStage *stage,
if (priv->key_focused_actor)
{
g_object_weak_unref (G_OBJECT(priv->key_focused_actor),
g_object_weak_unref (G_OBJECT (priv->key_focused_actor),
on_key_focused_weak_notify,
stage);
g_signal_emit_by_name (G_OBJECT(priv->key_focused_actor), "focus-out");
g_signal_emit_by_name (priv->key_focused_actor, "focus-out");
priv->key_focused_actor = NULL;
}
priv->key_focused_actor = actor;
else
g_signal_emit_by_name (stage, "focus-out");
if (actor)
{
g_object_weak_ref (G_OBJECT(actor),
priv->key_focused_actor = actor;
g_object_weak_ref (G_OBJECT (actor),
on_key_focused_weak_notify,
stage);
g_signal_emit_by_name (G_OBJECT(actor), "focus-in");
g_signal_emit_by_name (priv->key_focused_actor, "focus-in");
}
else
g_signal_emit_by_name (stage, "focus-in");
}
ClutterActor*
clutter_stage_get_key_focus (ClutterStage *stage)
ClutterActor *
clutter_stage_get_key_focus (ClutterStage *stage)
{
ClutterStagePrivate *priv;
g_return_val_if_fail (CLUTTER_IS_STAGE (stage), NULL);
priv = stage->priv;
if (stage->priv->key_focused_actor)
return stage->priv->key_focused_actor;
return priv->key_focused_actor;
return CLUTTER_ACTOR (stage);
}

View File

@ -11,7 +11,7 @@ stage_state_cb (ClutterStage *stage,
printf("[stage signal] %s\n", detail);
}
static void
static gboolean
blue_button_cb (ClutterActor *actor,
ClutterEvent *event,
gpointer data)
@ -26,9 +26,11 @@ blue_button_cb (ClutterActor *actor,
IsFullScreen = TRUE;
g_object_set (stage, "fullscreen", IsFullScreen, NULL);
return FALSE;
}
void
static void
key_focus_in_cb (ClutterActor *actor,
gpointer data)
{
@ -40,7 +42,6 @@ key_focus_in_cb (ClutterActor *actor,
}
else
{
clutter_actor_show (focus_box);
clutter_actor_set_position (focus_box,
clutter_actor_get_x (actor) - 5,
clutter_actor_get_y (actor) - 5);
@ -48,15 +49,16 @@ key_focus_in_cb (ClutterActor *actor,
clutter_actor_set_size (focus_box,
clutter_actor_get_width (actor) + 10,
clutter_actor_get_height (actor) + 10);
clutter_actor_show (focus_box);
}
}
static void
static gboolean
input_cb (ClutterActor *actor,
ClutterEvent *event,
gpointer data)
{
ClutterStage *stage = CLUTTER_STAGE (clutter_stage_get_default ());
gchar keybuf[9], *source = (gchar*)data;
int len = 0;
@ -88,9 +90,10 @@ input_cb (ClutterActor *actor,
break;
case CLUTTER_BUTTON_RELEASE:
printf("[%s] BUTTON RELEASE", source);
if (clutter_event_get_source (event) == actor)
clutter_stage_set_key_focus
(CLUTTER_STAGE(clutter_stage_get_default ()), actor);
if (clutter_event_get_source (event) == CLUTTER_ACTOR (stage))
clutter_stage_set_key_focus (stage, NULL);
else if (clutter_event_get_source (event) == actor)
clutter_stage_set_key_focus (stage, actor);
break;
case CLUTTER_SCROLL:
printf("[%s] BUTTON SCROLL", source);
@ -113,8 +116,10 @@ input_cb (ClutterActor *actor,
if (clutter_event_get_source (event) == actor)
printf(" *source*");
printf("\n");
return FALSE;
}
int
@ -148,7 +153,7 @@ main (int argc, char *argv[])
clutter_actor_set_reactive (actor);
clutter_container_add (CLUTTER_CONTAINER(stage), actor, NULL);
clutter_container_add (CLUTTER_CONTAINER (stage), actor, NULL);
g_signal_connect (actor, "event", G_CALLBACK (input_cb), "red box");
g_signal_connect (actor, "focus-in", G_CALLBACK (key_focus_in_cb),
@ -162,7 +167,7 @@ main (int argc, char *argv[])
clutter_actor_set_reactive (actor);
clutter_container_add (CLUTTER_CONTAINER(stage), actor, NULL);
clutter_container_add (CLUTTER_CONTAINER (stage), actor, NULL);
g_signal_connect (actor, "event", G_CALLBACK (input_cb), "green box");
g_signal_connect (actor, "focus-in", G_CALLBACK (key_focus_in_cb),