From 520039003787bae3347adc49e29709fa8c44c64d Mon Sep 17 00:00:00 2001 From: Lionel Landwerlin Date: Thu, 21 Jun 2012 02:55:56 +0100 Subject: [PATCH] gesture-action: add multiple point support and touch events support https://bugzilla.gnome.org/show_bug.cgi?id=678586 --- clutter/clutter-gesture-action.c | 352 ++++++++++++++++----- clutter/clutter-gesture-action.h | 3 + clutter/clutter.symbols | 2 + doc/reference/clutter/clutter-sections.txt | 2 + 4 files changed, 279 insertions(+), 80 deletions(-) diff --git a/clutter/clutter-gesture-action.c b/clutter/clutter-gesture-action.c index e7b089285..f75919b57 100644 --- a/clutter/clutter-gesture-action.c +++ b/clutter/clutter-gesture-action.c @@ -5,6 +5,7 @@ * * Copyright (C) 2010 Intel Corporation. * Copyright (C) 2011 Robert Bosch Car Multimedia GmbH. + * Copyright (C) 2012 Intel Corporation. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -61,18 +62,29 @@ #include "clutter-marshal.h" #include "clutter-private.h" -struct _ClutterGestureActionPrivate -{ - ClutterActor *stage; +#define MAX_GESTURE_POINTS (10) - guint actor_capture_id; - gulong stage_capture_id; +typedef struct +{ + ClutterInputDevice *device; + ClutterEventSequence *sequence; gfloat press_x, press_y; gfloat last_motion_x, last_motion_y; gfloat release_x, release_y; +} GesturePoint; - guint in_drag : 1; +struct _ClutterGestureActionPrivate +{ + ClutterActor *stage; + + gint requested_nb_points; + GArray *points; + + guint actor_capture_id; + gulong stage_capture_id; + + guint in_gesture : 1; }; enum @@ -89,6 +101,72 @@ static guint gesture_signals[LAST_SIGNAL] = { 0, }; G_DEFINE_TYPE (ClutterGestureAction, clutter_gesture_action, CLUTTER_TYPE_ACTION); +static GesturePoint * +gesture_register_point (ClutterGestureAction *action, ClutterEvent *event) +{ + ClutterGestureActionPrivate *priv = action->priv; + GesturePoint *point = NULL; + + if (priv->points->len >= MAX_GESTURE_POINTS) + return NULL; + + g_array_set_size (priv->points, priv->points->len + 1); + point = &g_array_index (priv->points, GesturePoint, priv->points->len - 1); + + point->device = clutter_event_get_device (event); + + clutter_event_get_coords (event, &point->press_x, &point->press_y); + point->last_motion_x = point->press_x; + point->last_motion_y = point->press_y; + + if (clutter_event_type (event) != CLUTTER_BUTTON_PRESS) + point->sequence = clutter_event_get_event_sequence (event); + else + point->sequence = NULL; + + return point; +} + +static GesturePoint * +gesture_find_point (ClutterGestureAction *action, + ClutterEvent *event, + gint *position) +{ + ClutterGestureActionPrivate *priv = action->priv; + GesturePoint *point = NULL; + ClutterEventType type = clutter_event_type (event); + ClutterInputDevice *device = clutter_event_get_device (event); + ClutterEventSequence *sequence = NULL; + gint i; + + if ((type != CLUTTER_BUTTON_PRESS) && + (type != CLUTTER_BUTTON_RELEASE) && + (type != CLUTTER_MOTION)) + sequence = clutter_event_get_event_sequence (event); + + for (i = 0; i < priv->points->len; i++) + { + if ((g_array_index (priv->points, GesturePoint, i).device == device) && + (g_array_index (priv->points, GesturePoint, i).sequence == sequence)) + { + if (position != NULL) + *position = i; + point = &g_array_index (priv->points, GesturePoint, i); + break; + } + } + + return point; +} + +static void +gesture_unregister_point (ClutterGestureAction *action, gint position) +{ + ClutterGestureActionPrivate *priv = action->priv; + + g_array_remove_index (priv->points, position); +} + static gboolean signal_accumulator (GSignalInvocationHint *ihint, GValue *return_accu, @@ -109,7 +187,7 @@ cancel_gesture (ClutterGestureAction *action) ClutterGestureActionPrivate *priv = action->priv; ClutterActor *actor; - priv->in_drag = FALSE; + priv->in_gesture = FALSE; g_signal_handler_disconnect (priv->stage, priv->stage_capture_id); priv->stage_capture_id = 0; @@ -125,7 +203,12 @@ stage_captured_event_cb (ClutterActor *stage, { ClutterGestureActionPrivate *priv = action->priv; ClutterActor *actor; + gint position; gboolean return_value; + GesturePoint *point; + + if ((point = gesture_find_point (action, event, &position)) == NULL) + return CLUTTER_EVENT_PROPAGATE; actor = clutter_actor_meta_get_actor (CLUTTER_ACTOR_META (action)); @@ -144,64 +227,77 @@ stage_captured_event_cb (ClutterActor *stage, cancel_gesture (action); return CLUTTER_EVENT_PROPAGATE; } - - clutter_event_get_coords (event, &priv->last_motion_x, - &priv->last_motion_y); - - if (!clutter_actor_transform_stage_point (actor, - priv->last_motion_x, - priv->last_motion_y, - NULL, NULL)) - return CLUTTER_EVENT_PROPAGATE; - - if (!priv->in_drag) - { - gint drag_threshold; - ClutterSettings *settings = clutter_settings_get_default (); - - g_object_get (settings, - "dnd-drag-threshold", &drag_threshold, - NULL); - - if ((ABS (priv->press_y - priv->last_motion_y) >= drag_threshold) || - (ABS (priv->press_x - priv->last_motion_x) >= drag_threshold)) - { - priv->in_drag = TRUE; - - g_signal_emit (action, gesture_signals[GESTURE_BEGIN], 0, actor, - &return_value); - if (!return_value) - { - cancel_gesture (action); - return CLUTTER_EVENT_PROPAGATE; - } - } - else - return CLUTTER_EVENT_PROPAGATE; - } - - g_signal_emit (action, gesture_signals[GESTURE_PROGRESS], 0, actor, - &return_value); - if (!return_value) - { - cancel_gesture (action); - return CLUTTER_EVENT_PROPAGATE; - } } + /* Follow same code path as a touch event update */ + + case CLUTTER_TOUCH_UPDATE: + clutter_event_get_coords (event, + &point->last_motion_x, + &point->last_motion_y); + + if (priv->points->len < priv->requested_nb_points) + return CLUTTER_EVENT_PROPAGATE; + + if (!priv->in_gesture) + { + gint drag_threshold; + ClutterSettings *settings = clutter_settings_get_default (); + + g_object_get (settings, + "dnd-drag-threshold", &drag_threshold, + NULL); + + if ((ABS (point->press_y - point->last_motion_y) >= drag_threshold) || + (ABS (point->press_x - point->last_motion_x) >= drag_threshold)) + { + priv->in_gesture = TRUE; + + g_signal_emit (action, gesture_signals[GESTURE_BEGIN], 0, actor, + &return_value); + if (!return_value) + { + cancel_gesture (action); + return CLUTTER_EVENT_PROPAGATE; + } + } + else + return CLUTTER_EVENT_PROPAGATE; + } + + g_signal_emit (action, gesture_signals[GESTURE_PROGRESS], 0, actor, + &return_value); + if (!return_value) + { + cancel_gesture (action); + return CLUTTER_EVENT_PROPAGATE; + } break; case CLUTTER_BUTTON_RELEASE: + case CLUTTER_TOUCH_END: { - clutter_event_get_coords (event, &priv->release_x, &priv->release_y); + clutter_event_get_coords (event, &point->release_x, &point->release_y); - g_signal_handler_disconnect (priv->stage, priv->stage_capture_id); - priv->stage_capture_id = 0; - - if (priv->in_drag) + if (priv->in_gesture && + ((priv->points->len - 1) < priv->requested_nb_points)) { - priv->in_drag = FALSE; + priv->in_gesture = FALSE; g_signal_emit (action, gesture_signals[GESTURE_END], 0, actor); } + + gesture_unregister_point (action, position); + } + break; + + case CLUTTER_TOUCH_CANCEL: + { + if (priv->in_gesture) + { + priv->in_gesture = FALSE; + cancel_gesture (action); + } + + gesture_unregister_point (action, position); } break; @@ -209,6 +305,12 @@ stage_captured_event_cb (ClutterActor *stage, break; } + if (priv->points->len == 0) + { + g_signal_handler_disconnect (priv->stage, priv->stage_capture_id); + priv->stage_capture_id = 0; + } + return CLUTTER_EVENT_PROPAGATE; } @@ -218,22 +320,25 @@ actor_captured_event_cb (ClutterActor *actor, ClutterGestureAction *action) { ClutterGestureActionPrivate *priv = action->priv; + GesturePoint *point; - if (clutter_event_type (event) != CLUTTER_BUTTON_PRESS) + if ((clutter_event_type (event) != CLUTTER_BUTTON_PRESS) && + (clutter_event_type (event) != CLUTTER_TOUCH_BEGIN)) return CLUTTER_EVENT_PROPAGATE; if (!clutter_actor_meta_get_enabled (CLUTTER_ACTOR_META (action))) return CLUTTER_EVENT_PROPAGATE; - clutter_event_get_coords (event, &priv->press_x, &priv->press_y); + point = gesture_register_point (action, event); if (priv->stage == NULL) priv->stage = clutter_actor_get_stage (actor); - priv->stage_capture_id = - g_signal_connect_after (priv->stage, "captured-event", - G_CALLBACK (stage_captured_event_cb), - action); + if (priv->stage_capture_id == 0) + priv->stage_capture_id = + g_signal_connect_after (priv->stage, "captured-event", + G_CALLBACK (stage_captured_event_cb), + action); return CLUTTER_EVENT_PROPAGATE; } @@ -394,9 +499,8 @@ clutter_gesture_action_init (ClutterGestureAction *self) self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, CLUTTER_TYPE_GESTURE_ACTION, ClutterGestureActionPrivate); - self->priv->press_x = self->priv->press_y = 0.f; - self->priv->last_motion_x = self->priv->last_motion_y = 0.f; - self->priv->release_x = self->priv->release_y = 0.f; + self->priv->points = g_array_sized_new (FALSE, TRUE, sizeof (GesturePoint), 3); + self->priv->requested_nb_points = 1; } /** @@ -433,15 +537,17 @@ clutter_gesture_action_get_press_coords (ClutterGestureAction *action, gfloat *press_y) { g_return_if_fail (CLUTTER_IS_GESTURE_ACTION (action)); - - if (device != 0) - g_warning ("Multi-device support not yet implemented"); + g_return_if_fail (action->priv->points->len > device); if (press_x) - *press_x = action->priv->press_x; + *press_x = g_array_index (action->priv->points, + GesturePoint, + device).press_x; if (press_y) - *press_y = action->priv->press_y; + *press_y = g_array_index (action->priv->points, + GesturePoint, + device).press_y; } /** @@ -465,15 +571,17 @@ clutter_gesture_action_get_motion_coords (ClutterGestureAction *action, gfloat *motion_y) { g_return_if_fail (CLUTTER_IS_GESTURE_ACTION (action)); - - if (device != 0) - g_warning ("Multi-device support not yet implemented"); + g_return_if_fail (action->priv->points->len > device); if (motion_x) - *motion_x = action->priv->last_motion_x; + *motion_x = g_array_index (action->priv->points, + GesturePoint, + device).last_motion_x; if (motion_y) - *motion_y = action->priv->last_motion_y; + *motion_y = g_array_index (action->priv->points, + GesturePoint, + device).last_motion_y; } /** @@ -495,13 +603,97 @@ clutter_gesture_action_get_release_coords (ClutterGestureAction *action, gfloat *release_y) { g_return_if_fail (CLUTTER_IS_GESTURE_ACTION (action)); - - if (device != 0) - g_warning ("Multi-device support not yet implemented"); + g_return_if_fail (action->priv->points->len > device); if (release_x) - *release_x = action->priv->release_x; + *release_x = g_array_index (action->priv->points, + GesturePoint, + device).release_x; if (release_y) - *release_y = action->priv->release_y; + *release_y = g_array_index (action->priv->points, + GesturePoint, + device).release_y; +} + +/** + * clutter_gesture_action_get_n_touch_points: + * @action: a #ClutterGestureAction + * + * Retrieves the number of requested points to trigger the gesture. + * + * Return value: the number of points to trigger the gesture. + * + * Since: 1.12 + */ +gint +clutter_gesture_action_get_n_touch_points (ClutterGestureAction *action) +{ + g_return_val_if_fail (CLUTTER_IS_GESTURE_ACTION (action), 0); + + return action->priv->requested_nb_points; +} + +/** + * clutter_gesture_action_set_n_touch_points: + * @action: a #ClutterGestureAction + * @nb_points: a number of points + * + * Sets the number of points needed to trigger the gesture. + * + * Since: 1.12 + */ +void +clutter_gesture_action_set_n_touch_points (ClutterGestureAction *action, + gint nb_points) +{ + ClutterGestureActionPrivate *priv; + + g_return_if_fail (CLUTTER_IS_GESTURE_ACTION (action)); + g_return_if_fail (nb_points >= 1); + + priv = action->priv; + + priv->requested_nb_points = nb_points; + + if (priv->in_gesture) + { + if (priv->points->len < priv->requested_nb_points) + cancel_gesture (action); + } + else + { + if (priv->points->len >= priv->requested_nb_points) + { + ClutterActor *actor = + clutter_actor_meta_get_actor (CLUTTER_ACTOR_META (action)); + ClutterSettings *settings = clutter_settings_get_default (); + gint i, drag_threshold; + + g_object_get (settings, + "dnd-drag-threshold", &drag_threshold, + NULL); + + for (i = 0; i < priv->points->len; i++) + { + GesturePoint *point = &g_array_index (priv->points, GesturePoint, i); + + if ((ABS (point->press_y - point->last_motion_y) >= drag_threshold) || + (ABS (point->press_x - point->last_motion_x) >= drag_threshold)) + { + gboolean return_value; + + priv->in_gesture = TRUE; + + g_signal_emit (action, gesture_signals[GESTURE_BEGIN], 0, actor, + &return_value); + + if (!return_value) + cancel_gesture (action); + + break; + } + } + } + } } diff --git a/clutter/clutter-gesture-action.h b/clutter/clutter-gesture-action.h index f7a9c0488..3aade5b4b 100644 --- a/clutter/clutter-gesture-action.h +++ b/clutter/clutter-gesture-action.h @@ -102,6 +102,9 @@ GType clutter_gesture_action_get_type (void) G_GNUC_CONST; ClutterAction * clutter_gesture_action_new (void); +gint clutter_gesture_action_get_n_touch_points (ClutterGestureAction *action); +void clutter_gesture_action_set_n_touch_points (ClutterGestureAction *action, + gint nb_points); void clutter_gesture_action_get_press_coords (ClutterGestureAction *action, guint device, gfloat *press_x, diff --git a/clutter/clutter.symbols b/clutter/clutter.symbols index 8633bb87d..d4ae77cf2 100644 --- a/clutter/clutter.symbols +++ b/clutter/clutter.symbols @@ -727,9 +727,11 @@ clutter_geometry_get_type clutter_geometry_intersects clutter_geometry_union clutter_gesture_action_get_motion_coords +clutter_gesture_action_get_n_touch_points clutter_gesture_action_get_press_coords clutter_gesture_action_get_release_coords clutter_gesture_action_get_type +clutter_gesture_action_set_n_touch_points clutter_gesture_action_new clutter_get_accessibility_enabled clutter_get_actor_by_gid diff --git a/doc/reference/clutter/clutter-sections.txt b/doc/reference/clutter/clutter-sections.txt index 4555e768c..eb2ca5781 100644 --- a/doc/reference/clutter/clutter-sections.txt +++ b/doc/reference/clutter/clutter-sections.txt @@ -2929,6 +2929,8 @@ clutter_gesture_action_new clutter_gesture_action_get_press_coords clutter_gesture_action_get_motion_coords clutter_gesture_action_get_release_coords +clutter_gesture_action_get_n_touch_points +clutter_gesture_action_set_n_touch_points CLUTTER_GESTURE_ACTION CLUTTER_GESTURE_ACTION_CLASS