4 * An OpenGL based 'interactive canvas' library.
6 * Copyright (C) 2010 Intel Corporation.
7 * Copyright (C) 2011 Robert Bosch Car Multimedia GmbH.
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2 of the License, or (at your option) any later version.
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
23 * Tomeu Vizoso <tomeu.vizoso@collabora.co.uk>
27 * SECTION:clutter-gesture-action
28 * @Title: ClutterGestureAction
29 * @Short_Description: Action for gesture gestures
31 * #ClutterGestureAction is a sub-class of #ClutterAction that implements
32 * the logic for recognizing gesture gestures. It listens for low level events
33 * such as #ClutterButtonEvent and #ClutterMotionEvent on the stage to raise
34 * the #ClutterGestureAction::gesture-begin, #ClutterGestureAction::gesture-progress,
35 * and #ClutterGestureAction::gesture-end signals.
37 * To use #ClutterGestureAction you just need to apply it to a #ClutterActor
38 * using clutter_actor_add_action() and connect to the signals:
41 * ClutterAction *action = clutter_gesture_action_new ();
43 * clutter_actor_add_action (actor, action);
45 * g_signal_connect (action, "gesture-begin", G_CALLBACK (on_gesture_begin), NULL);
46 * g_signal_connect (action, "gesture-progress", G_CALLBACK (on_gesture_progress), NULL);
47 * g_signal_connect (action, "gesture-end", G_CALLBACK (on_gesture_end), NULL);
57 #include "clutter-gesture-action.h"
59 #include "clutter-debug.h"
60 #include "clutter-enum-types.h"
61 #include "clutter-marshal.h"
62 #include "clutter-private.h"
64 struct _ClutterGestureActionPrivate
68 guint actor_capture_id;
69 gulong stage_capture_id;
71 gfloat press_x, press_y;
72 gfloat last_motion_x, last_motion_y;
73 gfloat release_x, release_y;
88 static guint gesture_signals[LAST_SIGNAL] = { 0, };
90 G_DEFINE_TYPE (ClutterGestureAction, clutter_gesture_action, CLUTTER_TYPE_ACTION);
93 signal_accumulator (GSignalInvocationHint *ihint,
95 const GValue *handler_return,
98 gboolean continue_emission;
100 continue_emission = g_value_get_boolean (handler_return);
101 g_value_set_boolean (return_accu, continue_emission);
103 return continue_emission;
107 cancel_gesture (ClutterGestureAction *action)
109 ClutterGestureActionPrivate *priv = action->priv;
112 priv->in_drag = FALSE;
114 g_signal_handler_disconnect (priv->stage, priv->stage_capture_id);
115 priv->stage_capture_id = 0;
117 actor = clutter_actor_meta_get_actor (CLUTTER_ACTOR_META (action));
118 g_signal_emit (action, gesture_signals[GESTURE_CANCEL], 0, actor);
122 stage_captured_event_cb (ClutterActor *stage,
124 ClutterGestureAction *action)
126 ClutterGestureActionPrivate *priv = action->priv;
128 gboolean return_value;
130 actor = clutter_actor_meta_get_actor (CLUTTER_ACTOR_META (action));
132 switch (clutter_event_type (event))
136 ClutterModifierType mods = clutter_event_get_state (event);
138 /* we might miss a button-release event in case of grabs,
139 * so we need to check whether the button is still down
140 * during a motion event
142 if (!(mods & CLUTTER_BUTTON1_MASK))
144 cancel_gesture (action);
145 return CLUTTER_EVENT_PROPAGATE;
148 clutter_event_get_coords (event, &priv->last_motion_x,
149 &priv->last_motion_y);
151 if (!clutter_actor_transform_stage_point (actor,
155 return CLUTTER_EVENT_PROPAGATE;
160 ClutterSettings *settings = clutter_settings_get_default ();
162 g_object_get (settings,
163 "dnd-drag-threshold", &drag_threshold,
166 if ((ABS (priv->press_y - priv->last_motion_y) >= drag_threshold) ||
167 (ABS (priv->press_x - priv->last_motion_x) >= drag_threshold))
169 priv->in_drag = TRUE;
171 g_signal_emit (action, gesture_signals[GESTURE_BEGIN], 0, actor,
175 cancel_gesture (action);
176 return CLUTTER_EVENT_PROPAGATE;
180 return CLUTTER_EVENT_PROPAGATE;
183 g_signal_emit (action, gesture_signals[GESTURE_PROGRESS], 0, actor,
187 cancel_gesture (action);
188 return CLUTTER_EVENT_PROPAGATE;
193 case CLUTTER_BUTTON_RELEASE:
195 clutter_event_get_coords (event, &priv->release_x, &priv->release_y);
197 g_signal_handler_disconnect (priv->stage, priv->stage_capture_id);
198 priv->stage_capture_id = 0;
202 priv->in_drag = FALSE;
203 g_signal_emit (action, gesture_signals[GESTURE_END], 0, actor);
212 return CLUTTER_EVENT_PROPAGATE;
216 actor_captured_event_cb (ClutterActor *actor,
218 ClutterGestureAction *action)
220 ClutterGestureActionPrivate *priv = action->priv;
222 if (clutter_event_type (event) != CLUTTER_BUTTON_PRESS)
223 return CLUTTER_EVENT_PROPAGATE;
225 if (!clutter_actor_meta_get_enabled (CLUTTER_ACTOR_META (action)))
226 return CLUTTER_EVENT_PROPAGATE;
228 clutter_event_get_coords (event, &priv->press_x, &priv->press_y);
230 if (priv->stage == NULL)
231 priv->stage = clutter_actor_get_stage (actor);
233 priv->stage_capture_id =
234 g_signal_connect_after (priv->stage, "captured-event",
235 G_CALLBACK (stage_captured_event_cb),
238 return CLUTTER_EVENT_PROPAGATE;
242 clutter_gesture_action_set_actor (ClutterActorMeta *meta,
245 ClutterGestureActionPrivate *priv = CLUTTER_GESTURE_ACTION (meta)->priv;
246 ClutterActorMetaClass *meta_class =
247 CLUTTER_ACTOR_META_CLASS (clutter_gesture_action_parent_class);
249 if (priv->actor_capture_id != 0)
251 ClutterActor *old_actor = clutter_actor_meta_get_actor (meta);
253 if (old_actor != NULL)
254 g_signal_handler_disconnect (old_actor, priv->actor_capture_id);
256 priv->actor_capture_id = 0;
259 if (priv->stage_capture_id != 0)
261 if (priv->stage != NULL)
262 g_signal_handler_disconnect (priv->stage, priv->stage_capture_id);
264 priv->stage_capture_id = 0;
270 priv->actor_capture_id =
271 g_signal_connect (actor, "captured-event",
272 G_CALLBACK (actor_captured_event_cb),
276 meta_class->set_actor (meta, actor);
280 default_event_handler (ClutterGestureAction *action,
287 clutter_gesture_action_class_init (ClutterGestureActionClass *klass)
289 ClutterActorMetaClass *meta_class = CLUTTER_ACTOR_META_CLASS (klass);
291 g_type_class_add_private (klass, sizeof (ClutterGestureActionPrivate));
293 meta_class->set_actor = clutter_gesture_action_set_actor;
295 klass->gesture_begin = default_event_handler;
296 klass->gesture_progress = default_event_handler;
299 * ClutterGestureAction::gesture-begin:
300 * @action: the #ClutterGestureAction that emitted the signal
301 * @actor: the #ClutterActor attached to the @action
303 * The ::gesture_begin signal is emitted when the #ClutterActor to which
304 * a #ClutterGestureAction has been applied starts receiving a gesture.
306 * Return value: %TRUE if the gesture should start, and %FALSE if
307 * the gesture should be ignored.
311 gesture_signals[GESTURE_BEGIN] =
312 g_signal_new (I_("gesture-begin"),
313 G_TYPE_FROM_CLASS (klass),
315 G_STRUCT_OFFSET (ClutterGestureActionClass, gesture_begin),
316 signal_accumulator, NULL,
317 _clutter_marshal_BOOLEAN__OBJECT,
322 * ClutterGestureAction::gesture-progress:
323 * @action: the #ClutterGestureAction that emitted the signal
324 * @actor: the #ClutterActor attached to the @action
326 * The ::gesture-progress signal is emitted for each motion event after
327 * the #ClutterGestureAction::gesture-begin signal has been emitted.
329 * Return value: %TRUE if the gesture should continue, and %FALSE if
330 * the gesture should be cancelled.
334 gesture_signals[GESTURE_PROGRESS] =
335 g_signal_new (I_("gesture-progress"),
336 G_TYPE_FROM_CLASS (klass),
338 G_STRUCT_OFFSET (ClutterGestureActionClass, gesture_progress),
339 signal_accumulator, NULL,
340 _clutter_marshal_BOOLEAN__OBJECT,
345 * ClutterGestureAction::gesture-end:
346 * @action: the #ClutterGestureAction that emitted the signal
347 * @actor: the #ClutterActor attached to the @action
349 * The ::gesture-end signal is emitted at the end of the gesture gesture,
350 * when the pointer's button is released
352 * This signal is emitted if and only if the #ClutterGestureAction::gesture-begin
353 * signal has been emitted first.
357 gesture_signals[GESTURE_END] =
358 g_signal_new (I_("gesture-end"),
359 G_TYPE_FROM_CLASS (klass),
361 G_STRUCT_OFFSET (ClutterGestureActionClass, gesture_end),
363 _clutter_marshal_VOID__OBJECT,
368 * ClutterGestureAction::gesture-cancel:
369 * @action: the #ClutterGestureAction that emitted the signal
370 * @actor: the #ClutterActor attached to the @action
372 * The ::gesture-cancel signal is emitted when the ongoing gesture gets
373 * cancelled from the #ClutterGestureAction::gesture-progress signal handler.
375 * This signal is emitted if and only if the #ClutterGestureAction::gesture-begin
376 * signal has been emitted first.
380 gesture_signals[GESTURE_CANCEL] =
381 g_signal_new (I_("gesture-cancel"),
382 G_TYPE_FROM_CLASS (klass),
384 G_STRUCT_OFFSET (ClutterGestureActionClass, gesture_cancel),
386 _clutter_marshal_VOID__OBJECT,
392 clutter_gesture_action_init (ClutterGestureAction *self)
394 self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, CLUTTER_TYPE_GESTURE_ACTION,
395 ClutterGestureActionPrivate);
397 self->priv->press_x = self->priv->press_y = 0.f;
398 self->priv->last_motion_x = self->priv->last_motion_y = 0.f;
399 self->priv->release_x = self->priv->release_y = 0.f;
403 * clutter_gesture_action_new:
405 * Creates a new #ClutterGestureAction instance.
407 * Return value: the newly created #ClutterGestureAction
412 clutter_gesture_action_new (void)
414 return g_object_new (CLUTTER_TYPE_GESTURE_ACTION, NULL);
418 * clutter_gesture_action_get_press_coords:
419 * @action: a #ClutterGestureAction
420 * @device: currently unused, set to 0
421 * @press_x: (out): return location for the press event's X coordinate
422 * @press_y: (out): return location for the press event's Y coordinate
424 * Retrieves the coordinates, in stage space, of the press event
425 * that started the dragging for an specific pointer device
430 clutter_gesture_action_get_press_coords (ClutterGestureAction *action,
435 g_return_if_fail (CLUTTER_IS_GESTURE_ACTION (action));
438 g_warning ("Multi-device support not yet implemented");
441 *press_x = action->priv->press_x;
444 *press_y = action->priv->press_y;
448 * clutter_gesture_action_get_motion_coords:
449 * @action: a #ClutterGestureAction
450 * @device: currently unused, set to 0
451 * @motion_x: (out): return location for the latest motion
452 * event's X coordinate
453 * @motion_y: (out): return location for the latest motion
454 * event's Y coordinate
456 * Retrieves the coordinates, in stage space, of the latest motion
457 * event during the dragging
462 clutter_gesture_action_get_motion_coords (ClutterGestureAction *action,
467 g_return_if_fail (CLUTTER_IS_GESTURE_ACTION (action));
470 g_warning ("Multi-device support not yet implemented");
473 *motion_x = action->priv->last_motion_x;
476 *motion_y = action->priv->last_motion_y;
480 * clutter_gesture_action_get_release_coords:
481 * @action: a #ClutterGestureAction
482 * @device: currently unused, set to 0
483 * @release_x: (out): return location for the X coordinate of the last release
484 * @release_y: (out): return location for the Y coordinate of the last release
486 * Retrieves the coordinates, in stage space, of the point where the pointer
487 * device was last released.
492 clutter_gesture_action_get_release_coords (ClutterGestureAction *action,
497 g_return_if_fail (CLUTTER_IS_GESTURE_ACTION (action));
500 g_warning ("Multi-device support not yet implemented");
503 *release_x = action->priv->release_x;
506 *release_y = action->priv->release_y;