2007-10-10 Matthew Allum <mallum@openedhand.com>

* clutter/clutter-actor.c:
        * clutter/clutter-actor.h:
        * clutter/clutter-event.c:
        * clutter/clutter-main.c:
        * tests/test-events.c:
        Add basic W3 DOM  event 'capture' like functionality.
This commit is contained in:
Matthew Allum 2007-10-10 13:04:34 +00:00
parent a4b7abd18e
commit 6216ce659c
7 changed files with 178 additions and 100 deletions

View File

@ -1,3 +1,12 @@
2007-10-10 Matthew Allum <mallum@openedhand.com>
* clutter/clutter-actor.c:
* clutter/clutter-actor.h:
* clutter/clutter-event.c:
* clutter/clutter-main.c:
* tests/test-events.c:
Add basic W3 DOM event 'capture' like functionality.
2007-10-10 Emmanuele Bassi <ebassi@openedhand.com> 2007-10-10 Emmanuele Bassi <ebassi@openedhand.com>
* clutter/clutter-script-private.h: * clutter/clutter-script-private.h:

View File

@ -106,6 +106,7 @@ enum
PARENT_SET, PARENT_SET,
EVENT, EVENT,
EVENT_CAPTURED,
EVENT_AFTER, EVENT_AFTER,
BUTTON_PRESS_EVENT, BUTTON_PRESS_EVENT,
BUTTON_RELEASE_EVENT, BUTTON_RELEASE_EVENT,
@ -117,7 +118,6 @@ enum
FOCUS_OUT, FOCUS_OUT,
ENTER_EVENT, ENTER_EVENT,
LEAVE_EVENT, LEAVE_EVENT,
LAST_SIGNAL LAST_SIGNAL
}; };
@ -1469,9 +1469,10 @@ clutter_actor_class_init (ClutterActorClass *klass)
G_TYPE_FROM_CLASS (object_class), G_TYPE_FROM_CLASS (object_class),
G_SIGNAL_RUN_LAST, G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (ClutterActorClass, enter), G_STRUCT_OFFSET (ClutterActorClass, enter),
NULL, NULL, _clutter_boolean_handled_accumulator, NULL,
clutter_marshal_VOID__VOID, clutter_marshal_BOOLEAN__BOXED,
G_TYPE_NONE, 0); G_TYPE_BOOLEAN, 1,
CLUTTER_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
/** /**
* ClutterActor::leave: * ClutterActor::leave:
@ -1486,10 +1487,28 @@ clutter_actor_class_init (ClutterActorClass *klass)
G_TYPE_FROM_CLASS (object_class), G_TYPE_FROM_CLASS (object_class),
G_SIGNAL_RUN_LAST, G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (ClutterActorClass, leave), G_STRUCT_OFFSET (ClutterActorClass, leave),
NULL, NULL, _clutter_boolean_handled_accumulator, NULL,
clutter_marshal_VOID__VOID, clutter_marshal_BOOLEAN__BOXED,
G_TYPE_NONE, 0); G_TYPE_BOOLEAN, 1,
CLUTTER_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
/**
* ClutterActor::captured-event:
* @actor: the actor which the pointer has left
*
* The ::leave signal is emitted when the pointer leaves the @actor.
*
* Since: 0.6
*/
actor_signals[EVENT_CAPTURED] =
g_signal_new ("captured-event",
G_TYPE_FROM_CLASS (object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (ClutterActorClass, captured),
_clutter_boolean_handled_accumulator, NULL,
clutter_marshal_BOOLEAN__BOXED,
G_TYPE_BOOLEAN, 1,
CLUTTER_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
klass->show = clutter_actor_real_show; klass->show = clutter_actor_real_show;
klass->show_all = clutter_actor_show; klass->show_all = clutter_actor_show;
@ -2977,6 +2996,7 @@ clutter_actor_lower_bottom (ClutterActor *self)
* clutter_actor_event: * clutter_actor_event:
* @actor: a #ClutterActor * @actor: a #ClutterActor
* @event: a #ClutterEvent * @event: a #ClutterEvent
* @capture: TRUE if event in in capture phase, FALSE otherwise.
* *
* This function is used to emit an event on the main stage. * This function is used to emit an event on the main stage.
* You should rarely need to use this function, except for * You should rarely need to use this function, except for
@ -2988,7 +3008,8 @@ clutter_actor_lower_bottom (ClutterActor *self)
*/ */
gboolean gboolean
clutter_actor_event (ClutterActor *actor, clutter_actor_event (ClutterActor *actor,
ClutterEvent *event) ClutterEvent *event,
gboolean capture)
{ {
gboolean retval = TRUE; gboolean retval = TRUE;
gint signal_num = -1; gint signal_num = -1;
@ -2997,51 +3018,64 @@ clutter_actor_event (ClutterActor *actor,
g_return_val_if_fail (event != NULL, FALSE); g_return_val_if_fail (event != NULL, FALSE);
g_object_ref (actor); g_object_ref (actor);
if (capture)
{
g_signal_emit (actor, actor_signals[EVENT_CAPTURED], 0,
event, &retval);
goto out;
}
g_signal_emit (actor, actor_signals[EVENT], 0, event, &retval); g_signal_emit (actor, actor_signals[EVENT], 0, event, &retval);
if (!retval) if (!retval)
{ {
switch (event->type) switch (event->type)
{ {
case CLUTTER_NOTHING: case CLUTTER_NOTHING:
break; break;
case CLUTTER_BUTTON_PRESS: case CLUTTER_BUTTON_PRESS:
signal_num = BUTTON_PRESS_EVENT; signal_num = BUTTON_PRESS_EVENT;
break; break;
case CLUTTER_BUTTON_RELEASE: case CLUTTER_BUTTON_RELEASE:
signal_num = BUTTON_RELEASE_EVENT; signal_num = BUTTON_RELEASE_EVENT;
break; break;
case CLUTTER_SCROLL: case CLUTTER_SCROLL:
signal_num = SCROLL_EVENT; signal_num = SCROLL_EVENT;
break; break;
case CLUTTER_KEY_PRESS: case CLUTTER_KEY_PRESS:
signal_num = KEY_PRESS_EVENT; signal_num = KEY_PRESS_EVENT;
break; break;
case CLUTTER_KEY_RELEASE: case CLUTTER_KEY_RELEASE:
signal_num = KEY_RELEASE_EVENT; signal_num = KEY_RELEASE_EVENT;
break; break;
case CLUTTER_MOTION: case CLUTTER_MOTION:
signal_num = MOTION_EVENT; signal_num = MOTION_EVENT;
break; break;
case CLUTTER_ENTER: case CLUTTER_ENTER:
signal_num = ENTER_EVENT; signal_num = ENTER_EVENT;
break; break;
case CLUTTER_LEAVE: case CLUTTER_LEAVE:
signal_num = LEAVE_EVENT; signal_num = LEAVE_EVENT;
break; break;
case CLUTTER_DELETE: case CLUTTER_DELETE:
case CLUTTER_DESTROY_NOTIFY: case CLUTTER_DESTROY_NOTIFY:
case CLUTTER_CLIENT_MESSAGE: case CLUTTER_CLIENT_MESSAGE:
default: default:
signal_num = -1; signal_num = -1;
break; break;
} }
if (signal_num != -1) if (signal_num != -1)
g_signal_emit (actor, actor_signals[signal_num], 0, event, &retval); g_signal_emit (actor, actor_signals[signal_num], 0,
event, &retval);
} }
g_signal_emit (actor, actor_signals[EVENT_AFTER], 0, event);
if (!retval)
g_signal_emit (actor, actor_signals[EVENT_AFTER], 0, event);
out:
g_object_unref (actor); g_object_unref (actor);

View File

@ -211,6 +211,8 @@ struct _ClutterActorClass
ClutterCrossingEvent *event); ClutterCrossingEvent *event);
void (* leave) (ClutterActor *actor, void (* leave) (ClutterActor *actor,
ClutterCrossingEvent *event); ClutterCrossingEvent *event);
void (* captured) (ClutterActor *actor,
ClutterEvent *event);
void (* focus_in) (ClutterActor *actor); void (* focus_in) (ClutterActor *actor);
void (* focus_out) (ClutterActor *actor); void (* focus_out) (ClutterActor *actor);
@ -367,7 +369,8 @@ void clutter_actor_apply_transform_to_point (ClutterActor
/* Per actor event handling - may change */ /* Per actor event handling - may change */
gboolean gboolean
clutter_actor_event (ClutterActor *actor, clutter_actor_event (ClutterActor *actor,
ClutterEvent *event); ClutterEvent *event,
gboolean capture);
void void
clutter_actor_set_reactive (ClutterActor *actor); clutter_actor_set_reactive (ClutterActor *actor);

View File

@ -379,10 +379,8 @@ clutter_event_free (ClutterEvent *event)
if (G_LIKELY (event)) if (G_LIKELY (event))
{ {
ClutterActor *source = NULL; ClutterActor *source = NULL;
if (event->type == CLUTTER_LEAVE || event->type == CLUTTER_ENTER)
source = clutter_event_get_source (event); g_object_unref (event->crossing.related);
if (source)
g_object_unref (source);
g_slice_free (ClutterEvent, event); g_slice_free (ClutterEvent, event);
} }
} }

View File

@ -1133,6 +1133,56 @@ event_click_count_generate (ClutterEvent *event)
} }
} }
static void
deliver_event (ClutterEvent *event, ClutterActor *source)
{
#define MAX_EVENT_DEPTH 512
static ClutterActor **event_tree = NULL;
static gboolean lock = FALSE;
ClutterActor *actor;
gint i = 0, n_tree_events = 0;
g_return_if_fail (source != NULL);
g_return_if_fail (lock == FALSE);
lock = TRUE; /* Guard against reentrancy */
/* Sorry Mr Bassi. */
if (event_tree == NULL)
event_tree = g_new0(ClutterActor*, MAX_EVENT_DEPTH);
actor = source;
/* Build 'tree' of events */
while (actor && n_tree_events < MAX_EVENT_DEPTH)
{
if (clutter_actor_is_reactive (actor) ||
clutter_actor_get_parent (actor) == NULL)
event_tree[n_tree_events++] = g_object_ref (actor);
actor = clutter_actor_get_parent (actor);
}
/* Capture */
for (i=n_tree_events-1; i >= 0; i--)
if (clutter_actor_event (event_tree[i], event, TRUE))
goto done;
/* Bubble */
for (i=0; i < n_tree_events; i++)
if (clutter_actor_event (event_tree[i], event, FALSE))
goto done;
done:
for (i=0; i < n_tree_events; i++)
g_object_unref (event_tree[i]);
lock = FALSE;
}
/** /**
* clutter_do_event * clutter_do_event
* @event: a #ClutterEvent. * @event: a #ClutterEvent.
@ -1151,6 +1201,7 @@ clutter_do_event (ClutterEvent *event)
ClutterMainContext *context; ClutterMainContext *context;
ClutterBackend *backend; ClutterBackend *backend;
ClutterActor *stage; ClutterActor *stage;
static ClutterActor *motion_last_actor = NULL; static ClutterActor *motion_last_actor = NULL;
context = clutter_context_get_default (); context = clutter_context_get_default ();
@ -1175,18 +1226,7 @@ clutter_do_event (ClutterEvent *event)
g_return_if_fail (actor != NULL); g_return_if_fail (actor != NULL);
while (actor) deliver_event (event, actor);
{
if (clutter_actor_is_reactive (actor) ||
clutter_actor_get_parent (actor) == NULL /* STAGE */ )
{
CLUTTER_NOTE (EVENT, "forwarding event to reactive actor");
if (clutter_actor_event (actor, event))
break;
}
actor = clutter_actor_get_parent (actor);
}
} }
break; break;
case CLUTTER_DESTROY_NOTIFY: case CLUTTER_DESTROY_NOTIFY:
@ -1203,25 +1243,16 @@ clutter_do_event (ClutterEvent *event)
g_return_if_fail (actor != NULL); g_return_if_fail (actor != NULL);
event->key.source = g_object_ref (actor); event->key.source = actor;
deliver_event (event, actor);
/* bubble up */
do
{
if (clutter_actor_event (actor, event))
break;
actor = clutter_actor_get_parent (actor);
}
while (actor != NULL);
} }
break; break;
case CLUTTER_MOTION: case CLUTTER_MOTION:
if (context->motion_events_per_actor == FALSE) if (context->motion_events_per_actor == FALSE)
{ {
/* Only stage gets motion events */ /* Only stage gets motion events */
event->motion.source = g_object_ref (stage); event->motion.source = stage;
clutter_actor_event (stage, event); clutter_actor_event (stage, event, FALSE);
break; break;
} }
case CLUTTER_BUTTON_PRESS: case CLUTTER_BUTTON_PRESS:
@ -1246,15 +1277,20 @@ clutter_do_event (ClutterEvent *event)
x, y, x, y,
CLUTTER_PICK_REACTIVE); CLUTTER_PICK_REACTIVE);
/* FIXME: for an optimisation should check if there are
* actually any reactive actors and avoid the pick all togeather
* (signalling just the stage). Should be big help for gles.
*/
CLUTTER_NOTE (EVENT, "Reactive event received at %i, %i - actor: %p", CLUTTER_NOTE (EVENT, "Reactive event received at %i, %i - actor: %p",
x, y, actor); x, y, actor);
g_return_if_fail (actor != NULL); g_return_if_fail (actor != NULL);
if (event->type == CLUTTER_SCROLL) if (event->type == CLUTTER_SCROLL)
event->scroll.source = g_object_ref (actor); event->scroll.source = actor;
else else
event->button.source = g_object_ref (actor); event->button.source = actor;
/* Motion enter leave events */ /* Motion enter leave events */
if (event->type == CLUTTER_MOTION) if (event->type == CLUTTER_MOTION)
@ -1270,7 +1306,8 @@ clutter_do_event (ClutterEvent *event)
cev.crossing.flags = 0; cev.crossing.flags = 0;
cev.crossing.x = x; cev.crossing.x = x;
cev.crossing.y = y; cev.crossing.y = y;
cev.crossing.source = g_object_ref (motion_last_actor); cev.crossing.source = motion_last_actor;
/* unref in free */
cev.crossing.related = g_object_ref (actor); cev.crossing.related = g_object_ref (actor);
clutter_event_put (&cev); /* copys */ clutter_event_put (&cev); /* copys */
@ -1280,7 +1317,7 @@ clutter_do_event (ClutterEvent *event)
cev.crossing.flags = 0; cev.crossing.flags = 0;
cev.crossing.x = x; cev.crossing.x = x;
cev.crossing.y = y; cev.crossing.y = y;
cev.crossing.source = g_object_ref (actor); cev.crossing.source = actor;
cev.crossing.related = g_object_ref (motion_last_actor); cev.crossing.related = g_object_ref (motion_last_actor);
clutter_event_put (&cev); clutter_event_put (&cev);
@ -1291,27 +1328,7 @@ clutter_do_event (ClutterEvent *event)
else else
event_click_count_generate (event); event_click_count_generate (event);
/* Send the event to the actor and all parents always the deliver_event (event, actor);
* stage.
*
* FIXME: for an optimisation should check if there are
* actually any reactive actors and avoid the pick all togeather
* (signalling just the stage). Should be big help for gles.
*
* FIXME: Actors be able to stop emission.
*/
while (actor)
{
if (clutter_actor_is_reactive (actor) ||
clutter_actor_get_parent (actor) == NULL /* STAGE */ )
{
CLUTTER_NOTE (EVENT, "forwarding event to reactive actor");
if (clutter_actor_event (actor, event))
break;
}
actor = clutter_actor_get_parent (actor);
}
} }
break; break;
case CLUTTER_STAGE_STATE: case CLUTTER_STAGE_STATE:

View File

@ -828,7 +828,7 @@ clutter_stage_event (ClutterStage *stage,
return FALSE; return FALSE;
/* emit raw event */ /* emit raw event */
if (clutter_actor_event (CLUTTER_ACTOR (stage), event)) if (clutter_actor_event (CLUTTER_ACTOR (stage), event, FALSE))
return TRUE; return TRUE;
if (event->stage_state.changed_mask & CLUTTER_STAGE_STATE_FULLSCREEN) if (event->stage_state.changed_mask & CLUTTER_STAGE_STATE_FULLSCREEN)

View File

@ -42,6 +42,18 @@ red_button_cb (ClutterActor *actor,
IsMotion = TRUE; IsMotion = TRUE;
clutter_enable_motion_events (IsMotion); clutter_enable_motion_events (IsMotion);
return FALSE;
}
static gboolean
capture_cb (ClutterActor *actor,
ClutterEvent *event,
gpointer data)
{
printf("captured event for type '%s'\n", G_OBJECT_TYPE_NAME(actor));
return FALSE;
} }
static void static void
@ -152,6 +164,7 @@ main (int argc, char *argv[])
stage = clutter_stage_get_default (); stage = clutter_stage_get_default ();
g_signal_connect (stage, "event", G_CALLBACK (input_cb), "stage"); g_signal_connect (stage, "event", G_CALLBACK (input_cb), "stage");
g_signal_connect (stage, "fullscreen", g_signal_connect (stage, "fullscreen",
G_CALLBACK (stage_state_cb), "fullscreen"); G_CALLBACK (stage_state_cb), "fullscreen");
g_signal_connect (stage, "unfullscreen", g_signal_connect (stage, "unfullscreen",
@ -161,6 +174,8 @@ main (int argc, char *argv[])
g_signal_connect (stage, "deactivate", g_signal_connect (stage, "deactivate",
G_CALLBACK (stage_state_cb), "deactivate"); G_CALLBACK (stage_state_cb), "deactivate");
g_signal_connect (stage, "captured-event", G_CALLBACK (capture_cb), NULL);
focus_box = clutter_rectangle_new_with_color (&ncol); focus_box = clutter_rectangle_new_with_color (&ncol);
clutter_container_add (CLUTTER_CONTAINER(stage), focus_box, NULL); clutter_container_add (CLUTTER_CONTAINER(stage), focus_box, NULL);
@ -193,6 +208,8 @@ main (int argc, char *argv[])
g_signal_connect (actor, "focus-in", G_CALLBACK (key_focus_in_cb), g_signal_connect (actor, "focus-in", G_CALLBACK (key_focus_in_cb),
focus_box); focus_box);
g_signal_connect (actor, "captured-event", G_CALLBACK (capture_cb), NULL);
actor = clutter_rectangle_new_with_color (&bcol); actor = clutter_rectangle_new_with_color (&bcol);
clutter_actor_set_size (actor, 100, 100); clutter_actor_set_size (actor, 100, 100);
clutter_actor_set_position (actor, 400, 100); clutter_actor_set_position (actor, 400, 100);