From 6216ce659cdd891b56eb7fadc818a678bc20d45a Mon Sep 17 00:00:00 2001 From: Matthew Allum Date: Wed, 10 Oct 2007 13:04:34 +0000 Subject: [PATCH] 2007-10-10 Matthew Allum * 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. --- ChangeLog | 9 +++ clutter/clutter-actor.c | 122 +++++++++++++++++++++++++--------------- clutter/clutter-actor.h | 5 +- clutter/clutter-event.c | 6 +- clutter/clutter-main.c | 117 ++++++++++++++++++++++---------------- clutter/clutter-stage.c | 2 +- tests/test-events.c | 17 ++++++ 7 files changed, 178 insertions(+), 100 deletions(-) diff --git a/ChangeLog b/ChangeLog index 386e24972..ed6e37cd8 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,12 @@ +2007-10-10 Matthew Allum + + * 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 * clutter/clutter-script-private.h: diff --git a/clutter/clutter-actor.c b/clutter/clutter-actor.c index 0e521eec6..0524022b8 100644 --- a/clutter/clutter-actor.c +++ b/clutter/clutter-actor.c @@ -106,6 +106,7 @@ enum PARENT_SET, EVENT, + EVENT_CAPTURED, EVENT_AFTER, BUTTON_PRESS_EVENT, BUTTON_RELEASE_EVENT, @@ -117,7 +118,6 @@ enum FOCUS_OUT, ENTER_EVENT, LEAVE_EVENT, - LAST_SIGNAL }; @@ -1469,9 +1469,10 @@ clutter_actor_class_init (ClutterActorClass *klass) G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (ClutterActorClass, enter), - NULL, NULL, - clutter_marshal_VOID__VOID, - G_TYPE_NONE, 0); + _clutter_boolean_handled_accumulator, NULL, + clutter_marshal_BOOLEAN__BOXED, + G_TYPE_BOOLEAN, 1, + CLUTTER_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE); /** * ClutterActor::leave: @@ -1486,10 +1487,28 @@ clutter_actor_class_init (ClutterActorClass *klass) G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (ClutterActorClass, leave), - NULL, NULL, - clutter_marshal_VOID__VOID, - G_TYPE_NONE, 0); + _clutter_boolean_handled_accumulator, NULL, + clutter_marshal_BOOLEAN__BOXED, + 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_all = clutter_actor_show; @@ -2977,6 +2996,7 @@ clutter_actor_lower_bottom (ClutterActor *self) * clutter_actor_event: * @actor: a #ClutterActor * @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. * You should rarely need to use this function, except for @@ -2988,7 +3008,8 @@ clutter_actor_lower_bottom (ClutterActor *self) */ gboolean clutter_actor_event (ClutterActor *actor, - ClutterEvent *event) + ClutterEvent *event, + gboolean capture) { gboolean retval = TRUE; gint signal_num = -1; @@ -2997,51 +3018,64 @@ clutter_actor_event (ClutterActor *actor, g_return_val_if_fail (event != NULL, FALSE); 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); if (!retval) { switch (event->type) - { - case CLUTTER_NOTHING: - break; - case CLUTTER_BUTTON_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_ENTER: - signal_num = ENTER_EVENT; - break; - case CLUTTER_LEAVE: - signal_num = LEAVE_EVENT; - break; - case CLUTTER_DELETE: - case CLUTTER_DESTROY_NOTIFY: - case CLUTTER_CLIENT_MESSAGE: - default: - signal_num = -1; - break; - } + { + case CLUTTER_NOTHING: + break; + case CLUTTER_BUTTON_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_ENTER: + signal_num = ENTER_EVENT; + break; + case CLUTTER_LEAVE: + signal_num = LEAVE_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); + 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); diff --git a/clutter/clutter-actor.h b/clutter/clutter-actor.h index 475d6a913..fc36c3844 100644 --- a/clutter/clutter-actor.h +++ b/clutter/clutter-actor.h @@ -211,6 +211,8 @@ struct _ClutterActorClass ClutterCrossingEvent *event); void (* leave) (ClutterActor *actor, ClutterCrossingEvent *event); + void (* captured) (ClutterActor *actor, + ClutterEvent *event); void (* focus_in) (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 */ gboolean clutter_actor_event (ClutterActor *actor, - ClutterEvent *event); + ClutterEvent *event, + gboolean capture); void clutter_actor_set_reactive (ClutterActor *actor); diff --git a/clutter/clutter-event.c b/clutter/clutter-event.c index 03d69afa2..2b13033a9 100644 --- a/clutter/clutter-event.c +++ b/clutter/clutter-event.c @@ -379,10 +379,8 @@ clutter_event_free (ClutterEvent *event) if (G_LIKELY (event)) { ClutterActor *source = NULL; - - source = clutter_event_get_source (event); - if (source) - g_object_unref (source); + if (event->type == CLUTTER_LEAVE || event->type == CLUTTER_ENTER) + g_object_unref (event->crossing.related); g_slice_free (ClutterEvent, event); } } diff --git a/clutter/clutter-main.c b/clutter/clutter-main.c index b6531362b..003da3cb4 100644 --- a/clutter/clutter-main.c +++ b/clutter/clutter-main.c @@ -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 * @event: a #ClutterEvent. @@ -1151,6 +1201,7 @@ clutter_do_event (ClutterEvent *event) ClutterMainContext *context; ClutterBackend *backend; ClutterActor *stage; + static ClutterActor *motion_last_actor = NULL; context = clutter_context_get_default (); @@ -1175,18 +1226,7 @@ clutter_do_event (ClutterEvent *event) g_return_if_fail (actor != NULL); - 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); - } + deliver_event (event, actor); } break; case CLUTTER_DESTROY_NOTIFY: @@ -1203,25 +1243,16 @@ clutter_do_event (ClutterEvent *event) g_return_if_fail (actor != NULL); - event->key.source = g_object_ref (actor); - - /* bubble up */ - do - { - if (clutter_actor_event (actor, event)) - break; - - actor = clutter_actor_get_parent (actor); - } - while (actor != NULL); + event->key.source = actor; + deliver_event (event, actor); } break; case CLUTTER_MOTION: if (context->motion_events_per_actor == FALSE) { /* Only stage gets motion events */ - event->motion.source = g_object_ref (stage); - clutter_actor_event (stage, event); + event->motion.source = stage; + clutter_actor_event (stage, event, FALSE); break; } case CLUTTER_BUTTON_PRESS: @@ -1246,15 +1277,20 @@ clutter_do_event (ClutterEvent *event) x, y, 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", x, y, actor); g_return_if_fail (actor != NULL); if (event->type == CLUTTER_SCROLL) - event->scroll.source = g_object_ref (actor); + event->scroll.source = actor; else - event->button.source = g_object_ref (actor); + event->button.source = actor; /* Motion enter leave events */ if (event->type == CLUTTER_MOTION) @@ -1270,7 +1306,8 @@ clutter_do_event (ClutterEvent *event) cev.crossing.flags = 0; cev.crossing.x = x; 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); clutter_event_put (&cev); /* copys */ @@ -1280,7 +1317,7 @@ clutter_do_event (ClutterEvent *event) cev.crossing.flags = 0; cev.crossing.x = x; cev.crossing.y = y; - cev.crossing.source = g_object_ref (actor); + cev.crossing.source = actor; cev.crossing.related = g_object_ref (motion_last_actor); clutter_event_put (&cev); @@ -1291,27 +1328,7 @@ clutter_do_event (ClutterEvent *event) else event_click_count_generate (event); - /* Send the event to the actor and all parents always the - * 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); - } + deliver_event (event, actor); } break; case CLUTTER_STAGE_STATE: diff --git a/clutter/clutter-stage.c b/clutter/clutter-stage.c index c5344e287..ee36601e9 100644 --- a/clutter/clutter-stage.c +++ b/clutter/clutter-stage.c @@ -828,7 +828,7 @@ clutter_stage_event (ClutterStage *stage, return FALSE; /* emit raw event */ - if (clutter_actor_event (CLUTTER_ACTOR (stage), event)) + if (clutter_actor_event (CLUTTER_ACTOR (stage), event, FALSE)) return TRUE; if (event->stage_state.changed_mask & CLUTTER_STAGE_STATE_FULLSCREEN) diff --git a/tests/test-events.c b/tests/test-events.c index 13c232a88..fbc6aaab1 100644 --- a/tests/test-events.c +++ b/tests/test-events.c @@ -42,6 +42,18 @@ red_button_cb (ClutterActor *actor, IsMotion = TRUE; 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 @@ -152,6 +164,7 @@ main (int argc, char *argv[]) stage = clutter_stage_get_default (); g_signal_connect (stage, "event", G_CALLBACK (input_cb), "stage"); + g_signal_connect (stage, "fullscreen", G_CALLBACK (stage_state_cb), "fullscreen"); g_signal_connect (stage, "unfullscreen", @@ -161,6 +174,8 @@ main (int argc, char *argv[]) g_signal_connect (stage, "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); 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), focus_box); + g_signal_connect (actor, "captured-event", G_CALLBACK (capture_cb), NULL); + actor = clutter_rectangle_new_with_color (&bcol); clutter_actor_set_size (actor, 100, 100); clutter_actor_set_position (actor, 400, 100);