diff --git a/clutter/Makefile.am b/clutter/Makefile.am
index 476a38d2d..7702a0e5e 100644
--- a/clutter/Makefile.am
+++ b/clutter/Makefile.am
@@ -95,6 +95,7 @@ source_h = \
$(srcdir)/clutter-fixed-layout.h \
$(srcdir)/clutter-flow-layout.h \
$(srcdir)/clutter-frame-source.h \
+ $(srcdir)/clutter-gesture-action.h \
$(srcdir)/clutter-group.h \
$(srcdir)/clutter-input-device.h \
$(srcdir)/clutter-interval.h \
@@ -118,6 +119,7 @@ source_h = \
$(srcdir)/clutter-shader.h \
$(srcdir)/clutter-shader-effect.h \
$(srcdir)/clutter-shader-types.h \
+ $(srcdir)/clutter-swipe-action.h \
$(srcdir)/clutter-snap-constraint.h \
$(srcdir)/clutter-stage.h \
$(srcdir)/clutter-stage-manager.h \
@@ -178,6 +180,7 @@ source_c = \
$(srcdir)/clutter-flatten-effect.c \
$(srcdir)/clutter-flow-layout.c \
$(srcdir)/clutter-frame-source.c \
+ $(srcdir)/clutter-gesture-action.c \
$(srcdir)/clutter-group.c \
$(srcdir)/clutter-input-device.c \
$(srcdir)/clutter-interval.c \
@@ -202,6 +205,7 @@ source_c = \
$(srcdir)/clutter-shader.c \
$(srcdir)/clutter-shader-effect.c \
$(srcdir)/clutter-shader-types.c \
+ $(srcdir)/clutter-swipe-action.c \
$(srcdir)/clutter-snap-constraint.c \
$(srcdir)/clutter-stage.c \
$(srcdir)/clutter-stage-manager.c \
diff --git a/clutter/clutter-gesture-action.c b/clutter/clutter-gesture-action.c
new file mode 100644
index 000000000..1ab84eabe
--- /dev/null
+++ b/clutter/clutter-gesture-action.c
@@ -0,0 +1,500 @@
+/*
+ * Clutter.
+ *
+ * An OpenGL based 'interactive canvas' library.
+ *
+ * Copyright (C) 2010 Intel Corporation.
+ * Copyright (C) 2011 Robert Bosch Car Multimedia GmbH.
+ *
+ * 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 .
+ *
+ * Author:
+ * Tomeu Vizoso
+ */
+
+/**
+ * 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 signals #ClutterGestureAction::gesture-begin, #ClutterGestureAction::gesture-motion and
+ * #ClutterGestureAction::gesture-end.
+ *
+ * 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-motion", G_CALLBACK (on_gesture_motion), 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"
+
+struct _ClutterGestureActionPrivate
+{
+ ClutterActor *stage;
+
+ guint actor_capture_id;
+ gulong stage_capture_id;
+
+ gboolean in_drag;
+
+ gfloat press_x, press_y;
+ gfloat last_motion_x, last_motion_y;
+ gfloat release_x, release_y;
+};
+
+enum
+{
+ GESTURE_BEGIN,
+ GESTURE_PROGRESS,
+ GESTURE_END,
+ GESTURE_CANCEL,
+
+ LAST_SIGNAL
+};
+
+static guint gesture_signals[LAST_SIGNAL] = { 0, };
+
+G_DEFINE_ABSTRACT_TYPE (ClutterGestureAction, clutter_gesture_action,
+ CLUTTER_TYPE_ACTION);
+
+static gboolean
+signal_accumulator (GSignalInvocationHint *ihint,
+ GValue *return_accu,
+ const GValue *handler_return,
+ gpointer user_data)
+{
+ gboolean continue_emission;
+
+ continue_emission = g_value_get_boolean (handler_return);
+ g_value_set_boolean (return_accu, continue_emission);
+
+ return continue_emission;
+}
+
+static void
+cancel_gesture (ClutterGestureAction *action)
+{
+ ClutterGestureActionPrivate *priv = action->priv;
+ ClutterActor *actor;
+
+ priv->in_drag = FALSE;
+
+ 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;
+ gboolean return_value;
+
+ actor = clutter_actor_meta_get_actor (CLUTTER_ACTOR_META (action));
+
+ switch (clutter_event_type (event))
+ {
+ case CLUTTER_MOTION:
+ {
+ 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 FALSE;
+
+ 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);
+ }
+ else
+ return FALSE;
+ }
+
+ g_signal_emit (action, gesture_signals[GESTURE_PROGRESS], 0, actor,
+ &return_value);
+ if (!return_value)
+ cancel_gesture (action);
+ }
+ break;
+
+ case CLUTTER_BUTTON_RELEASE:
+ {
+ clutter_event_get_coords (event, &priv->release_x, &priv->release_y);
+
+ g_signal_handler_disconnect (priv->stage, priv->stage_capture_id);
+ priv->stage_capture_id = 0;
+
+ if (priv->in_drag)
+ {
+ priv->in_drag = FALSE;
+ g_signal_emit (action, gesture_signals[GESTURE_END], 0, actor);
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+actor_captured_event_cb (ClutterActor *actor,
+ ClutterEvent *event,
+ ClutterGestureAction *action)
+{
+ ClutterGestureActionPrivate *priv = action->priv;
+
+ if (clutter_event_type (event) != CLUTTER_BUTTON_PRESS)
+ return FALSE;
+
+ if (!clutter_actor_meta_get_enabled (CLUTTER_ACTOR_META (action)))
+ return FALSE;
+
+ clutter_event_get_coords (event, &priv->press_x, &priv->press_y);
+
+ 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);
+
+ return FALSE;
+}
+
+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);
+
+ g_signal_handler_disconnect (old_actor, priv->actor_capture_id);
+ priv->actor_capture_id = 0;
+ }
+
+ if (priv->stage_capture_id != 0)
+ {
+ 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 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;
+
+ /**
+ * 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.
+ */
+ 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),
+ signal_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.
+ */
+ 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),
+ signal_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.
+ *
+ * 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);
+}
+
+/**
+ * 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: id of the device we are interested in
+ * @press_x: (out): return location for the press event's X coordinate
+ * @press_y: (out): 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));
+
+ if (device != 0)
+ {
+ g_warning ("Multi-device support not yet implemented");
+ return;
+ }
+
+ if (press_x)
+ *press_x = action->priv->press_x;
+
+ if (press_y)
+ *press_y = action->priv->press_y;
+}
+
+/**
+ * clutter_gesture_action_get_motion_coords:
+ * @action: a #ClutterGestureAction
+ * @motion_x: (out): return location for the latest motion
+ * event's X coordinate
+ * @motion_y: (out): 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));
+
+ if (device != 0)
+ {
+ g_warning ("Multi-device support not yet implemented");
+ return;
+ }
+
+ if (motion_x)
+ *motion_x = action->priv->last_motion_x;
+
+ if (motion_y)
+ *motion_y = action->priv->last_motion_y;
+}
+
+/**
+ * clutter_gesture_action_get_release_coords:
+ * @action: a #ClutterGestureAction
+ * @release_x: (out): return location for the X coordinate of the last release
+ * @release_y: (out): 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));
+
+ if (device != 0)
+ {
+ g_warning ("Multi-device support not yet implemented");
+ return;
+ }
+
+ if (release_x)
+ *release_x = action->priv->release_x;
+
+ if (release_y)
+ *release_y = action->priv->release_y;
+}
+
+/**
+ * clutter_gesture_action_set_required_devices:
+ * @action: a #ClutterGestureAction
+ * @n_required_devices: the number of pointer devices that are to be tracked
+ *
+ * Sets the number of pointer devices that are to be tracked by this gesture.
+ *
+ * Since: 1.8
+ */
+void
+clutter_gesture_action_set_required_devices (ClutterGestureAction *action,
+ guint n_required_devices)
+{
+ if (n_required_devices != 1)
+ {
+ g_warning ("Multi-device support not yet implemented");
+ return;
+ }
+}
+
+/**
+ * clutter_gesture_action_get_required_devices:
+ * @action: a #ClutterGestureAction
+ *
+ * Returns the number of devices tracked by this gesture.
+ *
+ * Since: 1.8
+ */
+guint
+clutter_gesture_action_get_required_devices (ClutterGestureAction *action)
+{
+ return 1;
+}
diff --git a/clutter/clutter-gesture-action.h b/clutter/clutter-gesture-action.h
new file mode 100644
index 000000000..e1f3efaa2
--- /dev/null
+++ b/clutter/clutter-gesture-action.h
@@ -0,0 +1,120 @@
+/*
+ * Clutter.
+ *
+ * An OpenGL based 'interactive canvas' library.
+ *
+ * Copyright (C) 2010 Intel Corporation.
+ * Copyright (C) 2011 Robert Bosch Car Multimedia GmbH.
+ *
+ * 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 .
+ *
+ * Author:
+ * Tomeu Vizoso
+ */
+
+#if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION)
+#error "Only can be included directly."
+#endif
+
+#ifndef __CLUTTER_GESTURE_ACTION_H__
+#define __CLUTTER_GESTURE_ACTION_H__
+
+#include
+
+G_BEGIN_DECLS
+
+#define CLUTTER_TYPE_GESTURE_ACTION (clutter_gesture_action_get_type ())
+#define CLUTTER_GESTURE_ACTION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_GESTURE_ACTION, ClutterGestureAction))
+#define CLUTTER_IS_GESTURE_ACTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_GESTURE_ACTION))
+#define CLUTTER_GESTURE_ACTION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_GESTURE_ACTION, ClutterGestureActionClass))
+#define CLUTTER_IS_GESTURE_ACTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_GESTURE_ACTION))
+#define CLUTTER_GESTURE_ACTION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), CLUTTER_TYPE_GESTURE_ACTION, ClutterGestureActionClass))
+
+typedef struct _ClutterGestureAction ClutterGestureAction;
+typedef struct _ClutterGestureActionPrivate ClutterGestureActionPrivate;
+typedef struct _ClutterGestureActionClass ClutterGestureActionClass;
+
+/**
+ * ClutterGestureAction:
+ *
+ * The ClutterGestureAction structure contains
+ * only private data and should be accessed using the provided API
+ *
+ * Since: 1.8
+ */
+struct _ClutterGestureAction
+{
+ /*< private >*/
+ ClutterAction parent_instance;
+
+ ClutterGestureActionPrivate *priv;
+};
+
+/**
+ * ClutterGestureActionClass:
+ * @gesture_begin: class handler for the #ClutterGestureAction::gesture-begin signal
+ *
+ * Since: 1.8
+ */
+struct _ClutterGestureActionClass
+{
+ /*< private >*/
+ ClutterActionClass parent_class;
+
+ /*< public >*/
+ gboolean (* gesture_begin) (ClutterGestureAction *action,
+ ClutterActor *actor);
+ gboolean (* gesture_progress) (ClutterGestureAction *action,
+ ClutterActor *actor);
+ void (* gesture_end) (ClutterGestureAction *action,
+ ClutterActor *actor);
+ void (* gesture_cancel) (ClutterGestureAction *action,
+ ClutterActor *actor);
+
+ /*< private >*/
+ void (* _clutter_gesture_action1) (void);
+ void (* _clutter_gesture_action2) (void);
+ void (* _clutter_gesture_action3) (void);
+ void (* _clutter_gesture_action4) (void);
+ void (* _clutter_gesture_action5) (void);
+ void (* _clutter_gesture_action6) (void);
+ void (* _clutter_gesture_action7) (void);
+};
+
+GType clutter_gesture_action_get_type (void) G_GNUC_CONST;
+
+ClutterAction *clutter_gesture_action_new (void);
+
+void clutter_gesture_action_get_press_coords (ClutterGestureAction *action,
+ guint device,
+ gfloat *press_x,
+ gfloat *press_y);
+
+void clutter_gesture_action_get_motion_coords (ClutterGestureAction *action,
+ guint device,
+ gfloat *motion_x,
+ gfloat *motion_y);
+
+void clutter_gesture_action_get_release_coords (ClutterGestureAction *action,
+ guint device,
+ gfloat *release_x,
+ gfloat *release_y);
+
+void clutter_gesture_action_set_required_devices (ClutterGestureAction *action,
+ guint n_required_devices);
+guint clutter_gesture_action_get_required_devices (ClutterGestureAction *action);
+
+G_END_DECLS
+
+#endif /* __CLUTTER_GESTURE_ACTION_H__ */
diff --git a/clutter/clutter-marshal.list b/clutter/clutter-marshal.list
index 563508ffc..f9a604b6b 100644
--- a/clutter/clutter-marshal.list
+++ b/clutter/clutter-marshal.list
@@ -1,6 +1,7 @@
BOOLEAN:BOXED
BOOLEAN:OBJECT,ENUM
BOOLEAN:STRING,UINT,FLAGS
+BOOLEAN:OBJECT
BOXED:UINT,UINT
DOUBLE:VOID
UINT:VOID
diff --git a/clutter/clutter-swipe-action.c b/clutter/clutter-swipe-action.c
new file mode 100644
index 000000000..98b575f5e
--- /dev/null
+++ b/clutter/clutter-swipe-action.c
@@ -0,0 +1,249 @@
+/*
+ * Clutter.
+ *
+ * An OpenGL based 'interactive canvas' library.
+ *
+ * Copyright (C) 2010 Intel Corporation.
+ * Copyright (C) 2011 Robert Bosch Car Multimedia GmbH.
+ *
+ * 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 .
+ *
+ * Author:
+ * Tomeu Vizoso
+ *
+ * Based on ClutterDragAction, written by:
+ * Emmanuele Bassi
+ */
+
+/**
+ * SECTION:clutter-swipe-action
+ * @Title: ClutterSwipeAction
+ * @Short_Description: Action for swipe gestures
+ *
+ * #ClutterSwipeAction is a sub-class of #ClutterAction that implements
+ * the logic for recognizing swipe gestures. It listens for low level events
+ * such as #ClutterButtonEvent and #ClutterMotionEvent on the stage to raise
+ * the signals #ClutterSwipeAction::swipe-begin, #ClutterSwipeAction::swipe-motion and
+ * #ClutterSwipeAction::swipe-end.
+ *
+ * To use #ClutterSwipeAction you just need to apply it to a #ClutterActor
+ * using clutter_actor_add_action() and connect to the signals:
+ *
+ * |[
+ * ClutterAction *action = clutter_swipe_action_new ();
+ *
+ * clutter_actor_add_action (actor, action);
+ *
+ * g_signal_connect (action, "swipe-begin", G_CALLBACK (on_swipe_begin), NULL);
+ * g_signal_connect (action, "swipe-motion", G_CALLBACK (on_swipe_motion), NULL);
+ * g_signal_connect (action, "swipe-end", G_CALLBACK (on_swipe_end), NULL);
+ * ]|
+ *
+ * Since: 1.8
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "clutter-swipe-action.h"
+
+#include "clutter-debug.h"
+#include "clutter-enum-types.h"
+#include "clutter-marshal.h"
+#include "clutter-private.h"
+
+struct _ClutterSwipeActionPrivate
+{
+ ClutterSwipeDirection h_direction;
+ ClutterSwipeDirection v_direction;
+ gfloat last_motion_x;
+ gfloat last_motion_y;
+};
+
+enum
+{
+ SWEPT,
+
+ LAST_SIGNAL
+};
+
+static guint swipe_signals[LAST_SIGNAL] = { 0, };
+
+G_DEFINE_TYPE (ClutterSwipeAction, clutter_swipe_action,
+ CLUTTER_TYPE_GESTURE_ACTION);
+
+static gboolean
+gesture_begin (ClutterGestureAction *action,
+ ClutterActor *actor)
+{
+ ClutterSwipeActionPrivate *priv = CLUTTER_SWIPE_ACTION (action)->priv;
+
+ priv->h_direction = 0;
+ priv->v_direction = 0;
+
+ return TRUE;
+}
+
+static gboolean
+gesture_progress (ClutterGestureAction *action,
+ ClutterActor *actor)
+{
+ ClutterSwipeActionPrivate *priv = CLUTTER_SWIPE_ACTION (action)->priv;
+ gfloat press_x, press_y;
+ gfloat motion_x, motion_y;
+ gfloat delta_x, delta_y;
+ ClutterSwipeDirection h_direction = 0, v_direction = 0;
+ ClutterSettings *settings = clutter_settings_get_default ();
+ gint drag_threshold;
+
+ clutter_gesture_action_get_press_coords (action,
+ 0,
+ &press_x,
+ &press_y);
+
+ clutter_gesture_action_get_motion_coords (action,
+ 0,
+ &motion_x,
+ &motion_y);
+
+ delta_x = press_x - motion_x;
+ delta_y = press_y - motion_y;
+
+ g_object_get (settings,
+ "dnd-drag-threshold", &drag_threshold,
+ NULL);
+
+ if (delta_x >= drag_threshold)
+ h_direction = CLUTTER_SWIPE_DIRECTION_RIGHT;
+ else if (delta_x < -drag_threshold)
+ h_direction = CLUTTER_SWIPE_DIRECTION_LEFT;
+
+ if (delta_y >= drag_threshold)
+ v_direction = CLUTTER_SWIPE_DIRECTION_DOWN;
+ else if (delta_y < -drag_threshold)
+ v_direction = CLUTTER_SWIPE_DIRECTION_UP;
+
+ /* cancel gesture on direction reversal */
+
+ if (priv->h_direction == 0)
+ priv->h_direction = h_direction;
+
+ if (priv->v_direction == 0)
+ priv->v_direction = v_direction;
+
+ if (priv->h_direction != h_direction)
+ return FALSE;
+
+ if (priv->v_direction != v_direction)
+ return FALSE;
+
+ return TRUE;
+}
+
+
+static void
+gesture_end (ClutterGestureAction *action,
+ ClutterActor *actor)
+{
+ gfloat press_x, press_y;
+ gfloat release_x, release_y;
+ ClutterSwipeDirection direction = 0;
+ ClutterSettings *settings = clutter_settings_get_default ();
+ gint drag_threshold;
+
+ clutter_gesture_action_get_press_coords (CLUTTER_GESTURE_ACTION (action),
+ 0,
+ &press_x, &press_y);
+
+ clutter_gesture_action_get_release_coords (CLUTTER_GESTURE_ACTION (action),
+ 0,
+ &release_x, &release_y);
+
+ g_object_get (settings,
+ "dnd-drag-threshold", &drag_threshold,
+ NULL);
+
+ if (release_x - press_x > drag_threshold)
+ direction |= CLUTTER_SWIPE_DIRECTION_RIGHT;
+ else if (press_x - release_x > drag_threshold)
+ direction |= CLUTTER_SWIPE_DIRECTION_LEFT;
+
+ if (release_y - press_y > drag_threshold)
+ direction |= CLUTTER_SWIPE_DIRECTION_DOWN;
+ else if (press_y - release_y > drag_threshold)
+ direction |= CLUTTER_SWIPE_DIRECTION_UP;
+
+ g_signal_emit (action, swipe_signals[SWEPT], 0, actor, direction);
+}
+
+static void
+clutter_swipe_action_class_init (ClutterSwipeActionClass *klass)
+{
+ ClutterGestureActionClass *gesture_class =
+ CLUTTER_GESTURE_ACTION_CLASS (klass);
+
+ g_type_class_add_private (klass, sizeof (ClutterSwipeActionPrivate));
+
+ gesture_class->gesture_begin = gesture_begin;
+ gesture_class->gesture_progress = gesture_progress;
+ gesture_class->gesture_end = gesture_end;
+
+ /**
+ * ClutterSwipeAction::swept:
+ * @action: the #ClutterSwipeAction that emitted the signal
+ * @actor: the #ClutterActor attached to the @action
+ * @direction: the main direction of the swipe gesture
+ *
+ * The ::swept signal is emitted when a swipe gesture is recognized on the
+ * attached actor.
+ *
+ * Since: 1.8
+ */
+ swipe_signals[SWEPT] =
+ g_signal_new (I_("swept"),
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (ClutterSwipeActionClass, swept),
+ NULL, NULL,
+ _clutter_marshal_VOID__OBJECT_INT,
+ G_TYPE_NONE, 2,
+ CLUTTER_TYPE_ACTOR,
+ G_TYPE_INT);
+}
+
+static void
+clutter_swipe_action_init (ClutterSwipeAction *self)
+{
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, CLUTTER_TYPE_SWIPE_ACTION,
+ ClutterSwipeActionPrivate);
+
+ clutter_gesture_action_set_required_devices (CLUTTER_GESTURE_ACTION (self),
+ 1);
+}
+
+/**
+ * clutter_swipe_action_new:
+ *
+ * Creates a new #ClutterSwipeAction instance
+ *
+ * Return value: the newly created #ClutterSwipeAction
+ *
+ * Since: 1.8
+ */
+ClutterAction *
+clutter_swipe_action_new (void)
+{
+ return g_object_new (CLUTTER_TYPE_SWIPE_ACTION, NULL);
+}
diff --git a/clutter/clutter-swipe-action.h b/clutter/clutter-swipe-action.h
new file mode 100644
index 000000000..ed9246de2
--- /dev/null
+++ b/clutter/clutter-swipe-action.h
@@ -0,0 +1,117 @@
+/*
+ * Clutter.
+ *
+ * An OpenGL based 'interactive canvas' library.
+ *
+ * Copyright (C) 2010 Intel Corporation.
+ * Copyright (C) 2011 Robert Bosch Car Multimedia GmbH.
+ *
+ * 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 .
+ *
+ * Author:
+ * Tomeu Vizoso
+ *
+ * Based on ClutterDragAction, written by:
+ * Emmanuele Bassi
+ */
+
+#if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION)
+#error "Only can be included directly."
+#endif
+
+#ifndef __CLUTTER_SWIPE_ACTION_H__
+#define __CLUTTER_SWIPE_ACTION_H__
+
+#include
+
+G_BEGIN_DECLS
+
+#define CLUTTER_TYPE_SWIPE_ACTION (clutter_swipe_action_get_type ())
+#define CLUTTER_SWIPE_ACTION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_SWIPE_ACTION, ClutterSwipeAction))
+#define CLUTTER_IS_SWIPE_ACTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_SWIPE_ACTION))
+#define CLUTTER_SWIPE_ACTION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_SWIPE_ACTION, ClutterSwipeActionClass))
+#define CLUTTER_IS_SWIPE_ACTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_SWIPE_ACTION))
+#define CLUTTER_SWIPE_ACTION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), CLUTTER_TYPE_SWIPE_ACTION, ClutterSwipeActionClass))
+
+typedef struct _ClutterSwipeAction ClutterSwipeAction;
+typedef struct _ClutterSwipeActionPrivate ClutterSwipeActionPrivate;
+typedef struct _ClutterSwipeActionClass ClutterSwipeActionClass;
+
+/**
+ * ClutterSwipeDirection:
+ * @CLUTTER_SWIPE_DIRECTION_UP: Upwards swipe gesture
+ * @CLUTTER_SWIPE_DIRECTION_DOWN: Downwards swipe gesture
+ * @CLUTTER_SWIPE_DIRECTION_LEFT: Leftwards swipe gesture
+ * @CLUTTER_SWIPE_DIRECTION_RIGHT: Rightwards swipe gesture
+ *
+ * The main direction of the swipe gesture
+ *
+ * Since: 1.8
+ */
+typedef enum { /*< prefix=CLUTTER_SWIPE >*/
+ CLUTTER_SWIPE_DIRECTION_UP = 1 << 0,
+ CLUTTER_SWIPE_DIRECTION_DOWN = 1 << 1,
+ CLUTTER_SWIPE_DIRECTION_LEFT = 1 << 2,
+ CLUTTER_SWIPE_DIRECTION_RIGHT = 1 << 3
+} ClutterSwipeDirection;
+
+/**
+ * ClutterSwipeAction:
+ *
+ * The ClutterSwipeAction structure contains
+ * only private data and should be accessed using the provided API
+ *
+ * Since: 1.8
+ */
+struct _ClutterSwipeAction
+{
+ /*< private >*/
+ ClutterGestureAction parent_instance;
+
+ ClutterSwipeActionPrivate *priv;
+};
+
+/**
+ * ClutterSwipeActionClass:
+ * @swept: class handler for the #ClutterSwipeAction::swept signal
+ *
+ * Since: 1.8
+ */
+struct _ClutterSwipeActionClass
+{
+ /*< private >*/
+ ClutterGestureActionClass parent_class;
+
+ /*< public >*/
+ void (* swept) (ClutterSwipeAction *action,
+ ClutterActor *actor,
+ ClutterSwipeDirection direction);
+
+ /*< private >*/
+ void (* _clutter_swipe_action1) (void);
+ void (* _clutter_swipe_action2) (void);
+ void (* _clutter_swipe_action3) (void);
+ void (* _clutter_swipe_action4) (void);
+ void (* _clutter_swipe_action5) (void);
+ void (* _clutter_swipe_action6) (void);
+ void (* _clutter_swipe_action7) (void);
+};
+
+GType clutter_swipe_action_get_type (void) G_GNUC_CONST;
+
+ClutterAction *clutter_swipe_action_new (void);
+
+G_END_DECLS
+
+#endif /* __CLUTTER_SWIPE_ACTION_H__ */
diff --git a/clutter/clutter.h b/clutter/clutter.h
index 3729cf8eb..d64a9b722 100644
--- a/clutter/clutter.h
+++ b/clutter/clutter.h
@@ -73,6 +73,7 @@
#include "clutter-fixed-layout.h"
#include "clutter-flow-layout.h"
#include "clutter-frame-source.h"
+#include "clutter-gesture-action.h"
#include "clutter-group.h"
#include "clutter-input-device.h"
#include "clutter-interval.h"
@@ -95,6 +96,7 @@
#include "clutter-shader.h"
#include "clutter-shader-effect.h"
#include "clutter-shader-types.h"
+#include "clutter-swipe-action.h"
#include "clutter-snap-constraint.h"
#include "clutter-stage.h"
#include "clutter-stage-manager.h"
diff --git a/tests/interactive/Makefile.am b/tests/interactive/Makefile.am
index 5c04bf61e..38c748cfc 100644
--- a/tests/interactive/Makefile.am
+++ b/tests/interactive/Makefile.am
@@ -52,6 +52,7 @@ UNIT_TESTS = \
test-drag.c \
test-constraints.c \
test-scrolling.c \
+ test-swipe-action.c \
test-cogl-point-sprites.c \
test-table-layout.c \
test-path-constraint.c \
diff --git a/tests/interactive/test-swipe-action.c b/tests/interactive/test-swipe-action.c
new file mode 100644
index 000000000..2837554e5
--- /dev/null
+++ b/tests/interactive/test-swipe-action.c
@@ -0,0 +1,109 @@
+#include
+#include
+
+static guint VERTICAL = 0;
+static guint HORIZONTAL = 1;
+static guint BOTH = 2;
+
+static void
+swept_cb (ClutterSwipeAction *action,
+ ClutterActor *actor,
+ ClutterSwipeDirection direction,
+ guint axis)
+{
+ gchar *direction_str = "";
+
+ if (axis == HORIZONTAL &&
+ (direction == CLUTTER_SWIPE_DIRECTION_UP ||
+ direction == CLUTTER_SWIPE_DIRECTION_DOWN))
+ return;
+ else if (axis == VERTICAL &&
+ (direction == CLUTTER_SWIPE_DIRECTION_LEFT ||
+ direction == CLUTTER_SWIPE_DIRECTION_RIGHT))
+ return;
+
+ if (direction & CLUTTER_SWIPE_DIRECTION_UP)
+ direction_str = g_strconcat (direction_str, " up", NULL);
+
+ if (direction & CLUTTER_SWIPE_DIRECTION_DOWN)
+ direction_str = g_strconcat (direction_str, " down", NULL);
+
+ if (direction & CLUTTER_SWIPE_DIRECTION_LEFT)
+ direction_str = g_strconcat (direction_str, " left", NULL);
+
+ if (direction & CLUTTER_SWIPE_DIRECTION_RIGHT)
+ direction_str = g_strconcat (direction_str, " right", NULL);
+
+ g_debug ("swept_cb '%s'%s", clutter_actor_get_name (actor), direction_str);
+}
+
+static gboolean
+gesture_progress_cb (ClutterSwipeAction *action,
+ ClutterActor *actor,
+ gpointer user_data)
+{
+ return TRUE;
+}
+
+static void
+gesture_cancel_cb (ClutterSwipeAction *action,
+ ClutterActor *actor,
+ gpointer user_data)
+{
+ g_debug ("gesture_cancel_cb '%s'", clutter_actor_get_name (actor));
+}
+
+static void
+attach_action (ClutterActor *actor, guint axis)
+{
+ ClutterAction *action;
+
+ action = g_object_new (CLUTTER_TYPE_SWIPE_ACTION, NULL);
+ clutter_actor_add_action (actor, action);
+ g_signal_connect (action, "swept", G_CALLBACK (swept_cb), (gpointer) axis);
+ g_signal_connect (action, "gesture-progress", G_CALLBACK (gesture_progress_cb), NULL);
+ g_signal_connect (action, "gesture-cancel", G_CALLBACK (gesture_cancel_cb), NULL);
+}
+
+G_MODULE_EXPORT int
+test_swipe_action_main (int argc, char *argv[])
+{
+ ClutterActor *stage, *rect;
+
+ if (clutter_init (&argc, &argv) != CLUTTER_INIT_SUCCESS)
+ return 1;
+
+ stage = clutter_stage_get_default ();
+ clutter_stage_set_title (CLUTTER_STAGE (stage), "Swipe action test");
+ clutter_actor_set_size (stage, 640, 480);
+
+ rect = clutter_rectangle_new_with_color (CLUTTER_COLOR_Red);
+ clutter_actor_set_name (rect, "Vertical swipes");
+ clutter_actor_set_size (rect, 150, 150);
+ clutter_actor_set_position (rect, 10, 100);
+ clutter_actor_set_reactive (rect, TRUE);
+ clutter_container_add_actor (CLUTTER_CONTAINER (stage), rect);
+ attach_action (rect, VERTICAL);
+
+ rect = clutter_rectangle_new_with_color (CLUTTER_COLOR_Blue);
+ clutter_actor_set_name (rect, "Horizontal swipes");
+ clutter_actor_set_size (rect, 150, 150);
+ clutter_actor_set_position (rect, 170, 100);
+ clutter_actor_set_reactive (rect, TRUE);
+ clutter_container_add_actor (CLUTTER_CONTAINER (stage), rect);
+ attach_action (rect, HORIZONTAL);
+
+ rect = clutter_rectangle_new_with_color (CLUTTER_COLOR_Green);
+ clutter_actor_set_name (rect, "All swipes");
+ clutter_actor_set_size (rect, 150, 150);
+ clutter_actor_set_position (rect, 330, 100);
+ clutter_actor_set_reactive (rect, TRUE);
+ clutter_container_add_actor (CLUTTER_CONTAINER (stage), rect);
+ attach_action (rect, BOTH);
+
+ clutter_actor_show_all (stage);
+
+ clutter_main ();
+
+ return EXIT_SUCCESS;
+}