9caf9ffbbb
When we miss button release events (eg. when they happen outside of our window) we must ensure that the corresponding point is removed from the array of tracked points, otherwise GestureAction will get very confused and will cancel all subsequent gestures. https://bugzilla.gnome.org/show_bug.cgi?id=683471
893 lines
26 KiB
C
893 lines
26 KiB
C
/*
|
|
* Clutter.
|
|
*
|
|
* An OpenGL based 'interactive canvas' library.
|
|
*
|
|
* 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
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
* Author:
|
|
* Tomeu Vizoso <tomeu.vizoso@collabora.co.uk>
|
|
*/
|
|
|
|
/**
|
|
* SECTION:clutter-gesture-action
|
|
* @Title: ClutterGestureAction
|
|
* @Short_Description: Action for gesture gestures
|
|
*
|
|
* #ClutterGestureAction is a sub-class of #ClutterAction that implements
|
|
* the logic for recognizing gesture gestures. It listens for low level events
|
|
* such as #ClutterButtonEvent and #ClutterMotionEvent on the stage to raise
|
|
* the #ClutterGestureAction::gesture-begin, #ClutterGestureAction::gesture-progress,
|
|
* and #ClutterGestureAction::gesture-end signals.
|
|
*
|
|
* To use #ClutterGestureAction you just need to apply it to a #ClutterActor
|
|
* using clutter_actor_add_action() and connect to the signals:
|
|
*
|
|
* |[
|
|
* ClutterAction *action = clutter_gesture_action_new ();
|
|
*
|
|
* clutter_actor_add_action (actor, action);
|
|
*
|
|
* g_signal_connect (action, "gesture-begin", G_CALLBACK (on_gesture_begin), NULL);
|
|
* g_signal_connect (action, "gesture-progress", G_CALLBACK (on_gesture_progress), NULL);
|
|
* g_signal_connect (action, "gesture-end", G_CALLBACK (on_gesture_end), NULL);
|
|
* ]|
|
|
*
|
|
* Since: 1.8
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include "clutter-gesture-action.h"
|
|
|
|
#include "clutter-debug.h"
|
|
#include "clutter-enum-types.h"
|
|
#include "clutter-marshal.h"
|
|
#include "clutter-private.h"
|
|
|
|
#include <math.h>
|
|
|
|
#define MAX_GESTURE_POINTS (10)
|
|
#define FLOAT_EPSILON (1e-15)
|
|
|
|
typedef struct
|
|
{
|
|
ClutterInputDevice *device;
|
|
ClutterEventSequence *sequence;
|
|
|
|
gfloat press_x, press_y;
|
|
gint64 last_motion_time;
|
|
gfloat last_motion_x, last_motion_y;
|
|
gint64 last_delta_time;
|
|
gfloat last_delta_x, last_delta_y;
|
|
gfloat release_x, release_y;
|
|
} GesturePoint;
|
|
|
|
struct _ClutterGestureActionPrivate
|
|
{
|
|
ClutterActor *stage;
|
|
|
|
gint requested_nb_points;
|
|
GArray *points;
|
|
|
|
guint actor_capture_id;
|
|
gulong stage_capture_id;
|
|
|
|
guint in_gesture : 1;
|
|
};
|
|
|
|
enum
|
|
{
|
|
GESTURE_BEGIN,
|
|
GESTURE_PROGRESS,
|
|
GESTURE_END,
|
|
GESTURE_CANCEL,
|
|
|
|
LAST_SIGNAL
|
|
};
|
|
|
|
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;
|
|
point->last_motion_time = clutter_event_get_time (event);
|
|
|
|
point->last_delta_x = point->last_delta_y = 0;
|
|
point->last_delta_time = 0;
|
|
|
|
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 void
|
|
cancel_gesture (ClutterGestureAction *action)
|
|
{
|
|
ClutterGestureActionPrivate *priv = action->priv;
|
|
ClutterActor *actor;
|
|
|
|
priv->in_gesture = FALSE;
|
|
|
|
if (priv->stage_capture_id != 0)
|
|
{
|
|
g_signal_handler_disconnect (priv->stage, priv->stage_capture_id);
|
|
priv->stage_capture_id = 0;
|
|
}
|
|
|
|
actor = clutter_actor_meta_get_actor (CLUTTER_ACTOR_META (action));
|
|
g_signal_emit (action, gesture_signals[GESTURE_CANCEL], 0, actor);
|
|
}
|
|
|
|
static gboolean
|
|
stage_captured_event_cb (ClutterActor *stage,
|
|
ClutterEvent *event,
|
|
ClutterGestureAction *action)
|
|
{
|
|
ClutterGestureActionPrivate *priv = action->priv;
|
|
ClutterActor *actor;
|
|
gint position;
|
|
gboolean return_value;
|
|
GesturePoint *point;
|
|
gfloat motion_x, motion_y;
|
|
gint64 time;
|
|
|
|
if ((point = gesture_find_point (action, event, &position)) == NULL)
|
|
return CLUTTER_EVENT_PROPAGATE;
|
|
|
|
actor = clutter_actor_meta_get_actor (CLUTTER_ACTOR_META (action));
|
|
|
|
switch (clutter_event_type (event))
|
|
{
|
|
case CLUTTER_MOTION:
|
|
{
|
|
ClutterModifierType mods = clutter_event_get_state (event);
|
|
|
|
/* we might miss a button-release event in case of grabs,
|
|
* so we need to check whether the button is still down
|
|
* during a motion event
|
|
*/
|
|
if (!(mods & CLUTTER_BUTTON1_MASK))
|
|
{
|
|
cancel_gesture (action);
|
|
gesture_unregister_point (action, position);
|
|
return CLUTTER_EVENT_PROPAGATE;
|
|
}
|
|
}
|
|
/* Follow same code path as a touch event update */
|
|
|
|
case CLUTTER_TOUCH_UPDATE:
|
|
clutter_event_get_coords (event,
|
|
&motion_x,
|
|
&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 - motion_y) >= drag_threshold) ||
|
|
(ABS (point->press_x - motion_x) >= drag_threshold))
|
|
{
|
|
priv->in_gesture = TRUE;
|
|
|
|
if (!CLUTTER_GESTURE_ACTION_GET_CLASS (action)->gesture_prepare (action, actor))
|
|
{
|
|
cancel_gesture (action);
|
|
return CLUTTER_EVENT_PROPAGATE;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
point->last_delta_x = motion_x - point->last_motion_x;
|
|
point->last_delta_y = motion_y - point->last_motion_y;
|
|
point->last_motion_x = motion_x;
|
|
point->last_motion_y = motion_y;
|
|
|
|
time = clutter_event_get_time (event);
|
|
point->last_delta_time = time - point->last_motion_time;
|
|
point->last_motion_time = time;
|
|
|
|
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, &point->release_x, &point->release_y);
|
|
|
|
if (priv->in_gesture &&
|
|
((priv->points->len - 1) < priv->requested_nb_points))
|
|
{
|
|
/* Treat the release event as the continuation of the last motion,
|
|
* in case the user keeps the pointer still for a while before
|
|
* releasing it. */
|
|
time = clutter_event_get_time (event);
|
|
point->last_delta_time += time - point->last_motion_time;
|
|
|
|
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;
|
|
|
|
default:
|
|
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;
|
|
}
|
|
|
|
static gboolean
|
|
actor_captured_event_cb (ClutterActor *actor,
|
|
ClutterEvent *event,
|
|
ClutterGestureAction *action)
|
|
{
|
|
ClutterGestureActionPrivate *priv = action->priv;
|
|
GesturePoint *point G_GNUC_UNUSED;
|
|
|
|
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;
|
|
|
|
point = gesture_register_point (action, event);
|
|
|
|
if (priv->stage == NULL)
|
|
priv->stage = clutter_actor_get_stage (actor);
|
|
|
|
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;
|
|
}
|
|
|
|
static void
|
|
clutter_gesture_action_set_actor (ClutterActorMeta *meta,
|
|
ClutterActor *actor)
|
|
{
|
|
ClutterGestureActionPrivate *priv = CLUTTER_GESTURE_ACTION (meta)->priv;
|
|
ClutterActorMetaClass *meta_class =
|
|
CLUTTER_ACTOR_META_CLASS (clutter_gesture_action_parent_class);
|
|
|
|
if (priv->actor_capture_id != 0)
|
|
{
|
|
ClutterActor *old_actor = clutter_actor_meta_get_actor (meta);
|
|
|
|
if (old_actor != NULL)
|
|
g_signal_handler_disconnect (old_actor, priv->actor_capture_id);
|
|
|
|
priv->actor_capture_id = 0;
|
|
}
|
|
|
|
if (priv->stage_capture_id != 0)
|
|
{
|
|
if (priv->stage != NULL)
|
|
g_signal_handler_disconnect (priv->stage, priv->stage_capture_id);
|
|
|
|
priv->stage_capture_id = 0;
|
|
priv->stage = NULL;
|
|
}
|
|
|
|
if (actor != NULL)
|
|
{
|
|
priv->actor_capture_id =
|
|
g_signal_connect (actor, "captured-event",
|
|
G_CALLBACK (actor_captured_event_cb),
|
|
meta);
|
|
}
|
|
|
|
meta_class->set_actor (meta, actor);
|
|
}
|
|
|
|
static gboolean
|
|
default_event_handler (ClutterGestureAction *action,
|
|
ClutterActor *actor)
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
clutter_gesture_action_class_init (ClutterGestureActionClass *klass)
|
|
{
|
|
ClutterActorMetaClass *meta_class = CLUTTER_ACTOR_META_CLASS (klass);
|
|
|
|
g_type_class_add_private (klass, sizeof (ClutterGestureActionPrivate));
|
|
|
|
meta_class->set_actor = clutter_gesture_action_set_actor;
|
|
|
|
klass->gesture_begin = default_event_handler;
|
|
klass->gesture_progress = default_event_handler;
|
|
klass->gesture_prepare = default_event_handler;
|
|
|
|
/**
|
|
* ClutterGestureAction::gesture-begin:
|
|
* @action: the #ClutterGestureAction that emitted the signal
|
|
* @actor: the #ClutterActor attached to the @action
|
|
*
|
|
* The ::gesture_begin signal is emitted when the #ClutterActor to which
|
|
* a #ClutterGestureAction has been applied starts receiving a gesture.
|
|
*
|
|
* Return value: %TRUE if the gesture should start, and %FALSE if
|
|
* the gesture should be ignored.
|
|
*
|
|
* Since: 1.8
|
|
*/
|
|
gesture_signals[GESTURE_BEGIN] =
|
|
g_signal_new (I_("gesture-begin"),
|
|
G_TYPE_FROM_CLASS (klass),
|
|
G_SIGNAL_RUN_LAST,
|
|
G_STRUCT_OFFSET (ClutterGestureActionClass, gesture_begin),
|
|
_clutter_boolean_continue_accumulator, NULL,
|
|
_clutter_marshal_BOOLEAN__OBJECT,
|
|
G_TYPE_BOOLEAN, 1,
|
|
CLUTTER_TYPE_ACTOR);
|
|
|
|
/**
|
|
* ClutterGestureAction::gesture-progress:
|
|
* @action: the #ClutterGestureAction that emitted the signal
|
|
* @actor: the #ClutterActor attached to the @action
|
|
*
|
|
* The ::gesture-progress signal is emitted for each motion event after
|
|
* the #ClutterGestureAction::gesture-begin signal has been emitted.
|
|
*
|
|
* Return value: %TRUE if the gesture should continue, and %FALSE if
|
|
* the gesture should be cancelled.
|
|
*
|
|
* Since: 1.8
|
|
*/
|
|
gesture_signals[GESTURE_PROGRESS] =
|
|
g_signal_new (I_("gesture-progress"),
|
|
G_TYPE_FROM_CLASS (klass),
|
|
G_SIGNAL_RUN_LAST,
|
|
G_STRUCT_OFFSET (ClutterGestureActionClass, gesture_progress),
|
|
_clutter_boolean_continue_accumulator, NULL,
|
|
_clutter_marshal_BOOLEAN__OBJECT,
|
|
G_TYPE_BOOLEAN, 1,
|
|
CLUTTER_TYPE_ACTOR);
|
|
|
|
/**
|
|
* ClutterGestureAction::gesture-end:
|
|
* @action: the #ClutterGestureAction that emitted the signal
|
|
* @actor: the #ClutterActor attached to the @action
|
|
*
|
|
* The ::gesture-end signal is emitted at the end of the gesture gesture,
|
|
* when the pointer's button is released
|
|
*
|
|
* This signal is emitted if and only if the #ClutterGestureAction::gesture-begin
|
|
* signal has been emitted first.
|
|
*
|
|
* Since: 1.8
|
|
*/
|
|
gesture_signals[GESTURE_END] =
|
|
g_signal_new (I_("gesture-end"),
|
|
G_TYPE_FROM_CLASS (klass),
|
|
G_SIGNAL_RUN_LAST,
|
|
G_STRUCT_OFFSET (ClutterGestureActionClass, gesture_end),
|
|
NULL, NULL,
|
|
_clutter_marshal_VOID__OBJECT,
|
|
G_TYPE_NONE, 1,
|
|
CLUTTER_TYPE_ACTOR);
|
|
|
|
/**
|
|
* ClutterGestureAction::gesture-cancel:
|
|
* @action: the #ClutterGestureAction that emitted the signal
|
|
* @actor: the #ClutterActor attached to the @action
|
|
*
|
|
* The ::gesture-cancel signal is emitted when the ongoing gesture gets
|
|
* cancelled from the #ClutterGestureAction::gesture-progress signal handler.
|
|
*
|
|
* This signal is emitted if and only if the #ClutterGestureAction::gesture-begin
|
|
* signal has been emitted first.
|
|
*
|
|
* Since: 1.8
|
|
*/
|
|
gesture_signals[GESTURE_CANCEL] =
|
|
g_signal_new (I_("gesture-cancel"),
|
|
G_TYPE_FROM_CLASS (klass),
|
|
G_SIGNAL_RUN_LAST,
|
|
G_STRUCT_OFFSET (ClutterGestureActionClass, gesture_cancel),
|
|
NULL, NULL,
|
|
_clutter_marshal_VOID__OBJECT,
|
|
G_TYPE_NONE, 1,
|
|
CLUTTER_TYPE_ACTOR);
|
|
}
|
|
|
|
static void
|
|
clutter_gesture_action_init (ClutterGestureAction *self)
|
|
{
|
|
self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, CLUTTER_TYPE_GESTURE_ACTION,
|
|
ClutterGestureActionPrivate);
|
|
|
|
self->priv->points = g_array_sized_new (FALSE, TRUE, sizeof (GesturePoint), 3);
|
|
self->priv->requested_nb_points = 1;
|
|
}
|
|
|
|
/**
|
|
* clutter_gesture_action_new:
|
|
*
|
|
* Creates a new #ClutterGestureAction instance.
|
|
*
|
|
* Return value: the newly created #ClutterGestureAction
|
|
*
|
|
* Since: 1.8
|
|
*/
|
|
ClutterAction *
|
|
clutter_gesture_action_new (void)
|
|
{
|
|
return g_object_new (CLUTTER_TYPE_GESTURE_ACTION, NULL);
|
|
}
|
|
|
|
/**
|
|
* clutter_gesture_action_get_press_coords:
|
|
* @action: a #ClutterGestureAction
|
|
* @device: currently unused, set to 0
|
|
* @press_x: (out) (allow-none): return location for the press
|
|
* event's X coordinate
|
|
* @press_y: (out) (allow-none): return location for the press
|
|
* event's Y coordinate
|
|
*
|
|
* Retrieves the coordinates, in stage space, of the press event
|
|
* that started the dragging for an specific pointer device
|
|
*
|
|
* Since: 1.8
|
|
*/
|
|
void
|
|
clutter_gesture_action_get_press_coords (ClutterGestureAction *action,
|
|
guint device,
|
|
gfloat *press_x,
|
|
gfloat *press_y)
|
|
{
|
|
g_return_if_fail (CLUTTER_IS_GESTURE_ACTION (action));
|
|
g_return_if_fail (action->priv->points->len > device);
|
|
|
|
if (press_x)
|
|
*press_x = g_array_index (action->priv->points,
|
|
GesturePoint,
|
|
device).press_x;
|
|
|
|
if (press_y)
|
|
*press_y = g_array_index (action->priv->points,
|
|
GesturePoint,
|
|
device).press_y;
|
|
}
|
|
|
|
/**
|
|
* clutter_gesture_action_get_motion_coords:
|
|
* @action: a #ClutterGestureAction
|
|
* @device: currently unused, set to 0
|
|
* @motion_x: (out) (allow-none): return location for the latest motion
|
|
* event's X coordinate
|
|
* @motion_y: (out) (allow-none): return location for the latest motion
|
|
* event's Y coordinate
|
|
*
|
|
* Retrieves the coordinates, in stage space, of the latest motion
|
|
* event during the dragging
|
|
*
|
|
* Since: 1.8
|
|
*/
|
|
void
|
|
clutter_gesture_action_get_motion_coords (ClutterGestureAction *action,
|
|
guint device,
|
|
gfloat *motion_x,
|
|
gfloat *motion_y)
|
|
{
|
|
g_return_if_fail (CLUTTER_IS_GESTURE_ACTION (action));
|
|
g_return_if_fail (action->priv->points->len > device);
|
|
|
|
if (motion_x)
|
|
*motion_x = g_array_index (action->priv->points,
|
|
GesturePoint,
|
|
device).last_motion_x;
|
|
|
|
if (motion_y)
|
|
*motion_y = g_array_index (action->priv->points,
|
|
GesturePoint,
|
|
device).last_motion_y;
|
|
}
|
|
|
|
/**
|
|
* clutter_gesture_action_get_motion_delta:
|
|
* @action: a #ClutterGestureAction
|
|
* @device: currently unused, set to 0
|
|
* @delta_x: (out) (allow-none): return location for the X axis
|
|
* component of the incremental motion delta
|
|
* @delta_y: (out) (allow-none): return location for the Y axis
|
|
* component of the incremental motion delta
|
|
*
|
|
* Retrieves the incremental delta since the last motion event
|
|
* during the dragging.
|
|
*
|
|
* Return value: the distance since last motion event
|
|
*
|
|
* Since: 1.12
|
|
*/
|
|
gfloat
|
|
clutter_gesture_action_get_motion_delta (ClutterGestureAction *action,
|
|
guint device,
|
|
gfloat *delta_x,
|
|
gfloat *delta_y)
|
|
{
|
|
gfloat d_x, d_y;
|
|
|
|
g_return_val_if_fail (CLUTTER_IS_GESTURE_ACTION (action), 0);
|
|
g_return_val_if_fail (action->priv->points->len > device, 0);
|
|
|
|
d_x = g_array_index (action->priv->points,
|
|
GesturePoint,
|
|
device).last_delta_x;
|
|
d_y = g_array_index (action->priv->points,
|
|
GesturePoint,
|
|
device).last_delta_y;
|
|
|
|
if (delta_x)
|
|
*delta_x = d_x;
|
|
|
|
if (delta_y)
|
|
*delta_y = d_y;
|
|
|
|
return sqrt ((d_x * d_x) + (d_y * d_y));
|
|
}
|
|
|
|
/**
|
|
* clutter_gesture_action_get_release_coords:
|
|
* @action: a #ClutterGestureAction
|
|
* @device: currently unused, set to 0
|
|
* @release_x: (out) (allow-none): return location for the X coordinate of
|
|
* the last release
|
|
* @release_y: (out) (allow-none): return location for the Y coordinate of
|
|
* the last release
|
|
*
|
|
* Retrieves the coordinates, in stage space, of the point where the pointer
|
|
* device was last released.
|
|
*
|
|
* Since: 1.8
|
|
*/
|
|
void
|
|
clutter_gesture_action_get_release_coords (ClutterGestureAction *action,
|
|
guint device,
|
|
gfloat *release_x,
|
|
gfloat *release_y)
|
|
{
|
|
g_return_if_fail (CLUTTER_IS_GESTURE_ACTION (action));
|
|
g_return_if_fail (action->priv->points->len > device);
|
|
|
|
if (release_x)
|
|
*release_x = g_array_index (action->priv->points,
|
|
GesturePoint,
|
|
device).release_x;
|
|
|
|
if (release_y)
|
|
*release_y = g_array_index (action->priv->points,
|
|
GesturePoint,
|
|
device).release_y;
|
|
}
|
|
|
|
/**
|
|
* clutter_gesture_action_get_velocity:
|
|
* @action: a #ClutterGestureAction
|
|
* @device: currently unused, set to 0
|
|
* @velocity_x: (out) (allow-none): return location for the latest motion
|
|
* event's X velocity
|
|
* @velocity_y: (out) (allow-none): return location for the latest motion
|
|
* event's Y velocity
|
|
*
|
|
* Retrieves the velocity, in stage pixels per milliseconds, of the
|
|
* latest motion event during the dragging
|
|
*
|
|
* Since: 1.12
|
|
*/
|
|
gfloat
|
|
clutter_gesture_action_get_velocity (ClutterGestureAction *action,
|
|
guint device,
|
|
gfloat *velocity_x,
|
|
gfloat *velocity_y)
|
|
{
|
|
gfloat d_x, d_y, distance, velocity;
|
|
gint64 d_t;
|
|
|
|
g_return_val_if_fail (CLUTTER_IS_GESTURE_ACTION (action), 0);
|
|
g_return_val_if_fail (action->priv->points->len > device, 0);
|
|
|
|
distance = clutter_gesture_action_get_motion_delta (action, device,
|
|
&d_x, &d_y);
|
|
|
|
d_t = g_array_index (action->priv->points,
|
|
GesturePoint,
|
|
device).last_delta_time;
|
|
|
|
if (velocity_x)
|
|
*velocity_x = d_t > FLOAT_EPSILON ? d_x / d_t : 0;
|
|
|
|
if (velocity_y)
|
|
*velocity_y = d_t > FLOAT_EPSILON ? d_y / d_t : 0;
|
|
|
|
velocity = d_t > FLOAT_EPSILON ? distance / d_t : 0;
|
|
return velocity;
|
|
}
|
|
|
|
/**
|
|
* 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;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* clutter_gesture_action_get_n_current_points:
|
|
* @action: a #ClutterGestureAction
|
|
*
|
|
* Retrieves the number of points currently active.
|
|
*
|
|
* Return value: the number of points currently active.
|
|
*
|
|
* Since: 1.12
|
|
*/
|
|
guint
|
|
clutter_gesture_action_get_n_current_points (ClutterGestureAction *action)
|
|
{
|
|
g_return_val_if_fail (CLUTTER_IS_GESTURE_ACTION (action), 0);
|
|
|
|
return action->priv->points->len;
|
|
}
|
|
|
|
/**
|
|
* clutter_gesture_action_get_sequence:
|
|
* @action: a #ClutterGestureAction
|
|
* @point: index of a point currently active
|
|
*
|
|
* Retrieves the #ClutterEventSequence of a touch point.
|
|
*
|
|
* Return value: (transfer none): the #ClutterEventSequence of a touch point.
|
|
*
|
|
* Since: 1.12
|
|
*/
|
|
ClutterEventSequence *
|
|
clutter_gesture_action_get_sequence (ClutterGestureAction *action,
|
|
guint point)
|
|
{
|
|
g_return_val_if_fail (CLUTTER_IS_GESTURE_ACTION (action), NULL);
|
|
g_return_val_if_fail (action->priv->points->len > point, NULL);
|
|
|
|
return g_array_index (action->priv->points, GesturePoint, point).sequence;
|
|
}
|
|
|
|
/**
|
|
* clutter_gesture_action_get_device:
|
|
* @action: a #ClutterGestureAction
|
|
* @point: index of a point currently active
|
|
*
|
|
* Retrieves the #ClutterInputDevice of a touch point.
|
|
*
|
|
* Return value: (transfer none): the #ClutterInputDevice of a touch point.
|
|
*
|
|
* Since: 1.12
|
|
*/
|
|
ClutterInputDevice *
|
|
clutter_gesture_action_get_device (ClutterGestureAction *action,
|
|
guint point)
|
|
{
|
|
g_return_val_if_fail (CLUTTER_IS_GESTURE_ACTION (action), NULL);
|
|
g_return_val_if_fail (action->priv->points->len > point, NULL);
|
|
|
|
return g_array_index (action->priv->points, GesturePoint, point).device;
|
|
}
|
|
|
|
/**
|
|
* clutter_gesture_action_cancel:
|
|
* @action: a #ClutterGestureAction
|
|
*
|
|
* Cancel a #ClutterGestureAction before it begins
|
|
*
|
|
* Since: 1.12
|
|
*/
|
|
void
|
|
clutter_gesture_action_cancel (ClutterGestureAction *action)
|
|
{
|
|
g_return_if_fail (CLUTTER_IS_GESTURE_ACTION (action));
|
|
g_return_if_fail (!action->priv->in_gesture);
|
|
|
|
cancel_gesture (action);
|
|
|
|
g_array_set_size (action->priv->points, 0);
|
|
}
|