clutter-gesture-0.0.2.1/000077500000000000000000000000001132100660000147735ustar00rootroot00000000000000clutter-gesture-0.0.2.1/AUTHORS000066400000000000000000000000001132100660000160310ustar00rootroot00000000000000clutter-gesture-0.0.2.1/ChangeLog000066400000000000000000000000001132100660000165330ustar00rootroot00000000000000clutter-gesture-0.0.2.1/Makefile.am000066400000000000000000000017761132100660000170420ustar00rootroot00000000000000# # This file is part of Clutter-Gesture. # # Copyright 2009 (c) Intel Corp. # Author: Long Bu (long.bu@intel.com) # # Clutter-Gesture 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.1 of the License, # or (at your option) any later version. # # Clutter-Gesture 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 ClutterGesture; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 # USA. # EXTRA_DIST = autogen.sh SUBDIRS = engine clutter-gesture tests pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = clutter-gesture.pc DISTCLEANFILES = CLEANFILES = *~ clutter-gesture-0.0.2.1/NEWS000066400000000000000000000000001132100660000154600ustar00rootroot00000000000000clutter-gesture-0.0.2.1/README000066400000000000000000000004251132100660000156540ustar00rootroot00000000000000Clutter Gesture Library This library allows clutter applications to be aware of gestures and to easily attach some handlers to the gesture events. Please see INSTALL for how to install this library. For first time building this library. Run: ./autogen.sh make make install clutter-gesture-0.0.2.1/autogen.sh000077500000000000000000000017651132100660000170050ustar00rootroot00000000000000#!/bin/sh ### # This file is part of Clutter-Gesture. # # Copyright 2009 (c) Intel Corp. # Author: Long Bu (long.bu@intel.com) # # Clutter-Gesture 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.1 of the License, # or (at your option) any later version. # # Clutter-Gesture 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 ClutterGesture; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 # USA. ###/ [ -e config.cache ] && rm -f config.cache libtoolize --automake #gtkdocize || exit 1 aclocal autoconf autoheader automake -a ./configure $@ exit clutter-gesture-0.0.2.1/clutter-gesture.pc.in000066400000000000000000000004261132100660000210640ustar00rootroot00000000000000prefix=@prefix@ exec_prefix=@exec_prefix@ libdir=@libdir@ includedir=@includedir@ modlibexecdir=@modlibexecdir@ Name: clutter-gesture Description: Clutter Gesture library Version: @PACKAGE_VERSION@ Libs: -L${libdir} -lcluttergesture-@PACKAGE_VERSION@ Cflags: -I${includedir}/ clutter-gesture-0.0.2.1/clutter-gesture/000077500000000000000000000000001132100660000201315ustar00rootroot00000000000000clutter-gesture-0.0.2.1/clutter-gesture/Makefile.am000066400000000000000000000031511132100660000221650ustar00rootroot00000000000000# # This file is part of Clutter-Gesture. # # Copyright 2009 (c) Intel Corp. # Author: Long Bu (long.bu@intel.com) # # Clutter-Gesture 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.1 of the License, # or (at your option) any later version. # # Clutter-Gesture 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 ClutterGesture; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 # USA. EXTRA_DIST = autogen.sh pkgconfigdir = $(libdir)/pkgconfig #pkgconfig_DATA = cluttergesture.pc library_includedir=$(includedir)/clutter-gesture library_include_HEADERS = clutter-gesture.h gesture-events.h #Tell library where data directory is AM_CFLAGS = -g @CLUTTERGESTURE_CFLAGS@ -DPKGDATADIR="\"$(pkgdatadir)\"" -I./ -I../engine INCLUDES = @CLUTTERGESTURE_CFLAGS@ lib_LTLIBRARIES = libcluttergesture-@PACKAGE_VERSION@.la libcluttergesture_@PACKAGE_VERSION@_la_SOURCES = clutter-gesture.c clutter-gesture.h \ marshal.c marshal.h ../engine/engine.h libcluttergesture_@PACKAGE_VERSION@_la_LIBADD = @CLUTTERGESTURE_LIBS@ ../engine/libengine.la libcluttergesture_@PACKAGE_VERSION@_la_LDFLAGS= -version-info 0:0:0 -no-undefined DISTCLEANFILES = CLEANFILES = *~ cluttergesture.pc clutter-gesture-0.0.2.1/clutter-gesture/clutter-gesture.c000066400000000000000000000514021132100660000234350ustar00rootroot00000000000000/*** * This file is part of Clutter-Gesture. * * Copyright 2009 (c) Intel Corp. * Author: Long Bu (long.bu@intel.com) * * Referenced clutter code * Copyright (C) 2006 OpenedHand * * Clutter-Gesture 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.1 of the License, * or (at your option) any later version. * * Clutter-Gesture 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 ClutterGesture; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * USA. ***/ #include #include "math.h" #include #include #include #include #include #include #include #include "clutter-gesture.h" #include "marshal.h" #include "engine.h" G_DEFINE_TYPE (ClutterGesture, clutter_gesture, G_TYPE_OBJECT); #define CLUTTER_GESTURE_GET_PRIVATE(obj) \ (G_TYPE_INSTANCE_GET_PRIVATE ((obj), CLUTTER_TYPE_GESTURE, ClutterGesturePrivate)) enum { GEST_SLIDE, GEST_HOLD, GEST_PINCH, GEST_ROTATE, GEST_NAVIGATE, GEST_ANY, LAST_SIGNAL }; static guint gesture_signals[LAST_SIGNAL] = { 0, }; enum { PROP_0, PROP_GESTURE_ACTOR, PROP_GESTURE_MASK, PROP_TOUCH_HOLD_TIMEOUT, PROP_TOUCH_HOLD_RADIUS }; struct _ClutterGesturePrivate { ClutterActor *root_actor; gulong handler_id; handle_t gesture_handle; GSList *actors; /* interesting list of actores */ }; static gboolean recognize_cb (ClutterActor *target_actor, event_type_t event_type, guint flag, GSList *event_list, gint number, void *data); static gboolean captured_event_cb (ClutterActor *root_actor, ClutterEvent *event, gpointer data); static GMutex *lock = NULL; static GSList *root_actors = NULL; static gboolean is_a_child(ClutterActor *parent, ClutterActor *child) { ClutterActor *t; if (!parent || !child) return FALSE; for (t = child; t; t = clutter_actor_get_parent(t)) { if (t == parent) return TRUE; } return FALSE; } static ClutterActor * find_actor(GSList *list, ClutterActor *actor) { GSList *l; for (l = list; l; l = l->next) { ClutterActor *stored_actor = l->data; if (stored_actor == actor) return actor; } return NULL; } /* which actor is interested in which gestures */ gboolean clutter_gesture_set_gesture_mask (ClutterGesture *gesture, ClutterActor *actor, gint mask) { ClutterGesturePrivate *priv; g_return_val_if_fail (IS_CLUTTER_GESTURE (gesture), FALSE); priv = gesture->priv; if (!actor) return FALSE; /* The actor must be a child of the root_actor. * Otherwise, no event can be captured for the actor */ if (!is_a_child(priv->root_actor, actor)) return FALSE; if (find_actor (priv->actors, actor) == NULL) priv->actors = g_slist_append(priv->actors, actor); if (set_gesture_mask (priv->gesture_handle, actor, mask) == 0) return TRUE; else return FALSE; } static void clutter_gesture_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { ClutterGesture *gesture; ClutterGesturePrivate *priv; handle_t gesture_handle; ClutterActor *actor; gesture = CLUTTER_GESTURE (object); priv = gesture->priv; switch (prop_id) { case PROP_GESTURE_ACTOR: if (priv->root_actor != NULL) return; /*By default no gestures should be interesting */ gesture_handle = engine_init (recognize_cb, gesture); if (!gesture_handle) { g_object_unref (gesture); return; } priv->gesture_handle = gesture_handle; actor = g_value_get_object (value); priv->root_actor = actor; priv->handler_id = g_signal_connect (G_OBJECT (actor), "captured-event", G_CALLBACK (captured_event_cb), gesture); root_actors = g_slist_prepend (root_actors, actor); break; case PROP_GESTURE_MASK: clutter_gesture_set_gesture_mask (gesture, priv->root_actor, g_value_get_uint (value)); break; case PROP_TOUCH_HOLD_TIMEOUT: clutter_gesture_set_hold_timeout (gesture, priv->root_actor, g_value_get_uint (value)); break; case PROP_TOUCH_HOLD_RADIUS: clutter_gesture_set_hold_radius (gesture, priv->root_actor, g_value_get_uint (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void clutter_gesture_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { ClutterGesture *gesture; ClutterGesturePrivate *priv; guint mask, timeout, radius; gesture = CLUTTER_GESTURE (object); priv = gesture->priv; switch (prop_id) { case PROP_GESTURE_ACTOR: g_value_set_object (value, priv->root_actor); break; case PROP_GESTURE_MASK: get_gesture_mask (priv->gesture_handle, priv->root_actor, &mask); g_value_set_uint (value, mask); break; case PROP_TOUCH_HOLD_TIMEOUT: get_hold_timeout (priv->gesture_handle, priv->root_actor, &timeout); g_value_set_uint (value, timeout); break; case PROP_TOUCH_HOLD_RADIUS: get_hold_radius (priv->gesture_handle, priv->root_actor, &radius); g_value_set_uint (value, radius); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void clutter_gesture_finalize (GObject *object) { ClutterGestureClass *klass; ClutterGesture *gesture; gesture = CLUTTER_GESTURE(object); klass = CLUTTER_GESTURE_GET_CLASS(gesture); if (klass) { ClutterGesturePrivate *priv; priv = gesture->priv; g_mutex_lock (lock); root_actors = g_slist_remove (root_actors, priv->root_actor); g_mutex_unlock (lock); g_slist_free (priv->actors); engine_shutdown (priv->gesture_handle); g_signal_handler_disconnect (priv->root_actor, priv->handler_id); } G_OBJECT_CLASS (clutter_gesture_parent_class)->finalize (object); } #if 0 gboolean _clutter_boolean_handled_accumulator (GSignalInvocationHint *ihint, GValue *return_accu, const GValue *handler_return, gpointer dummy) { gboolean continue_emission; gboolean signal_handled; signal_handled = g_value_get_boolean (handler_return); g_value_set_boolean (return_accu, signal_handled); continue_emission = !signal_handled; return continue_emission; } #endif static void clutter_gesture_class_init (ClutterGestureClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); GParamSpec *pspec; object_class->finalize = clutter_gesture_finalize; object_class->set_property = clutter_gesture_set_property; object_class->get_property = clutter_gesture_get_property; if (!g_thread_supported ()) g_thread_init(NULL); lock = g_mutex_new(); g_type_class_add_private (klass, sizeof (ClutterGesturePrivate)); gesture_signals[GEST_SLIDE] = g_signal_new ("gesture-slide-event", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (ClutterGestureClass, gesture_slide_event), NULL, NULL, g_cclosure_marshal_VOID__POINTER, G_TYPE_NONE, 1, G_TYPE_POINTER); gesture_debug("slide signal:%d\n", gesture_signals[GEST_SLIDE]); gesture_signals[GEST_HOLD] = g_signal_new ("gesture-hold-event", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (ClutterGestureClass, gesture_hold_event), NULL, NULL, _cluttergesture_marshal_BOOLEAN__POINTER, G_TYPE_BOOLEAN, 1, G_TYPE_POINTER); gesture_debug("hold signal:%d\n", gesture_signals[GEST_HOLD]); gesture_signals[GEST_PINCH] = g_signal_new ("gesture-pinch-event", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (ClutterGestureClass, gesture_pinch_event), NULL, NULL, g_cclosure_marshal_VOID__POINTER, G_TYPE_NONE, 1, G_TYPE_POINTER); gesture_debug("pinch signal:%d\n", gesture_signals[GEST_PINCH]); gesture_signals[GEST_ROTATE] = g_signal_new ("gesture-rotate-event", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (ClutterGestureClass, gesture_rotate_event), NULL, NULL, g_cclosure_marshal_VOID__POINTER, G_TYPE_NONE, 1, G_TYPE_POINTER); gesture_debug("rotate signal:%d\n", gesture_signals[GEST_ROTATE]); gesture_signals[GEST_NAVIGATE] = g_signal_new ("gesture-navigate-event", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (ClutterGestureClass, gesture_navigate_event), NULL, NULL, g_cclosure_marshal_VOID__POINTER, G_TYPE_NONE, 1, G_TYPE_POINTER); gesture_debug("navigate signal:%d\n", gesture_signals[GEST_NAVIGATE]); gesture_signals[GEST_ANY] = g_signal_new ("gesture-any-event", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (ClutterGestureClass, gesture_any_event), NULL, NULL, g_cclosure_marshal_VOID__POINTER, G_TYPE_NONE, 1, G_TYPE_POINTER); gesture_debug("any signal:%d\n", gesture_signals[GEST_ANY]); pspec = g_param_spec_object ("actor", "actor", "the actor on which we do gesture recognition", CLUTTER_TYPE_ACTOR, G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_STATIC_NICK | G_PARAM_STATIC_NAME | G_PARAM_STATIC_BLURB); g_object_class_install_property (object_class, PROP_GESTURE_ACTOR, pspec); pspec = g_param_spec_uint ("gesture-mask", "gesture-mask", "bit-map mask to enable/disable gestures", 0x0, 0xffffffff, 0x0, G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_STATIC_NICK | G_PARAM_STATIC_NAME | G_PARAM_STATIC_BLURB); g_object_class_install_property (object_class, PROP_GESTURE_MASK, pspec); pspec = g_param_spec_uint ("touch-hold-timeout", "touch-hold-timeout", "timeout value for touch-hold in millisecond", 200, 2000, DEFAULT_TOUCH_HOLD_INTERVAL, G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_STATIC_NICK | G_PARAM_STATIC_NAME | G_PARAM_STATIC_BLURB); g_object_class_install_property (object_class, PROP_TOUCH_HOLD_TIMEOUT, pspec); pspec = g_param_spec_uint ("touch-hold-radius", "touch-hold-radius", "radius for touch-hold in pixel", 5, 100, DEFAULT_TOUCH_HOLD_RADIUS, G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_STATIC_NICK | G_PARAM_STATIC_NAME | G_PARAM_STATIC_BLURB); g_object_class_install_property (object_class, PROP_TOUCH_HOLD_RADIUS, pspec); } static void clutter_gesture_init (ClutterGesture *gesture) { gesture->priv = G_TYPE_INSTANCE_GET_PRIVATE (gesture, CLUTTER_TYPE_GESTURE, ClutterGesturePrivate); } static gboolean captured_event_cb (ClutterActor *root_actor, ClutterEvent *event, gpointer data) { ClutterGesturePrivate *priv; ClutterGesture *gesture; GSList *node; gboolean ret; gesture_debug("root_captured_event_cb\n"); gesture = (ClutterGesture *)data; priv = gesture->priv; { ClutterActor *stage = CLUTTER_ACTOR(event->any.stage); stage = NULL; } ret = FALSE; if (event->any.flags & (1<<31)) return ret; /* this event has been through the engine */ /* Check if the event->any.source is a child of any actors in the list * of interesting actors */ for (node = priv->actors; node; node = g_slist_next(node)) { ClutterActor *interesting_actor; if (!node->data) continue; interesting_actor = (ClutterActor *)(node->data); if (is_a_child(interesting_actor, event->any.source)) { ret = fill_event(priv->gesture_handle, interesting_actor, event); break; } } if (!ret) { /* The event is not eaten by the engine. * Pass it alone*/ return FALSE; } else { /* Before we return true. We need to pass * the event to other instance of ClutterGesture. * Otherwise, the captured_event_cb of other ClutterGesture instance * may not have a change to be called */ ClutterGestureClass *klass; gboolean dummy; klass = CLUTTER_GESTURE_GET_CLASS(gesture); g_mutex_lock(lock); for (node = root_actors; node; node = g_slist_next(node)) { ClutterActor *root_actor = (ClutterActor *)node->data; if (root_actor != priv->root_actor) { /*don't need to send this event to myself */ g_signal_emit_by_name (root_actor, "catpured-event", event, &dummy); } } g_mutex_unlock(lock); return TRUE; } } static void event_list_free (GSList *l) { GSList *node; for (node = l; node; node = g_slist_next(node)) { ClutterEvent *event = (ClutterEvent *)(node->data); clutter_event_free(event); } g_slist_free(l); } static inline void emit_event (ClutterEvent *event, gboolean is_key_event) { static gboolean lock = FALSE; GPtrArray *event_tree = NULL; ClutterActor *actor; gint i = 0; if (!event->any.source) { g_warning("No source set, discarding event"); return; } /* reentrancy check */ if (lock != FALSE) { g_warning ("Tried emitting event during event delivery, bailing out.n"); return; } lock = TRUE; event_tree = g_ptr_array_sized_new (64); actor = event->any.source; /* Build 'tree' of emitters for the event */ while (actor) { ClutterActor *parent; parent = clutter_actor_get_parent (actor); if (clutter_actor_get_reactive (actor) || parent == NULL || /* stage gets all events */ is_key_event) /* keyboard events are always emitted */ { g_ptr_array_add (event_tree, g_object_ref (actor)); } actor = parent; } /* Capture */ for (i = event_tree->len - 1; i >= 0; i--) if (clutter_actor_event (g_ptr_array_index (event_tree, i), event, TRUE)) goto done; /* Bubble */ for (i = 0; i < event_tree->len; i++) if (clutter_actor_event (g_ptr_array_index (event_tree, i), event, FALSE)) goto done; done: for (i = 0; i < event_tree->len; i++) g_object_unref (g_ptr_array_index (event_tree, i)); g_ptr_array_free (event_tree, TRUE); lock = FALSE; } typedef struct { GSList *event_list; guint flag; } _event_param_t; static gboolean process_clutter_event (gpointer data) { GSList *node; _event_param_t *event_param = (_event_param_t *)data; GSList *event_list = event_param->event_list; guint flag = event_param->flag; gesture_debug("process_clutter_event: type: CLUTTER_EVENT\n"); for (node = event_list; node; node = g_slist_next(node)) { ClutterEvent *event = (ClutterEvent *)(node->data); event->any.flags |= (1<<31); /* indicates that this event has been processed by the engine */ if (flag & IN_MT_GESTURE) event->any.flags |= GESTURE_IN_PROGRESS; gesture_debug("process_clutter_event: do_event\n"); g_assert(event->type == CLUTTER_MOTION || event->type == CLUTTER_BUTTON_PRESS || event->type == CLUTTER_BUTTON_RELEASE || event->type == CLUTTER_SCROLL); emit_event (event, FALSE); // clutter_do_event (event); } event_list_free(event_list); g_free(event_param); return FALSE; } static GSList * event_list_copy (GSList *l) { GSList *node; GSList *ret = NULL; for (node = l; node; node = g_slist_next(node)) { ClutterEvent *event = clutter_event_copy(node->data); ret = g_slist_append(ret, event); } return ret; } static gboolean recognize_cb (ClutterActor *target_actor, event_type_t event_type, guint flag, GSList *event_list, gint number, void *data) { GSList *node; gboolean result = FALSE; ClutterGesture *gesture = (ClutterGesture *)data; gesture_debug("recognized_cb gets called, type:%d, gesture:%p\n", event_type, data); if (event_type == GESTURE_EVENT) { gesture_debug("recognized_cb: type: GESTURE_EVENT, number:%d\n", number); for (node = event_list; node; node = g_slist_next(node)) { ClutterGestureEvent *event = (ClutterGestureEvent *)(node->data); switch (event->type) { case GESTURE_PINCH: g_signal_emit (gesture, gesture_signals[GEST_PINCH], 0, event, &result); break; case GESTURE_SLIDE: g_signal_emit (gesture, gesture_signals[GEST_SLIDE], 0, event, &result); break; case GESTURE_HOLD: g_signal_emit (gesture, gesture_signals[GEST_HOLD], 0, event, &result); break; case GESTURE_ROTATE: g_signal_emit (gesture, gesture_signals[GEST_ROTATE], 0, event, &result); break; case GESTURE_NAVIGATE: g_signal_emit (gesture, gesture_signals[GEST_NAVIGATE], 0, event, &result); break; default: break; } /* if (event->type == GESTURE_PINCH || event->type == GESTURE_NAVIGATE || event->type == GESTURE_SLIDE || event->type == GESTURE_ROTATE || event->type == GESTURE_HOLD) { g_signal_emit (gesture, gesture_signals[GEST_ANY], 0, event, &result); } */ } } else if (event_type == CLUTTER_EVENT) { /* The event_list will be free before the timeout_cb gets called */ _event_param_t *event_param = g_new(_event_param_t, 1); event_param->event_list = event_list_copy(event_list); event_param->flag = flag; g_timeout_add(0, process_clutter_event, event_param); } return result; } ClutterGesture * clutter_gesture_new (ClutterActor *actor) { if (!actor) return NULL; return g_object_new (CLUTTER_TYPE_GESTURE, "actor", actor, NULL); } gboolean clutter_gesture_set_hold_timeout (ClutterGesture *gesture, ClutterActor *actor, guint interval) { ClutterGesturePrivate *priv; g_return_val_if_fail (IS_CLUTTER_GESTURE (gesture), FALSE); priv = gesture->priv; if (!actor) return FALSE; /* The actor must be a child of the root_actor. * Otherwise, no event can be captured for the actor */ if (!is_a_child(priv->root_actor, actor)) return FALSE; if (find_actor (priv->actors, actor) == NULL) return FALSE; if (set_hold_timeout (priv->gesture_handle, actor, interval) == 0) return TRUE; return FALSE; } gboolean clutter_gesture_set_hold_radius (ClutterGesture *gesture, ClutterActor *actor, guint radius) { ClutterGesturePrivate *priv; g_return_val_if_fail (IS_CLUTTER_GESTURE (gesture), FALSE); priv = gesture->priv; if (!actor) return FALSE; /* The actor must be a child of the root_actor. * Otherwise, no event can be captured for the actor */ if (!is_a_child(priv->root_actor, actor)) return FALSE; if (find_actor (priv->actors, actor) == NULL) return FALSE; if (set_hold_radius (priv->gesture_handle, actor, radius) == 0) return TRUE; return FALSE; } clutter-gesture-0.0.2.1/clutter-gesture/clutter-gesture.h000066400000000000000000000075541132100660000234530ustar00rootroot00000000000000/*** * This file is part of Clutter-Gesture. * * Copyright 2009 (c) Intel Corp. * Author: Long Bu (long.bu@intel.com) * * Referenced clutter code * Copyright (C) 2006 OpenedHand * * Clutter-Gesture 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.1 of the License, * or (at your option) any later version. * * Clutter-Gesture 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 ClutterGesture; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * USA. ***/ #ifndef __clutter_gesture_h__ #define __clutter_gesture_h__ #include "config.h" #include "gesture-events.h" typedef struct _ClutterGesture ClutterGesture; typedef struct _ClutterGestureClass ClutterGestureClass; typedef struct _ClutterGesturePrivate ClutterGesturePrivate; G_BEGIN_DECLS #define CLUTTER_TYPE_GESTURE (clutter_gesture_get_type ()) #define CLUTTER_GESTURE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_GESTURE, ClutterGesture)) #define CLUTTER_PAGE_GESTURE(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_GESTURE, ClutterGestureClass)) #define IS_CLUTTER_GESTURE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_GESTURE)) #define IS_CLUTTER_GESTURE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_GESTURE)) #define CLUTTER_GESTURE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_GESTURE, ClutterGestureClass)) GType clutter_gesture_get_type (void) G_GNUC_CONST; struct _ClutterGesture { /*< private >*/ GObject parent; ClutterGesturePrivate *priv; }; struct _ClutterGestureClass { /*< private >*/ GObjectClass parent_class; /*< public >*/ gboolean (* gesture_pinch_event) (ClutterGesture *gesture, ClutterGesturePinchEvent *event); gboolean (* gesture_rotate_event) (ClutterGesture *gesture, ClutterGestureRotateEvent *event); gboolean (* gesture_slide_event) (ClutterGesture *gesture, ClutterGestureSlideEvent *event); gboolean (* gesture_hold_event) (ClutterGesture *gesture, ClutterGestureHoldEvent *event); gboolean (* gesture_navigate_event) (ClutterGesture *gesture, ClutterGestureNavigateEvent *event); gboolean (* gesture_any_event) (ClutterGesture *gesture, ClutterGestureEvent *event); }; ClutterGesture * clutter_gesture_new (ClutterActor *actor); gboolean clutter_gesture_set_gesture_mask (ClutterGesture *gesture, ClutterActor *actor, gint mask); gboolean clutter_gesture_set_hold_timeout (ClutterGesture *gesture, ClutterActor *actor, guint interval); gboolean clutter_gesture_set_hold_radius (ClutterGesture *gesture, ClutterActor *actor, guint radius); typedef enum { GESTURE_MASK_SLIDE = 1<<0, GESTURE_MASK_HOLD = 1<<1, GESTURE_MASK_PINCH = 1<<2, GESTURE_MASK_ROTATE = 1<<3, GESTURE_MASK_NAVIGATE = 1<<4 } GestureMask; #define GESTURE_IN_PROGRESS (1<<30) #ifdef GESTURE_DEBUG #define gesture_debug(format, args...) do {\ fprintf(stderr, format, ##args); \ } while(0) #else #define gesture_debug(format, ...) #endif G_END_DECLS #endif clutter-gesture-0.0.2.1/clutter-gesture/gesture-events.h000066400000000000000000000144141132100660000232660ustar00rootroot00000000000000/*** * This file is part of Clutter-Gesture. * * Copyright 2009 (c) Intel Corp. * Author: Long Bu (long.bu@intel.com) * Forrest Zhao (forrest.zhao@intel.com) * * Referenced clutter code * Copyright (C) 2006 OpenedHand * * Clutter-Gesture 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.1 of the License, * or (at your option) any later version. * * Clutter-Gesture 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 ClutterGesture; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * USA. ***/ #ifndef __GESTURE_EVENTS_H__ #define __GESTURE_EVENTS_H__ typedef enum { GESTURE_SLIDE = 1, GESTURE_HOLD, GESTURE_PINCH, GESTURE_ROTATE, GESTURE_NAVIGATE, GESTURE_ANY } ClutterGestureEventType; typedef enum { SLIDE_UP = 1, SLIDE_DOWN, SLIDE_LEFT, SLIDE_RIGHT } ClutterGestureSlideDirection; typedef enum { LONG_PRESS_QUERY, LONG_PRESS_ACTION, LONG_PRESS_CANCEL } ClutterGestureTouchholdAction; typedef struct { ClutterGestureEventType type; //gesture event type, GESTURE_SLIDE guint32 time; //event time ClutterEventFlags flags; //event flags ClutterStage *stage; //event source stage ClutterActor *source; //event source actor guint32 start_time; //gesture start time gfloat x_start; //X coordinate of start point gfloat y_start; //Y coordinate of start point gfloat x_end; //X coordinate of end point gfloat y_end; //Y coordinate of end point ClutterGestureSlideDirection direction; //1: up, 2: down, 3: left, 4: right gint device_id; //the device id on which the slide is active } ClutterGestureSlideEvent; typedef struct { ClutterGestureEventType type; //gesture event type, GESTURE_HOLD guint32 time; //event time ClutterEventFlags flags; //event flags ClutterStage *stage; //event source stage ClutterActor *source; //event source actor gfloat x; //X coordinate of hold point gfloat y; //Y coordinate of hold point ClutterGestureTouchholdAction action; //touch&hold action }ClutterGestureHoldEvent; typedef struct { ClutterGestureEventType type; //gesture event type, GESTURE_PINCH guint32 time; //event time ClutterEventFlags flags; //event flags ClutterStage *stage; //event source stage ClutterActor *source; //event source actor guint32 start_time; //gesture start time gint x_start_1; //X coordinate of start point of first finger gint y_start_1; //Y coordinate of start point of first finger gint x_end_1; //X coordinate of end point of first finger gint y_end_1; //Y coordinate of end point of first finger gint x_start_2; //X coordinate of start point of second finger gint y_start_2; //Y coordinate of start point of second finger gint x_end_2; //X coordinate of end point of second finger gint y_end_2; //Y coordinate of end point of second finger } ClutterGesturePinchEvent; typedef struct { ClutterGestureEventType type; //gesture event type, GESTURE_ROTATE guint32 time; //event time ClutterEventFlags flags; //event flags ClutterStage *stage; //event source stage ClutterActor *source; //event source actor guint32 start_time; //gesture start time gint x_start_1; //X coordinate of start point of first finger gint y_start_1; //Y coordinate of start point of first finger gint x_end_1; //X coordinate of end point of first finger gint y_end_1; //Y coordinate of end point of first finger gint x_start_2; //X coordinate of start point of second finger gint y_start_2; //Y coordinate of start point of second finger gint x_end_2; //X coordinate of end point of second finger gint y_end_2; //Y coordinate of end point of second finger } ClutterGestureRotateEvent; typedef struct { ClutterGestureEventType type; //gesture event type, GESTURE_NAVIGATE guint32 time; //event time ClutterEventFlags flags; //event flags ClutterStage *stage; //event source stage ClutterActor *source; //event source actor guint32 start_time; //gesture start time gint x_start_1; //X coordinate of start point of first finger gint y_start_1; //Y coordinate of start point of first finger gint x_end_1; //X coordinate of end point of first finger gint y_end_1; //Y coordinate of end point of first finger gint x_start_2; //X coordinate of start point of second finger gint y_start_2; //Y coordinate of start point of second finger gint x_end_2; //X coordinate of end point of second finger gint y_end_2; //Y coordinate of end point of second finger } ClutterGestureNavigateEvent; typedef struct { ClutterGestureEventType type; //gesture event type, guint32 time; //event time ClutterEventFlags flags; //event flags ClutterStage *stage; //event source stage ClutterActor *source; //event source actor } ClutterGestureEvent; #endif clutter-gesture-0.0.2.1/clutter-gesture/marshal.c000066400000000000000000000027241132100660000217310ustar00rootroot00000000000000#include "marshal.h" #include #define g_marshal_value_peek_pointer(v) g_value_get_pointer (v) void _cluttergesture_marshal_BOOLEAN__POINTER (GClosure *closure, GValue *return_value G_GNUC_UNUSED, guint n_param_values, const GValue *param_values, gpointer invocation_hint G_GNUC_UNUSED, gpointer marshal_data) { typedef gboolean (*GMarshalFunc_BOOLEAN__POINTER) (gpointer data1, gpointer arg_1, gpointer data2); register GMarshalFunc_BOOLEAN__POINTER callback; register GCClosure *cc = (GCClosure*) closure; register gpointer data1, data2; gboolean v_return; g_return_if_fail (return_value != NULL); g_return_if_fail (n_param_values == 2); if (G_CCLOSURE_SWAP_DATA (closure)) { data1 = closure->data; data2 = g_value_peek_pointer (param_values + 0); } else { data1 = g_value_peek_pointer (param_values + 0); data2 = closure->data; } callback = (GMarshalFunc_BOOLEAN__POINTER) (marshal_data ? marshal_data : cc->callback); v_return = callback (data1, g_marshal_value_peek_pointer (param_values + 1), data2); g_value_set_boolean (return_value, v_return); } clutter-gesture-0.0.2.1/clutter-gesture/marshal.h000066400000000000000000000012241132100660000217300ustar00rootroot00000000000000#ifndef __MARSHAL_H__ #define __MARSHAL_H__ #include G_BEGIN_DECLS extern void _cluttergesture_marshal_BOOLEAN__POINTER (GClosure *closure, GValue *return_value, guint n_param_values, const GValue *param_values, gpointer invocation_hint, gpointer marshal_data); #define _cluttergesture_marshal_BOOL__POINTER _cluttergesture_marshal_BOOLEAN__POINTER G_END_DECLS #endif /* __MARSHAL_H__ */ clutter-gesture-0.0.2.1/configure.ac000066400000000000000000000052551132100660000172700ustar00rootroot00000000000000 # This file is part of Clutter-Gesture. # # Copyright 2009 (c) Intel Corp. # Author: Long Bu (long.bu@intel.com) # # Clutter-Gesture 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.1 of the License, # or (at your option) any later version. # # Clutter-Gesture 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 ClutterGesture; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 # USA. AC_PREREQ(2.61) # package version number (not shared library version) # odd micro numbers indicate in-progress development # even micro numbers indicate released versions m4_define(cluttergesture_version_major, 0) m4_define(cluttergesture_version_minor, 0) m4_define(cluttergesture_version_micro, 2) m4_define([cluttergesture_version], [cluttergesture_version_major.cluttergesture_version_minor.cluttergesture_version_micro]) m4_define([cluttergesture_api_version], [cluttergesture_version_major.cluttergesture_version_minor]) AC_INIT(cluttergesture, cluttergesture_version) AM_INIT_AUTOMAKE(cluttergesture, cluttergesture_version) AC_CONFIG_HEADERS(config.h) # Check for programs AC_PROG_LIBTOOL AC_PROG_CC AC_PROG_INSTALL AM_PROG_CC_C_O # enable pkg-config PKG_PROG_PKG_CONFIG PKG_CHECK_MODULES(CLUTTERGESTURE, glib-2.0 clutter-1.0 >= 1.0.0 gobject-2.0 glib-2.0) CLUTTERGESTURE_CFLAGS="$CLUTTERGESTURE_CFLAGS -Wall -Werror -fPIC" AC_SUBST(CLUTTERGESTURE_CFLAGS) AC_SUBST(CLUTTERGESTURE_LIBS) #GTK_DOC_CHECK([1.9]) # i18n ALL_LINGUAS="en_US" AC_SUBST(ALL_LINGUAS) AM_GLIB_GNU_GETTEXT AC_DEFINE(GETTEXT_PACKAGE, ["cluttergesture"], [Package name for gettext]) AC_ARG_ENABLE([gesture-debug], AC_HELP_STRING([--enable-gesture-debug], [enable debug mode. This makes the application be verbose and impact the performance]), [AC_DEFINE(GESTURE_DEBUG, 1, [Enable Gesture Debug output])]) # Output files AC_OUTPUT([ Makefile clutter-gesture/Makefile tests/Makefile engine/Makefile clutter-gesture.pc ]) # doc/Makefile # doc/reference/Makefile echo "" echo " cluttergesture $VERSION" echo " =====================" #echo " Documentation: ${enable_gtk_doc}" echo "" echo " To build the project, run \"make\"" echo "" clutter-gesture-0.0.2.1/engine/000077500000000000000000000000001132100660000162405ustar00rootroot00000000000000clutter-gesture-0.0.2.1/engine/Makefile.am000066400000000000000000000023451132100660000203000ustar00rootroot00000000000000# # This file is part of Clutter-Gesture. # # Copyright 2009 (c) Intel Corp. # Author: Forrest Zhao # # Clutter-Gesture 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.1 of the License, # or (at your option) any later version. # # Clutter-Gesture 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 ClutterGesture; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 # USA. lib_LTLIBRARIES= libengine.la libengine_la_SOURCES = engine.c engine.h plugin.h stroke.c stroke.h gesture_recog.c gesture_recog.h AM_CFLAGS = @CLUTTERGESTURE_CFLAGS@ -DPKGDATADIR="\"$(pkgdatadir)\"" INCLUDES = @CLUTTERGESTURE_CFLAGS@ DISTCLEANFILES = $(MARSHALFILES) CLEANFILES = *~ engine *~stroke clutter-gesture-0.0.2.1/engine/engine.c000066400000000000000000001316011132100660000176530ustar00rootroot00000000000000/* * Copyright (C) 2009 Intel Corp. * Author: Forrest Zhao * * 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, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #include #include "math.h" #include #include #include #include #include #include #include #include "engine.h" #include "../clutter-gesture/clutter-gesture.h" #include "stroke.h" #include "plugin.h" #include "gesture_recog.h" typedef enum { ACTOR_STATE_IDLE = 0, ACTOR_STATE_SINGLE_GESTURE, ACTOR_STATE_SINGLE_TOUCH, ACTOR_STATE_2_FINGER_GESTURE, ACTOR_STATE_IN_EMIT_EVENT } actor_state_t; struct engine_core { GSList *actors; recognize_callback_t cb; void *data; void *s_plugin_handle; }; struct actor_core { ClutterActor *handle; guint mask; actor_state_t state; GSList *events; guint num_events; guint timer; guint touch_hold_interval; guint touch_hold_radius; guint touch_hold_timer; gfloat touch_hold_start_x; gfloat touch_hold_start_y; gfloat touch_hold_end_x; gfloat touch_hold_end_y; guint32 touch_hold_time; gint dev_id; gint dev_id_second; struct coord position; struct coord position_second; struct coord position_last; struct coord position_second_last; gboolean cached; GSList *cached_events; recognize_callback_t cb; void *data; }; static gfloat pixels; static void set_actor_state(struct actor_core *actor_core, actor_state_t state) { actor_core->state = state; } static void release_cached_events(struct actor_core *actor_core) { GSList *l; for (l = actor_core->events; l; l = l->next) { ClutterEvent *event; event = l->data; clutter_event_free(event); } g_slist_free(actor_core->events); actor_core->num_events = 0; actor_core->events = NULL; } static void release_point_list(GSList *list) { GSList *l; for (l = list; l; l = l->next) { g_free(l->data); } g_slist_free(list); } static gboolean gesture_recognize_timeout(struct actor_core *actor_core) { GSList *l; /* release cached_events */ if (actor_core->cached_events) { for (l = actor_core->cached_events; l; l = l->next) { ClutterEvent *event; event = l->data; clutter_event_free(event); } g_slist_free(actor_core->cached_events); } /* point cached_events to events */ actor_core->cached_events = actor_core->events; if (actor_core->events) { engine_debug("<333\n"); clutter_event_get_coords(g_slist_nth_data(actor_core->events, actor_core->num_events - 1), &(actor_core->position.x), &(actor_core->position.y)); engine_debug("333>\n"); } actor_core->num_events = 0; actor_core->events = NULL; return TRUE; } static ClutterGestureHoldEvent *new_gesture_touch_hold_event(struct actor_core *actor_core, ClutterGestureTouchholdAction action) { ClutterGestureHoldEvent *hold_event; hold_event = g_new0(ClutterGestureHoldEvent, 1); hold_event->type = GESTURE_HOLD; hold_event->time = actor_core->touch_hold_time; hold_event->source = actor_core->handle; hold_event->stage = CLUTTER_STAGE(clutter_actor_get_stage(hold_event->source)); hold_event->action = action; if (!actor_core->touch_hold_end_x && !actor_core->touch_hold_end_y) { hold_event->x = actor_core->touch_hold_start_x; hold_event->y = actor_core->touch_hold_start_y; } else { hold_event->x = actor_core->touch_hold_end_x; hold_event->x = actor_core->touch_hold_end_y; } return hold_event; } static gboolean emit_touch_hold_event(struct actor_core *actor_core, ClutterGestureTouchholdAction action) { void *gesture_event = NULL; GSList *gesture_event_list = NULL; gboolean result; gesture_event = new_gesture_touch_hold_event(actor_core, action); gesture_event_list = g_slist_append(gesture_event_list, gesture_event); result = actor_core->cb(actor_core->handle, GESTURE_EVENT, 0, gesture_event_list, 1, actor_core->data); g_free(gesture_event); g_slist_free(gesture_event_list); return result; } static gboolean touch_hold_timeout(struct actor_core *actor_core) { GSList *l; /* TODO: emit touch_hold event */ emit_touch_hold_event(actor_core, LONG_PRESS_ACTION); /* stop ST gesture recognition */ if (actor_core->timer) { g_source_remove(actor_core->timer); actor_core->timer = 0; } if (actor_core->cached_events) { for (l = actor_core->cached_events; l ; l = l->next) { ClutterEvent *event; event = l->data; clutter_event_free(event); } g_slist_free(actor_core->cached_events); actor_core->cached_events = NULL; } if (actor_core->events) { clutter_event_get_coords(g_slist_nth_data(actor_core->events, actor_core->num_events - 1), &(actor_core->position.x), &(actor_core->position.y)); release_cached_events(actor_core); } actor_core->touch_hold_start_x = 0; actor_core->touch_hold_start_y = 0; actor_core->touch_hold_end_x = 0; actor_core->touch_hold_end_y = 0; actor_core->touch_hold_timer = 0; actor_core->touch_hold_time = 0; actor_core->state = ACTOR_STATE_SINGLE_TOUCH; return FALSE; } ClutterGestureSlideEvent *new_gesture_slide_event(struct actor_core *actor_core, ClutterGestureSlideDirection direction, guint speed) { ClutterGestureSlideEvent *slide_event; ClutterEvent *event; slide_event = g_new0(ClutterGestureSlideEvent, 1); slide_event->type = GESTURE_SLIDE; event = g_slist_nth_data(actor_core->events, actor_core->num_events - 1); clutter_event_get_coords(event, &slide_event->x_end, &slide_event->y_end); slide_event->time = clutter_event_get_time(event); slide_event->stage = clutter_event_get_stage(event); /* TODO: ref/unref actor_core->handle */ slide_event->source = actor_core->handle; event = g_slist_nth_data(actor_core->events, 0); clutter_event_get_coords(event, &slide_event->x_start, &slide_event->y_start); slide_event->start_time = clutter_event_get_time(event); slide_event->device_id = clutter_event_get_device_id(event); slide_event->direction = direction; return slide_event; } /*static guint calculate_speed(struct actor_core *actor_core, ClutterGestureSlideDirection direction) { gfloat x, y, x1, y1; guint32 time, time1; ClutterEvent *event; double distance, speed; event = g_slist_nth_data(actor_core->events, 0); clutter_event_get_coords(event, &x, &y); time = clutter_event_get_time(event); event = g_slist_nth_data(actor_core->events, actor_core->num_events - 1); clutter_event_get_coords(event, &x1, &y1); time1 = clutter_event_get_time(event); if (direction == SLIDE_UP || direction == SLIDE_DOWN) { distance = y1 - y; } else { distance = x1 - x; } speed = distance/(time1 - time); speed = speed * 10; if (speed < 0) { speed = -speed; } if (speed > 10) speed = 10; engine_debug("y1 is %f, y is %f, time1 is %u, time is %u, the speed is %f\n", y1, y, time1, time, speed); return speed; } */ /* send out GESTURE_EVENT if a gesture is recognize, * otherwise replay the cached CLUTTER_EVENT */ static void gesture_recognize(struct engine_core *engine, struct actor_core *actor_core) { GSList *l, *point_list = NULL; ClutterGestureSlideDirection direction; void *gesture_event = NULL; GSList *gesture_event_list = NULL; /* TODO: recognize the gesture */ for (l = actor_core->events; l; l = l->next) { ClutterEvent *event; struct coord *point; point = g_new0(struct coord, 1); event = l->data; clutter_event_get_coords(event, &(point->x), &(point->y)); point_list = g_slist_append(point_list, point); engine_debug("x/y is %f, %f, event type is %d\n", point->x, point->y, clutter_event_type(event)); } direction = st_gesture_process(engine->s_plugin_handle, point_list, actor_core->num_events); if (direction) { guint speed = 0; engine_debug("this is a match!!, direction is %d\n", direction); /* calculate speed */ // speed = calculate_speed(actor_core, direction); engine_debug("the speed is %d\n", speed); /* send out GESTURE_EVENT */ gesture_event = new_gesture_slide_event(actor_core, direction, speed); gesture_event_list = g_slist_append(gesture_event_list, gesture_event); actor_core->cb(actor_core->handle, GESTURE_EVENT, 0, gesture_event_list, 1, actor_core->data); goto done; } done: if (gesture_event_list) { g_free(gesture_event); g_slist_free(gesture_event_list); } release_cached_events(actor_core); release_point_list(point_list); set_actor_state(actor_core, ACTOR_STATE_IDLE); } static struct actor_core *find_actor(GSList *list, ClutterActor *actor) { GSList *l; for (l = list; l; l = l->next) { struct actor_core *actor_core = l->data; if (actor_core->handle == actor) return actor_core; } return NULL; } handle_t engine_init(recognize_callback_t cb, void *data) { struct engine_core *engine; ClutterUnits units; engine_debug("engine init is called\n"); engine = g_new0(struct engine_core, 1); engine->cb = cb; engine->data = data; engine->s_plugin_handle = s_plugin_init(); clutter_units_from_mm(&units, 5); pixels = clutter_units_to_pixels(&units); return engine; } void engine_shutdown(handle_t handle) { struct engine_core *engine = handle; GSList *l; s_plugin_shutdown(engine->s_plugin_handle); /* release actor_cores */ for (l = engine->actors; l; l = l->next) { struct actor_core *actor_core; actor_core = l->data; g_object_unref(actor_core->handle); release_cached_events(actor_core); if (actor_core->timer) { g_source_remove(actor_core->timer); actor_core->timer = 0; } } g_slist_free(engine->actors); g_free(engine); return; } /* Return value: %0 if success, less than 0 if fail */ gint set_gesture_mask(handle_t handle, ClutterActor *target_actor, guint mask) { struct engine_core *engine = handle; struct actor_core *actor_core; if (!mask) { actor_core = find_actor(engine->actors, target_actor); if (!actor_core) return -ENOENT; engine->actors = g_slist_remove(engine->actors, actor_core); g_object_unref(target_actor); /* release events, stop timer */ release_cached_events(actor_core); if (actor_core->timer) { g_source_remove(actor_core->timer); actor_core->timer = 0; } g_free(actor_core); return 0; } actor_core = find_actor(engine->actors, target_actor); if (!actor_core) { g_object_ref(target_actor); actor_core = g_new0(struct actor_core, 1); actor_core->handle = target_actor; actor_core->mask = mask; set_actor_state(actor_core, ACTOR_STATE_IDLE); actor_core->num_events = 0; actor_core->timer = 0; actor_core->touch_hold_start_x = 0; actor_core->touch_hold_start_y = 0; actor_core->touch_hold_end_x = 0; actor_core->touch_hold_end_y = 0; actor_core->touch_hold_timer = 0; actor_core->touch_hold_interval = DEFAULT_TOUCH_HOLD_INTERVAL; actor_core->touch_hold_radius = DEFAULT_TOUCH_HOLD_RADIUS; actor_core->touch_hold_time = 0; actor_core->dev_id = -1; actor_core->cb = engine->cb; actor_core->data = engine->data; engine->actors = g_slist_append(engine->actors, actor_core); } else { actor_core->mask = mask; } return 0; } gint get_gesture_mask(handle_t handle, ClutterActor *target_actor, guint *mask) { struct engine_core *engine = handle; struct actor_core *actor_core; actor_core = find_actor(engine->actors, target_actor); if (!actor_core) { *mask = 0; return -ENOENT; } *mask = actor_core->mask; return 0; } gboolean set_hold_timeout(handle_t handle, ClutterActor *target_actor, guint interval) { struct engine_core *engine = handle; struct actor_core *actor_core; actor_core = find_actor(engine->actors, target_actor); if (!actor_core) return -ENOENT; actor_core->touch_hold_interval = interval; return 0; } gboolean get_hold_timeout(handle_t handle, ClutterActor *target_actor, guint *interval) { struct engine_core *engine = handle; struct actor_core *actor_core; actor_core = find_actor(engine->actors, target_actor); if (!actor_core) { *interval = 0; return -ENOENT; } *interval = actor_core->touch_hold_interval; return 0; } gboolean set_hold_radius(handle_t handle, ClutterActor *target_actor, guint radius) { struct engine_core *engine = handle; struct actor_core *actor_core; actor_core = find_actor(engine->actors, target_actor); if (!actor_core) return -ENOENT; actor_core->touch_hold_radius = radius; return 0; } gboolean get_hold_radius(handle_t handle, ClutterActor *target_actor, guint *radius) { struct engine_core *engine = handle; struct actor_core *actor_core; actor_core = find_actor(engine->actors, target_actor); if (!actor_core) { *radius = 0; return -ENOENT; } *radius = actor_core->touch_hold_radius; return 0; } static gboolean state_idle_handle_event(struct engine_core *engine, struct actor_core *actor_core, ClutterEvent *event) { ClutterButtonEvent *buttev; struct coord point; if (clutter_event_type(event) != CLUTTER_BUTTON_PRESS) return FALSE; buttev = (ClutterButtonEvent *)event; /* "single left click" triggers the gesture recognition */ if (clutter_event_get_button(event) != 1 || buttev->click_count != 1) return FALSE; // struct coord point; clutter_event_get_coords(event, &(point.x), &(point.y)); /* start the timer */ actor_core->timer = g_timeout_add(100, (GSourceFunc) gesture_recognize_timeout, actor_core); actor_core->events = g_slist_append(actor_core->events, clutter_event_copy(event)); actor_core->num_events++; actor_core->dev_id = clutter_event_get_device_id(event); actor_core->cached_events = NULL; /* start touch_hold timer */ if ((actor_core->mask & GESTURE_MASK_HOLD) && (actor_core->handle == event->any.source)) { gboolean result; clutter_event_get_coords(event, &actor_core->touch_hold_start_x, &actor_core->touch_hold_start_y); actor_core->touch_hold_time = clutter_event_get_time(event); result = emit_touch_hold_event(actor_core, LONG_PRESS_QUERY); if (result == TRUE) { actor_core->touch_hold_timer = g_timeout_add(actor_core->touch_hold_interval, (GSourceFunc) touch_hold_timeout, actor_core); } else { actor_core->touch_hold_start_x = 0; actor_core->touch_hold_start_y = 0; actor_core->touch_hold_time = 0; } } set_actor_state(actor_core, ACTOR_STATE_SINGLE_GESTURE); return FALSE; } ClutterGesturePinchEvent *new_gesture_pinch_event(struct actor_core *actor_core, gint x_start_1, gint y_start_1, gint x_end_1, gint y_end_1, gint x_start_2, gint y_start_2, gint x_end_2, gint y_end_2) { ClutterGesturePinchEvent *pinch_event; // ClutterEvent *event; pinch_event = g_new0(ClutterGesturePinchEvent, 1); pinch_event->type = GESTURE_PINCH; // event = g_slist_nth_data(actor_core->events, 0); // pinch_event->start_time = clutter_event_get_time(event); // event = g_slist_nth_data(actor_core->events, actor_core->num_events - 1); // pinch_event->time = clutter_event_get_time(event); // pinch_event->stage = clutter_event_get_stage(event); pinch_event->source = actor_core->handle; pinch_event->x_start_1 = x_start_1; pinch_event->y_start_1 = y_start_1; pinch_event->x_end_1 = x_end_1; pinch_event->y_end_1 = y_end_1; pinch_event->x_start_2 = x_start_2; pinch_event->y_start_2 = y_start_2; pinch_event->x_end_2 = x_end_2; pinch_event->y_end_2 = y_end_2; return pinch_event; } ClutterGestureRotateEvent *new_gesture_rotate_event(struct actor_core *actor_core, gint x_start_1, gint y_start_1, gint x_end_1, gint y_end_1, gint x_start_2, gint y_start_2, gint x_end_2, gint y_end_2) { ClutterGestureRotateEvent *rotate_event; // ClutterEvent *event; rotate_event = g_new0(ClutterGestureRotateEvent, 1); rotate_event->type = GESTURE_ROTATE; // event = g_slist_nth_data(actor_core->events, 0); // rotate_event->start_time = clutter_event_get_time(event); // event = g_slist_nth_data(actor_core->events, actor_core->num_events - 1); // rotate_event->time = clutter_event_get_time(event); // rotate_event->stage = clutter_event_get_stage(event); rotate_event->source = actor_core->handle; rotate_event->x_start_1 = x_start_1; rotate_event->y_start_1 = y_start_1; rotate_event->x_end_1 = x_end_1; rotate_event->y_end_1 = y_end_1; rotate_event->x_start_2 = x_start_2; rotate_event->y_start_2 = y_start_2; rotate_event->x_end_2 = x_end_2; rotate_event->y_end_2 = y_end_2; return rotate_event; } ClutterGestureNavigateEvent *new_gesture_navigate_event(struct actor_core *actor_core, gint x_start_1, gint y_start_1, gint x_end_1, gint y_end_1, gint x_start_2, gint y_start_2, gint x_end_2, gint y_end_2) { ClutterGestureNavigateEvent *navigate_event; // ClutterEvent *event; navigate_event = g_new0(ClutterGestureNavigateEvent, 1); navigate_event->type = GESTURE_NAVIGATE; // event = g_slist_nth_data(actor_core->events, 0); // navigate_event->start_time = clutter_event_get_time(event); // event = g_slist_nth_data(actor_core->events, actor_core->num_events - 1); // navigate_event->time = clutter_event_get_time(event); // navigate_event->stage = clutter_event_get_stage(event); navigate_event->source = actor_core->handle; navigate_event->x_start_1 = x_start_1; navigate_event->y_start_1 = y_start_1; navigate_event->x_end_1 = x_end_1; navigate_event->y_end_1 = y_end_1; navigate_event->x_start_2 = x_start_2; navigate_event->y_start_2 = y_start_2; navigate_event->x_end_2 = x_end_2; navigate_event->y_end_2 = y_end_2; return navigate_event; } #if 0 static void mt_gesture_recognize(struct engine_core *engine, struct actor_core *actor_core) { GSList *l, *list1 = NULL, *list2 = NULL; struct coord *point; gesture_type g_type; void *gesture_event = NULL; GSList *gesture_event_list = NULL; gesture_info *gesture_info_p = NULL; actor_core->cached = FALSE; if (!actor_core->events) return; if ((actor_core->position.x != 0) || (actor_core->position.y != 0)) { point = g_new0(struct coord, 1); point->x = actor_core->position.x; point->y = actor_core->position.y; list1 = g_slist_append(list1, point); } if ((actor_core->position_second.x != 0) || (actor_core->position_second.y != 0)) { point = g_new0(struct coord, 1); point->x = actor_core->position_second.x; point->y = actor_core->position_second.y; list2 = g_slist_append(list2, point); } for (l = actor_core->events; l; l = l->next) { ClutterEvent *event; point = g_new0(struct coord, 1); event = l->data; clutter_event_get_coords(event, &(point->x), &(point->y)); if (clutter_event_get_device_id(event) == actor_core->dev_id) { list1 = g_slist_append(list1, point); actor_core->position.x = point->x; actor_core->position.y = point->y; } else { list2 = g_slist_append(list2, point); actor_core->position_second.x = point->x; actor_core->position_second.y = point->y; } } /* release events if MT MASK is not set */ if (!((actor_core->mask) & (GESTURE_MASK_ROTATE | GESTURE_MASK_PINCH))) { actor_core->cb(actor_core->handle, CLUTTER_EVENT, 0, actor_core->events, actor_core->num_events, actor_core->data); goto done; } /* TODO: call weian's algorithm */ gesture_info_p = g_new0(gesture_info, 1); gesture_process (list1, list2, gesture_info_p); g_type = gesture_info_p->g_type; engine_debug ("\ngesture type is:"); if (g_type == GESTURE_PINCH_IN) engine_debug ("--------Pitch Zoom In--------\n"); else if (g_type == GESTURE_PINCH_OUT) engine_debug ("--------Pitch Zoom Out--------\n"); else if (g_type == GESTURE_ROTATE_CKW) engine_debug ("--------Rotate Clockwise--------\n"); else if (g_type == GESTURE_ROTATE_ACKW) engine_debug ("--------Rotate Anti - Clockwise--------\n"); else if (g_type == GESTURE_MT_NAVIGATE) engine_debug ("--------Slide--------\n"); else engine_debug ("--------Unknow--------\n"); engine_debug ("\n"); if ((g_type == GESTURE_PINCH_IN) || (g_type == GESTURE_PINCH_OUT)) { struct coord *point1, *point2; // void *gesture_event = NULL; // GSList *gesture_event_list = NULL; point1 = g_slist_nth_data(list1, 0); point2 = g_slist_nth_data(list2, 0); gesture_event = new_gesture_pinch_event(actor_core, point1->x, point1->y, actor_core->position.x, actor_core->position.y, point2->x, point2->y, actor_core->position_second.x, actor_core->position_second.y); gesture_event_list = g_slist_append(gesture_event_list, gesture_event); actor_core->cb(actor_core->handle, GESTURE_EVENT, 0, gesture_event_list, 1, actor_core->data); } else if ((g_type == GESTURE_ROTATE_CKW) || (g_type == GESTURE_ROTATE_ACKW)) { struct coord *point1, *point2; point1 = g_slist_nth_data(list1, 0); point2 = g_slist_nth_data(list2, 0); gesture_event = new_gesture_rotate_event(actor_core, point1->x, point1->y, actor_core->position.x, actor_core->position.y, point2->x, point2->y, actor_core->position_second.x, actor_core->position_second.y); gesture_event_list = g_slist_append(gesture_event_list, gesture_event); actor_core->cb(actor_core->handle, GESTURE_EVENT, 0, gesture_event_list, 1, actor_core->data); } else if (g_type == GESTURE_MT_NAVIGATE) { struct coord *point1, *point2; point1 = g_slist_nth_data(list1, 0); point2 = g_slist_nth_data(list2, 0); gesture_event = new_gesture_navigate_event(actor_core, point1->x, point1->y, actor_core->position.x, actor_core->position.y, point2->x, point2->y, actor_core->position_second.x, actor_core->position_second.y); gesture_event_list = g_slist_append(gesture_event_list, gesture_event); actor_core->cb(actor_core->handle, GESTURE_EVENT, 0, gesture_event_list, 1, actor_core->data); } else { actor_core->cb(actor_core->handle, CLUTTER_EVENT, IN_MT_GESTURE, actor_core->events, actor_core->num_events, actor_core->data); } g_free(gesture_info_p); done: if (gesture_event_list) { g_free(gesture_event); g_slist_free(gesture_event_list); } release_cached_events(actor_core); release_point_list(list1); release_point_list(list2); } #endif /* static gboolean mt_gesture_recognize_timeout(struct actor_core *actor_core) { guint32 list1 = 0, list2 = 0; GSList *l; if (actor_core->cached) { mt_gesture_recognize(NULL, actor_core); return TRUE; } if ((actor_core->position.x != 0) || (actor_core->position.y != 0)) { list1++; } if ((actor_core->position_second.x != 0) || (actor_core->position_second.y != 0)) { list2++; } for (l = actor_core->events; l; l = l->next) { ClutterEvent *event; event = l->data; if (clutter_event_get_device_id(event) == actor_core->dev_id) { list1++; } else { list2++; } } if ((list1 <= 2) && (list2 <= 2)) { actor_core->cached = TRUE; return TRUE; } mt_gesture_recognize(NULL, actor_core); return TRUE; } */ static void state_single_gesture_reset_state(struct actor_core *actor_core, ClutterEvent *event) { GSList *l; /* stop the timer */ if (actor_core->timer) { g_source_remove(actor_core->timer); actor_core->timer = 0; } /* reset the dev_id */ actor_core->dev_id = -1; if (actor_core->touch_hold_timer) { clutter_event_get_coords(event, &actor_core->touch_hold_end_x, &actor_core->touch_hold_end_y); actor_core->touch_hold_time = clutter_event_get_time(event); emit_touch_hold_event(actor_core, LONG_PRESS_CANCEL); actor_core->touch_hold_start_x = 0; actor_core->touch_hold_start_y = 0; actor_core->touch_hold_end_x = 0; actor_core->touch_hold_end_y = 0; g_source_remove(actor_core->touch_hold_timer); actor_core->touch_hold_timer = 0; actor_core->touch_hold_time = 0; } if (actor_core->cached_events) { for (l = actor_core->cached_events; l; l = l->next) { ClutterEvent *current_event = l->data; clutter_event_free(current_event); } g_slist_free(actor_core->cached_events); actor_core->cached_events = NULL; } release_cached_events(actor_core); set_actor_state(actor_core, ACTOR_STATE_IDLE); } static gboolean state_single_gesture_handle_event(struct engine_core *engine, struct actor_core *actor_core, ClutterEvent *event) { ClutterEvent *start_event; gfloat start_x, start_y, end_x, end_y; GSList *l; if (clutter_event_type(event) == CLUTTER_MOTION && actor_core->dev_id == clutter_event_get_device_id(event)) { gfloat x, y; actor_core->events = g_slist_append(actor_core->events, clutter_event_copy(event)); actor_core->num_events++; if (actor_core->touch_hold_timer == 0) return FALSE; /* if motion event out of circle, stop timer */ clutter_event_get_coords(event, &x, &y); if ((abs(actor_core->touch_hold_start_x - x) < actor_core->touch_hold_radius) && (abs(actor_core->touch_hold_start_y - y) < actor_core->touch_hold_radius)) { actor_core->touch_hold_end_x = x; actor_core->touch_hold_end_y = y; actor_core->touch_hold_time = clutter_event_get_time(event); } else { actor_core->touch_hold_end_x = x; actor_core->touch_hold_end_y = y; actor_core->touch_hold_time = clutter_event_get_time(event); emit_touch_hold_event(actor_core, LONG_PRESS_CANCEL); actor_core->touch_hold_start_x = 0; actor_core->touch_hold_start_y = 0; actor_core->touch_hold_end_x = 0; actor_core->touch_hold_end_y = 0; if (actor_core->touch_hold_timer) g_source_remove(actor_core->touch_hold_timer); actor_core->touch_hold_timer = 0; actor_core->touch_hold_time = 0; } return FALSE; } if (clutter_event_type(event) == CLUTTER_BUTTON_PRESS && actor_core->dev_id != clutter_event_get_device_id(event)) { ClutterButtonEvent *buttev = (ClutterButtonEvent *)event; if (clutter_event_get_button(event) != 1 || buttev->click_count != 1) return FALSE; actor_core->dev_id_second = clutter_event_get_device_id(event); if (actor_core->timer) { g_source_remove(actor_core->timer); actor_core->timer = 0; } if (actor_core->touch_hold_timer) { actor_core->touch_hold_start_x = 0; actor_core->touch_hold_start_y = 0; actor_core->touch_hold_end_x = 0; actor_core->touch_hold_end_y = 0; g_source_remove(actor_core->touch_hold_timer); actor_core->touch_hold_timer = 0; actor_core->touch_hold_time = 0; } if (actor_core->cached_events) { for (l = actor_core->cached_events; l; l = l->next) { ClutterEvent *event; event = l->data; clutter_event_free(event); } g_slist_free(actor_core->cached_events); actor_core->cached_events = NULL; } // actor_core->cb(actor_core->handle, CLUTTER_EVENT, actor_core->events, // actor_core->num_events, actor_core->data); if (actor_core->events) { engine_debug("<111\n"); clutter_event_get_coords(g_slist_nth_data(actor_core->events, actor_core->num_events - 1), &(actor_core->position.x), &(actor_core->position.y)); engine_debug("111>\n"); release_cached_events(actor_core); } // actor_core->events = g_slist_append(actor_core->events, // clutter_event_copy(event)); // actor_core->num_events++; clutter_event_get_coords(event, &(actor_core->position_second.x), &(actor_core->position_second.y)); // actor_core->timer = g_timeout_add(100, // (GSourceFunc) mt_gesture_recognize_timeout, actor_core); set_actor_state(actor_core, ACTOR_STATE_2_FINGER_GESTURE); return TRUE; } /* Reset the state to idle */ if (clutter_event_type(event) == CLUTTER_LEAVE && actor_core->dev_id == clutter_event_get_device_id(event) && actor_core->handle == event->any.source) { state_single_gesture_reset_state(actor_core, event); return FALSE; } if (clutter_event_type(event) != CLUTTER_BUTTON_RELEASE || actor_core->dev_id != clutter_event_get_device_id(event)) { return FALSE; } /* release events if GESTURE_MASK_SLIDE is not set */ if (!((actor_core->mask) & GESTURE_MASK_SLIDE)) { state_single_gesture_reset_state(actor_core, event); return FALSE; } /* add the last CLUTTER_BUTTON_RELEASE event to event queue */ actor_core->events = g_slist_append(actor_core->events, clutter_event_copy(event)); actor_core->num_events++; /* stop the timer and start recognition */ if (actor_core->timer) { g_source_remove(actor_core->timer); actor_core->timer = 0; } /* reset the dev_id */ actor_core->dev_id = -1; if (actor_core->touch_hold_timer) { clutter_event_get_coords(event, &actor_core->touch_hold_end_x, &actor_core->touch_hold_end_y); actor_core->touch_hold_time = clutter_event_get_time(event); emit_touch_hold_event(actor_core, LONG_PRESS_CANCEL); actor_core->touch_hold_start_x = 0; actor_core->touch_hold_start_y = 0; actor_core->touch_hold_end_x = 0; actor_core->touch_hold_end_y = 0; g_source_remove(actor_core->touch_hold_timer); actor_core->touch_hold_timer = 0; actor_core->touch_hold_time = 0; } if (actor_core->cached_events) { actor_core->cached_events = g_slist_reverse(actor_core->cached_events); for (l = actor_core->cached_events; l; l = l->next) { ClutterEvent *current_event = l->data; if ((clutter_event_get_time(event) - clutter_event_get_time(current_event)) <= 100) { actor_core->events = g_slist_prepend(actor_core->events, current_event); actor_core->num_events++; } else { clutter_event_free(current_event); } } g_slist_free(actor_core->cached_events); actor_core->cached_events = NULL; } start_event = g_slist_nth_data(actor_core->events, 0); engine_debug("<222\n"); clutter_event_get_coords(start_event, &start_x, &start_y); clutter_event_get_coords(event, &end_x, &end_y); engine_debug("222>\n"); if (((start_x == end_x) && (start_y == end_y)) || (actor_core->num_events <= 3) || ((abs(start_x - end_x) < pixels) && abs(start_y - end_y) < pixels)) { release_cached_events(actor_core); set_actor_state(actor_core, ACTOR_STATE_IDLE); } else { gesture_recognize(engine, actor_core); } return FALSE; } static gboolean state_single_touch_handle_event(struct engine_core *engine, struct actor_core *actor_core, ClutterEvent *event) { if (clutter_event_type(event) == CLUTTER_BUTTON_RELEASE && actor_core->dev_id == clutter_event_get_device_id(event)) { actor_core->dev_id = -1; actor_core->position.x = 0; actor_core->position.y = 0; set_actor_state(actor_core, ACTOR_STATE_IDLE); } if (clutter_event_type(event) == CLUTTER_MOTION && actor_core->dev_id == clutter_event_get_device_id(event)) { clutter_event_get_coords(event, &(actor_core->position.x), &(actor_core->position.y)); } if (clutter_event_type(event) == CLUTTER_BUTTON_PRESS && actor_core->dev_id != clutter_event_get_device_id(event)) { ClutterButtonEvent *buttev = (ClutterButtonEvent *)event; if (clutter_event_get_button(event) != 1 || buttev->click_count != 1) return FALSE; actor_core->dev_id_second = clutter_event_get_device_id(event); clutter_event_get_coords(event, &(actor_core->position_second.x), &(actor_core->position_second.y)); // actor_core->events = g_slist_append(actor_core->events, // clutter_event_copy(event)); // actor_core->num_events++; // actor_core->timer = g_timeout_add(100, // (GSourceFunc) mt_gesture_recognize_timeout, actor_core); set_actor_state(actor_core, ACTOR_STATE_2_FINGER_GESTURE); return TRUE; } return FALSE; } #define PI 3.1415926535 #define angle(r) (180.0*r/PI) static gboolean is_pinch(struct coord center, struct coord point1, struct coord point2) { float frt; float dx, dy; float fago, fagn; dx = point1.x - center.x; dy = point1.y - center.y; if (dx ==0) { if (dy > 0) fago = 90; else fago = -90; } else { fago = angle(atan(dy/dx)); } dx = point2.x - center.x; dy = point2.y - center.y; if (dx ==0) { if (dy > 0) fagn = 90; else fagn = -90; } else { fagn = angle(atan(dy/dx)); } frt = fago - fagn; if (frt < 0.3 && frt > -0.3) return TRUE; else return FALSE; } static gboolean state_2_finger_gesture_handle_event(struct engine_core *engine, struct actor_core *actor_core, ClutterEvent *event) { if (clutter_event_type(event) == CLUTTER_BUTTON_RELEASE) { if (actor_core->timer) { g_source_remove(actor_core->timer); actor_core->timer = 0; } actor_core->events = g_slist_append(actor_core->events, clutter_event_copy(event)); actor_core->num_events++; /*TODO: recognize MT gestures */ // mt_gesture_recognize(engine, actor_core); if (clutter_event_get_device_id(event) == actor_core->dev_id) { actor_core->dev_id = actor_core->dev_id_second; actor_core->position.x = actor_core->position_second.x; actor_core->position.y = actor_core->position_second.y; } actor_core->position_second.x = 0; actor_core->position_second.y = 0; actor_core->position_last.x = 0; actor_core->position_last.y = 0; actor_core->position_second_last.x = 0; actor_core->position_second_last.y = 0; actor_core->cached_events = NULL; actor_core->timer = g_timeout_add(100, (GSourceFunc) gesture_recognize_timeout, actor_core); set_actor_state(actor_core, ACTOR_STATE_SINGLE_GESTURE); return TRUE; } if (clutter_event_type(event) == CLUTTER_MOTION) { struct coord point; gboolean pinch; void *gesture_event = NULL; GSList *gesture_event_list = NULL; // actor_core->events = g_slist_append(actor_core->events, // clutter_event_copy(event)); // actor_core->num_events++; clutter_event_get_coords(event, &(point.x), &(point.y)); if (clutter_event_get_device_id(event) == actor_core->dev_id) { pinch = is_pinch(actor_core->position_second, actor_core->position, point); } else { pinch = is_pinch(actor_core->position, actor_core->position_second, point); } if (pinch) { if (clutter_event_get_device_id(event) == actor_core->dev_id) { gesture_event = new_gesture_pinch_event(actor_core, actor_core->position.x, actor_core->position.y, point.x, point.y, actor_core->position_second.x, actor_core->position_second.y, actor_core->position_second.x, actor_core->position_second.y); // printf ("actor_core->position_last.x, actor_core->position_last.y, actor_core->position.x, actor_core->position.y, %f, %f, %f, %f", ); actor_core->position_last.x = actor_core->position.x; actor_core->position_last.y = actor_core->position.y; actor_core->position.x = point.x; actor_core->position.y = point.y; engine_debug ("actor_core->position_last.x, actor_core->position_last.y, actor_core->position.x, actor_core->position.y, %f, %f, %f, %f\n", actor_core->position_last.x, actor_core->position_last.y, actor_core->position.x, actor_core->position.y); } else { gesture_event = new_gesture_pinch_event(actor_core, actor_core->position.x, actor_core->position.y, actor_core->position.x, actor_core->position.y, actor_core->position_second.x, actor_core->position_second.y, point.x, point.y); actor_core->position_second_last.x = actor_core->position_second.x; actor_core->position_second_last.y = actor_core->position_second.y; actor_core->position_second.x = point.x; actor_core->position_second.y = point.y; } // gesture_event_list = g_slist_append(gesture_event_list, gesture_event); // actor_core->cb(actor_core->handle, GESTURE_EVENT, 0, gesture_event_list, // 1, actor_core->data); } else { if (clutter_event_get_device_id(event) == actor_core->dev_id) { gesture_event = new_gesture_rotate_event(actor_core, actor_core->position.x, actor_core->position.y, point.x, point.y, actor_core->position_second.x, actor_core->position_second.y, actor_core->position_second.x, actor_core->position_second.y); actor_core->position_last.x = actor_core->position.x; actor_core->position_last.y = actor_core->position.y; actor_core->position.x = point.x; actor_core->position.y = point.y; } else { gesture_event = new_gesture_rotate_event(actor_core, actor_core->position.x, actor_core->position.y, actor_core->position.x, actor_core->position.y, actor_core->position_second.x, actor_core->position_second.y, point.x, point.y); actor_core->position_second_last.x = actor_core->position_second.x; actor_core->position_second_last.y = actor_core->position_second.y; actor_core->position_second.x = point.x; actor_core->position_second.y = point.y; } } ((ClutterGestureEvent *)gesture_event)->time = clutter_event_get_time(event); ((ClutterGestureEvent *)gesture_event)->stage = clutter_event_get_stage(event); gesture_event_list = g_slist_append(gesture_event_list, gesture_event); actor_core->cb(actor_core->handle, GESTURE_EVENT, 0, gesture_event_list, 1, actor_core->data); if (gesture_event_list) { g_free(gesture_event); g_slist_free(gesture_event_list); gesture_event_list = NULL; } if ((actor_core->position_last.x == 0 && actor_core->position_last.y == 0) || (actor_core->position_second_last.x == 0 && actor_core->position_second_last.y == 0)) return TRUE; if ((actor_core->position_last.x == actor_core->position.x && actor_core->position_last.y == actor_core->position.y) || (actor_core->position_second_last.x == actor_core->position_second.x && actor_core->position_second_last.y == actor_core->position_second.y)) return TRUE; if ((actor_core->position_last.y - actor_core->position.y > 0) && (actor_core->position_second_last.y - actor_core->position_second.y) < 0) return TRUE; if ((actor_core->position_last.y - actor_core->position.y <= 0) && (actor_core->position_second_last.y - actor_core->position_second.y) > 0) return TRUE; if ((actor_core->position_last.x - actor_core->position.x > 0) && (actor_core->position_second_last.x - actor_core->position_second.x) < 0) return TRUE; if ((actor_core->position_last.x - actor_core->position.x < 0) && (actor_core->position_second_last.x - actor_core->position_second.x) > 0) return TRUE; engine_debug("new_gesture_navigate_event is called, x,x`, y,y`, x1,x1`, y1, y1`, %f,%f,%f,%f,%f,%f,%f,%f\n", actor_core->position_last.x, actor_core->position_last.y, actor_core->position.x, actor_core->position.y, actor_core->position_second_last.x, actor_core->position_second_last.y, actor_core->position_second.x, actor_core->position_second.y); gesture_event = new_gesture_navigate_event(actor_core, actor_core->position_last.x, actor_core->position_last.y, actor_core->position.x, actor_core->position.y, actor_core->position_second_last.x, actor_core->position_second_last.y, actor_core->position_second.x, actor_core->position_second.y); ((ClutterGestureEvent *)gesture_event)->time = clutter_event_get_time(event); ((ClutterGestureEvent *)gesture_event)->stage = clutter_event_get_stage(event); gesture_event_list = g_slist_append(gesture_event_list, gesture_event); actor_core->cb(actor_core->handle, GESTURE_EVENT, 0, gesture_event_list, 1, actor_core->data); actor_core->position_last.x = 0; actor_core->position_last.y = 0; actor_core->position_second_last.x = 0; actor_core->position_second_last.y = 0; if (gesture_event_list) { g_free(gesture_event); g_slist_free(gesture_event_list); } return TRUE; } return FALSE; } /* Return value: %TRUE if event is accepted by engine; %FALSE otherwise */ gboolean fill_event(handle_t handle, ClutterActor *target_actor, ClutterEvent *event) { struct engine_core *engine = handle; struct actor_core *actor_core; gboolean ret = FALSE; actor_core = find_actor(engine->actors, target_actor); if (!actor_core) { engine_debug("in fill_event(), actor_core not found\n"); return FALSE; } switch (actor_core->state) { case ACTOR_STATE_IDLE: ret = state_idle_handle_event(engine, actor_core, event); break; case ACTOR_STATE_SINGLE_GESTURE: ret = state_single_gesture_handle_event(engine, actor_core, event); break; case ACTOR_STATE_SINGLE_TOUCH: ret = state_single_touch_handle_event(engine, actor_core, event); break; case ACTOR_STATE_2_FINGER_GESTURE: ret = state_2_finger_gesture_handle_event(engine, actor_core, event); break; default: break; } return ret; } clutter-gesture-0.0.2.1/engine/engine.h000066400000000000000000000037341132100660000176650ustar00rootroot00000000000000/* * Copyright (C) 2009 Intel Corp. * Author: Forrest Zhao * * 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, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #ifndef __engine_h__ #define __engine_h__ #include "config.h" typedef void *handle_t; typedef enum { CLUTTER_EVENT = 0, GESTURE_EVENT } event_type_t; #define IN_MT_GESTURE (1<<0) #define DEFAULT_TOUCH_HOLD_INTERVAL 800 #define DEFAULT_TOUCH_HOLD_RADIUS 20 typedef gboolean (*recognize_callback_t) (ClutterActor *target_actor, event_type_t event_type, guint flag, GSList *event_list, gint number, void *data); handle_t engine_init(recognize_callback_t cb, void *data); void engine_shutdown(handle_t handle); gint set_gesture_mask(handle_t handle, ClutterActor *target_actor, guint mask); gint get_gesture_mask(handle_t handle, ClutterActor *target_actor, guint *mask); gboolean set_hold_timeout(handle_t handle, ClutterActor *target_actor, guint interval); gboolean get_hold_timeout(handle_t handle, ClutterActor *target_actor, guint *interval); gboolean set_hold_radius(handle_t handle, ClutterActor *target_actor, guint radius); gboolean get_hold_radius(handle_t handle, ClutterActor *target_actor, guint *radius); gboolean fill_event(handle_t handle, ClutterActor *target_actor, ClutterEvent *event); #endif clutter-gesture-0.0.2.1/engine/gesture_recog.c000066400000000000000000000564301132100660000212510ustar00rootroot00000000000000 /* * Copyright (C) 2009 Intel Corp. * Author: Weian Chen * * 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, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #include #include #include #include #include "gesture_recog.h" stroke *stroke_new (int n) { if (n < 10) n = 10; stroke *s = malloc (sizeof (stroke)); s->p = malloc (n * sizeof (point)); s->num_p = 0; s->max_num_p = n; s->ex_angle = 0.0; // need reasonable init value s->var_angle = 0.0; // need reasonable init value s->ols_a = s->ols_b = s->ols_r = 0.0; return s; } void stroke_del (stroke *s) { if (s->p) free (s->p); if (s) free (s); } void add_point (stroke *s, point p) { s->p[s->num_p].x = p.x; s->p[s->num_p].y = p.y; s->num_p ++; } void add_point_list (stroke *s, point *p, int n) { int index; for (index = 0; index < n; index ++) { add_point (s, p[index]); } } void add_point_glist (stroke *s, GSList *list) { GSList *it = NULL; coor *c; point p; for (it = list; it; it = it->next) { c = (coor *)it->data; p.x = c->x; p.y = c->y; add_point (s, p); } } void compute_angle (stroke *s) { int n = s->num_p - 1; int index; if (n < P_NUM_THRD) return; for (index = 0; index < n; index ++) { s->p[index].angle = atan2 ((float) (s->p[index + 1].y - s->p[index].y), (float) (s->p[index + 1].x - s->p[index].x)); //engine_debug ("y = %d, x = %d\n", (s->p[index + 1].y - s->p[index].y), (s->p[index + 1].x - s->p[index].x)); //engine_debug ("%f\n", s->p[index].angle); } } void compute_ex_var (stroke *s) { double sum_ex = 0; double sum_var = 0; int i; int n = s->num_p - 1; for (i = 0; i < n; i++) sum_ex += s->p[i].angle; s->ex_angle = sum_ex / n; for (i = 0; i < n; i ++) { sum_var += (s->ex_angle - s->p[i].angle) * (s->ex_angle - s->p[i].angle); //engine_debug ("sum_var = %04f\n", sum_var); } s->var_angle = sum_var / n; } // Ordinary Least Square void compute_ols (stroke *s) { int i; int n = s->num_p; float dx, dy; double sum_x = 0; double sum_y = 0; double sum_xy = 0; double sum_xx = 0; double sum_yy = 0; double ex,ey; double var_x = 0.0; double var_y = 0.0; double d_sum_x = 0; double d_sum_y = 0; double dd_sum_x = 0; double dd_sum_y = 0; double dd_sum_xy = 0; double delta = 0; double delta_sum = 0; double s_s = 0.0; double lxx, lyy, lxy; //double a,b,r; for (i = 0; i < n; i++) { sum_x += s->p[i].x; sum_y += s->p[i].y; sum_xy += (s->p[i].x * s->p[i].y); sum_xx += (s->p[i].x * s->p[i].x); sum_yy += (s->p[i].y * s->p[i].y); } //engine_debug ("sum_x = %ld, sum_y = %ld, sum_xy = %ld, sum_xx = %ld, sum_yy = %ld\n", sum_x, sum_y, sum_xy, sum_xx, sum_yy); ex = sum_x / n; ey = sum_y / n; for (i = 0; i < n; i++) { var_x += (ex - s->p[i].x) * (ex - s->p[i].x); var_y += (ey - s->p[i].y) * (ey - s->p[i].y); } var_x /= n; var_y /= n; engine_debug ("var_x = %f, var_y = %f\n", var_x, var_y); #if 1 // It is possible that both var_x and var_y is small than 4, we need to detect which one smaller if ((var_x < 2) && (var_x < var_y)){ s->ols_r = 0.99; s->ols_a = 999; s->ols_b = ex; s->ols_angle = M_PI / 2; #if 0 if (s->p[s->num_p - 1].y - s->p[0].y > 0){ s->ols_angle = M_PI / 2; s->ols_a = 999; } else { s->ols_angle = - (M_PI / 2); s->ols_a = -999; } #endif engine_debug ("ols para is: a = %f, b = %f, r = %f, angle = %f, sin = %f\n", s->ols_a, s->ols_b, s->ols_r, s->ols_angle, sin (s->ols_angle)); return; } if ((var_y < 2) && (var_y < var_x)){ s->ols_r = 0.99; s->ols_a = 0.001; s->ols_b = ey; s->ols_angle = 0.001; #if 0 if (s->p[s->num_p - 1].x - s->p[0].x > 0){ s->ols_angle = 0.001; s->ols_a = 0.001; } else { s->ols_angle = M_PI - 0.001; s->ols_a = -0.001; } #endif engine_debug ("ols para is: a = %f, b = %f, r = %f, angle = %f, sin = %f\n", s->ols_a, s->ols_b, s->ols_r, s->ols_angle, sin (s->ols_angle)); return; } #endif lxy = (double)sum_xy - (double)(sum_x * sum_y / n); lxx = (double)sum_xx - (double)(sum_x * sum_x / n); lyy = (double)sum_yy - (double)(sum_y * sum_y / n); if (lxx == 0) { s->ols_r = 1; s->ols_a = 9999; s->ols_b = s->p[0].x; s->ols_angle = M_PI / 2; engine_debug ("ols para is: a = %f, b = %f, r = %f, angle = %f, sin = %f\n", s->ols_a, s->ols_b, s->ols_r, s->ols_angle, sin (s->ols_angle)); return; } //engine_debug ("lxx = %f, lyy = %f, lxy = %f\n", lxx, lyy, lxy); //a = (double) (n * sum_xy - sum_x * sum_y) / (double) (n * sum_xx - sum_x * sum_x); //engine_debug ("a = %f\n", a); s->ols_a = lxy / lxx; s->ols_b = (double) (sum_y / n) - s->ols_a * (double)(sum_x / n); //engine_debug ("a = %f, b = %f\n", s->ols_a, s->ols_b); for (i = 0; i < n; i++) { dx = (s->p[i].x - ex); dy = (s->p[i].y - ey); d_sum_x += dx; d_sum_y += dy; delta = s->p[i].y - (double)(s->ols_a * s->p[i].x + s->ols_b); delta_sum = delta * delta; dd_sum_x += dx * dx; dd_sum_y += dy * dy; dd_sum_xy += (dx * dy); } s_s = sqrt (delta_sum / (n - 2)); //s->ols_r = (double) (d_sum_x * d_sum_y) / (double)((double)sqrt(dd_sum_x) * (double)sqrt(dd_sum_y)); s->ols_r = (double) dd_sum_xy / (double) sqrt (dd_sum_x * dd_sum_y); //s->ols_r = (double) (sum_xy / n - sum_x / n * sum_y / n) / (double)sqrt ((sum_xx/n -sum_x/n * sum_x/n) * (sum_yy/n - sum_y/n * sum_y/n)); //s->ols_r = lxy / sqrt(lxx * lyy); s->ols_angle = atan (s->ols_a); engine_debug ("ols para is: a = %f, b = %f, r = %f, s = %f, angle = %f, sin = %f\n\n", s->ols_a, s->ols_b, s->ols_r, s_s, s->ols_angle, sin (s->ols_angle)); } stroke_type compute_stroke_type (stroke *s) { int n = s->num_p - 1; int i; double d = 0.0; if (s->num_p < 2) { s->type_s = STROKE_DOT; return STROKE_DOT; } for (i = 0; i < n; i++) { d += hypot (s->p[i + 1].x - s->p[i].x, s->p[i+1].y - s->p[i].y); if (d >= DIS_THRD) break; } if (d < DIS_THRD) { s->type_s = STROKE_DOT; return STROKE_DOT; } #if 0 // we will not use this algorithm compute_angle (s); // compute the angle between two adjacent points compute_ex_var (s); // compute the variance of the angles if (s->var_angle < LINE_THRD) return STROKE_LINE; else return STROKE_CURVE; #endif compute_ols (s); if (fabs(s->ols_r) > OLS_LINE_THRD) { s->type_s = STROKE_LINE; return STROKE_LINE; } else { s->type_s = STROKE_CURVE; return STROKE_CURVE; } } int is_stroke_para (stroke *s1, stroke *s2) { double abs_ex_diff = fabs (s1->ex_angle - s2->ex_angle); if (abs_ex_diff < PARA_THRD) return 1; else return 0; } int is_angle_mono (stroke *s) { return 1; } gesture *gesture_new (int n) { if (n < 1) return NULL; gesture *g = malloc (sizeof (gesture)); //g->s = (stroke *) malloc (n * sizeof (stroke)); g->num_s = 0; g->max_num_s = n; return g; } void gesture_del (gesture *g) { free (g); } void add_stroke_into_gesture (gesture *g, stroke *s) { g->s[g->num_s] = *s; g->num_s ++; } void handle_dot_line (gesture *g, gesture_info *g_info) { stroke *s1 = &(g->s[0]); stroke *s2 = &(g->s[1]); int n1 = s1->num_p; int n2 = s2->num_p; stroke *s = stroke_new (n1 + n2); int i; gesture_type g_type = GESTURE_UNDEF; double delta_a; double a1; double a2; double d1, d2; double a; if (s1->type_s == STROKE_DOT) { add_point (s, s1->p[0]); for (i = 0; i < n2; i++) { add_point (s, s2->p[i]); } } else { add_point (s, s2->p[0]); for (i = 0; i < n1; i++) { add_point (s, s1->p[i]); } } a1 = atan2 (s->p[0].y - s->p[1].y, s->p[0].x - s->p[1].x); a2 = atan2 (s->p[0].y - s->p[s->num_p - 1].y, s->p[0].x - s->p[s->num_p - 1].x); d1 = hypot (s->p[0].x - s->p[1].x, s->p[0].y - s->p[1].y); d2 = hypot (s->p[0].x - s->p[s->num_p - 1].x, s->p[0].y - s->p[s->num_p - 1].y); if (s1->type_s == STROKE_DOT) a = s2->ols_angle - a1; else a = s1->ols_angle - a1; if ((fabs(tan (a)) < 0.5) && (fabs (a1 - a2) < 0.2)) { if (d1 > d2) g_type = GESTURE_PINCH_IN; else g_type = GESTURE_PINCH_OUT; } engine_debug ("------angle1: %f\n", a1); engine_debug ("------angle2: %f\n", a2); engine_debug ("@@@@ a = %f\n", fabs(tan (a))); #if 0 compute_ols (s); engine_debug ("------delta angle: %f\n", fabs (s1->ols_angle - s2->ols_angle)); engine_debug ("------s1 angle: %f\n", (s1->ols_angle)); engine_debug ("------s2 angle: %f\n", (s2->ols_angle)); if (fabs(s->ols_r) > OLS_LINE_THRD) { if (s1->type_s == STROKE_DOT) { if (fabs (s->ols_angle - s2->ols_angle) < 0.4) { if (hypot (s->p[0].x - s->p[1].x, s->p[0].y - s->p[1].y) > hypot (s->p[0].x - s->p[s->num_p - 1].x, s->p[0].y - s->p[s->num_p - 1].y)) g_type = GESTURE_PINCH_IN; else g_type = GESTURE_PINCH_OUT; } } else { if (fabs (s->ols_angle - s1->ols_angle) < 0.4) { if (hypot (s->p[0].x - s->p[1].x, s->p[0].y - s->p[1].y) > hypot (s->p[0].x - s->p[s->num_p - 1].x, s->p[0].y - s->p[s->num_p - 1].y)) g_type = GESTURE_PINCH_IN; else g_type = GESTURE_PINCH_OUT; } } } #endif if (g_type != GESTURE_UNDEF) { g_info->g_type = g_type; return; } delta_a = a1 -a2; if (delta_a > M_PI) delta_a -= 2 * M_PI; else if (delta_a < - M_PI) delta_a += 2 * M_PI; if (delta_a > 0.0001) g_type = GESTURE_ROTATE_ACKW; else if (delta_a < -0.001) g_type = GESTURE_ROTATE_CKW; g_info->g_type = g_type; g_info->angle = delta_a; g_info->center.x = s->p[0].x; g_info->center.y = s->p[0].y; stroke_del(s); return; } void handle_pinch (gesture *g, gesture_info *g_info) { stroke *s1 = &(g->s[0]); stroke *s2 = &(g->s[1]); int n1 = s1->num_p; int n2 = s2->num_p; int i; int num = 0; float max_x, max_y, min_x, min_y; double dis = 0.0; double angle_dot = 0.0; double angle_line = 0.0; double delta_angle = 0.0; // only compare x, because para to X axis if (fabs(s1->ols_a) < X_AXIS_THRD) { dis = fabs (s1->ols_b - s2->ols_b); if (s1->p[0].x- s1->p[n1-1].x >= 0) { max_x = s1->p[0].x; min_x = s1->p[n1-1].x; } else { max_x = s1->p[n1-1].x; min_x = s1->p[0].x; } for (i = 0; i < n2; i ++) { if ((s2->p[i].x >= max_x) || (s2->p[i].x <= min_x)) num ++; } } // only compare y, because para to Y axis else if (fabs (s1->ols_a) > Y_AXIS_THRD) { dis = fabs (s1->ols_b - s2->ols_b); if (s1->p[0].y - s1->p[n1-1].y >= 0) { max_y = s1->p[0].y; min_y = s1->p[n1-1].y; } else { max_y = s1->p[n1-1].y; min_y = s1->p[0].y; } for (i = 0; i < n2; i ++) { if ((s2->p[i].y >= max_y) || (s2->p[i].y <= min_y)) num ++; } } else { engine_debug ("non x-axis nor y-axis\n"); if (s1->p[0].y - s1->p[n1-1].y >= 0) { max_y = s1->p[0].y; min_y = s1->p[n1-1].y; } else { max_y = s1->p[n1-1].y; min_y = s1->p[0].y; } if (s1->p[0].x - s1->p[n1-1].x >= 0) { max_x = s1->p[0].x; min_x = s1->p[n1-1].x; } else { max_x = s1->p[n1-1].x; min_x = s1->p[0].x; } engine_debug ("min_x = %f, min_y = %f, max_x = %f, max_y = %f\n", min_x, min_y, max_x, max_y); for (i = 0; i < n2; i++) { if (((s2->p[i].x > max_x) || (s2->p[i].x < min_x)) && ((s2->p[i].y > max_y) || (s2->p[i].y < min_y))) num ++; } } engine_debug ("total number = %d, num matched = %d\n", n2, num); angle_dot = atan2 (s1->p[(n1 - 1)/2].y - s2->p[(n2 - 1)/2].y, s1->p[(n1 - 1)/2].x - s2->p[(n2 - 1)/2].x); dis = hypot (s1->p[(n1 - 1)/2].x - s2->p[(n2 - 1)/2].x, s1->p[(n1 - 1)/2].y - s2->p[(n2 - 1)/2].y); if (angle_dot > M_PI/2) angle_dot = M_PI - angle_dot; if (angle_dot < -M_PI/2) angle_dot = M_PI + angle_dot; angle_line = (s1->ols_angle + s2->ols_angle) / 2; if (angle_line > M_PI/2) angle_line = M_PI - angle_line; if (angle_line < -M_PI/2) angle_line = M_PI + angle_line; engine_debug ("p-p distance is %f\n", dis); dis = dis * fabs (sin (angle_line - angle_dot)); engine_debug ("dis = %f, angle_line = %f, angle_dot = %f\n", dis, angle_line, angle_dot); if (dis > 60.0) { delta_angle = atan2 (s1->p[0].y - s1->p[0].y,s1->p[0].x - s2->p[0].x) - atan2 (s1->p[s1->num_p - 1].y - s2->p[s2->num_p - 1].y,s1->p[s1->num_p - 1].x - s2->p[s2->num_p -1].x); //fix me, the direction is not right if (delta_angle > M_PI) delta_angle -= 2 * M_PI; else if (delta_angle < - M_PI) delta_angle += 2 * M_PI; if (delta_angle > 0.0001) g_info->g_type = GESTURE_ROTATE_ACKW; else if (delta_angle < -0.001) g_info->g_type = GESTURE_ROTATE_CKW; return; } if (num >= s2->num_p -1) { if (hypot (s1->p[0].x - s2->p[0].x, s1->p[0].y - s2->p[0].y) < hypot (s1->p[n1-1].x - s2->p[n2-1].x, s1->p[n1-1].y - s2->p[n2-1].y)) { g_info->g_type = GESTURE_PINCH_OUT; } else g_info->g_type = GESTURE_PINCH_IN; } else { g_info->g_type = GESTURE_UNDEF; } return; } void handle_rotate (gesture *g, gesture_info *g_info) { stroke *s1 = &(g->s[0]); stroke *s2 = &(g->s[1]); int n1 = s1->num_p; int n2 = s2->num_p; int angle_ckw1 = 0; int angle_ckw2 = 0; int i; double delta_angle; int trend_up = 0; int trend_down = 0; int trend_zero = 0; //engine_debug ("gesture recog: stroke type = %d, %d\n", s1->type_s, s2->type_s); if (s1->type_s == STROKE_CURVE) { engine_debug ("stroke 1\n"); compute_angle (s1); for (i = 0; i < n1 - 1; i ++) { delta_angle = s1->p[i+1].angle - s1->p[i].angle; if (delta_angle > M_PI) delta_angle -= 2 * M_PI; else if (delta_angle < - M_PI) delta_angle += 2 * M_PI; if (fabs (delta_angle) < 0.1) delta_angle = 0; //if ((delta_angle <= 0.05) || (delta_angle >= -0.05)) // delta_angle = 0.0; //engine_debug ("delta_angle = %f\n", delta_angle); if (delta_angle > 0) trend_up += 1; else if (delta_angle < 0) trend_down += 1; else trend_zero += 1; } engine_debug ("up = %d, down = %d, zero = %d\n", trend_up, trend_down, trend_zero); if ((trend_up + trend_zero) >= trend_down * 2) angle_ckw1 = 1; else if ((trend_down + trend_zero) >= trend_up * 2) angle_ckw1 = 0; else { g_info->g_type = GESTURE_UNDEF; return; } } trend_up = 0; trend_down = 0; trend_zero = 0; if (s2->type_s == STROKE_CURVE) { engine_debug ("stroke 2 \n"); compute_angle (s2); for (i = 0; i < n2 - 1; i ++) { delta_angle = s2->p[i+1].angle - s2->p[i].angle; if (delta_angle > M_PI) delta_angle -= 2 * M_PI; else if (delta_angle < - M_PI) delta_angle += 2 * M_PI; if (fabs (delta_angle) < 0.1) delta_angle = 0; //engine_debug ("delta_angle = %f\n", delta_angle); if (delta_angle > 0) trend_up += 1; else if (delta_angle < 0) trend_down += 1; else trend_zero += 1; } engine_debug ("up = %d, down = %d, zero = %d\n", trend_up, trend_down, trend_zero); if (((trend_up + trend_zero) >= trend_down * ARCH_THRD) && (trend_up > trend_down)) angle_ckw2 = 1; else if (((trend_down + trend_zero) >= trend_up * ARCH_THRD) && (trend_down > trend_up)) angle_ckw2 = 0; else { g_info->g_type = GESTURE_UNDEF; return; } } if (s1->type_s == STROKE_CURVE) { if (s2->type_s == STROKE_CURVE) { if (angle_ckw1 + angle_ckw2 == 2) g_info->g_type = GESTURE_ROTATE_CKW; else if (angle_ckw1 + angle_ckw2 == 0) g_info->g_type = GESTURE_ROTATE_ACKW; else g_info->g_type = GESTURE_UNDEF; } else { if (angle_ckw1 == 1) g_info->g_type = GESTURE_ROTATE_CKW; else g_info->g_type = GESTURE_ROTATE_ACKW; } } else { if (angle_ckw2 == 1) g_info->g_type = GESTURE_ROTATE_CKW; else g_info->g_type = GESTURE_ROTATE_ACKW; } return; } void gesture_recog (gesture *g, gesture_info *g_info) { stroke *s1 = &(g->s[0]); stroke *s2 = &(g->s[1]); float s1_delta_x; float s1_delta_y; float s2_delta_x; float s2_delta_y; int n1 = s1->num_p; int n2 = s2->num_p; //engine_debug ("gesture recog: stroke type = %d, %d\n", s1->type_s, s2->type_s); if (((s1->type_s == STROKE_DOT) && (s2->type_s == STROKE_DOT)) || ((s1->type_s == STROKE_LINE) && (s2->type_s == STROKE_CURVE)) || ((s1->type_s == STROKE_CURVE) && (s2->type_s == STROKE_LINE))) g_info->g_type = GESTURE_UNDEF; else if (((s1->type_s == STROKE_DOT) && (s2->type_s == STROKE_LINE)) || ((s1->type_s == STROKE_LINE) && (s2->type_s == STROKE_DOT))) { handle_dot_line (g, g_info); } else if (((s1->type_s == STROKE_LINE) && (s2->type_s == STROKE_LINE))) { if (fabs(s1->ols_angle - s2->ols_angle) > PARA_THRD) { //TODO, We can do more to recogise more gestures g_info->g_type = GESTURE_UNDEF; } s1_delta_x = s1->p[n1 - 1].x - s1->p[0].x; s1_delta_y = s1->p[n1 - 1].y - s1->p[0].y; s2_delta_x = s2->p[n2 - 2].x - s2->p[0].x; s2_delta_y = s2->p[n2 - 2].y - s2->p[0].y; engine_debug ("x1[n] = %f, y1[n] = %f, x2[n] = %f, y2[n] = %f\n", s1->p[n1 - 1].x , s1->p[n1 - 1].y , s2->p[n2 - 1].x , s2->p[n2 - 1].y ); engine_debug ("x1[0] = %f, y1[0] = %f, x2[0] = %f, y2[0] = %f\n", s1->p[0].x , s1->p[0].y , s2->p[0].x , s2->p[0].y ); engine_debug ("n1 = %d, n2 = %d\n", n1, n2); engine_debug ("x1= %f, y1 = %f, x2 = %f, y2 = %f\n", s1_delta_x, s1_delta_y, s2_delta_x, s2_delta_y); if (s1->ols_a < X_AXIS_THRD || s2->ols_a < X_AXIS_THRD) { if (s1_delta_x * s2_delta_x > 0) g_info->g_type = GESTURE_MT_NAVIGATE; } else if (s1->ols_a > Y_AXIS_THRD || s2->ols_a > Y_AXIS_THRD) { if (s1_delta_y * s2_delta_y > 0) g_info->g_type = GESTURE_MT_NAVIGATE; } else { if ((s1_delta_x * s2_delta_x > 0) || (s1_delta_y * s2_delta_y > 0)) g_info->g_type = GESTURE_MT_NAVIGATE; } handle_pinch (g, g_info); } /* else if (((s1->type_s == STROKE_DOT) && (s2->type_s == STROKE_CURVE)) || ((s1->type_s == STROKE_CURVE) && (s2->type_s == STROKE_DOT)) || ((s1->type_s == STROKE_CURVE) && (s2->type_s == STROKE_CURVE))) */ else { handle_rotate (g, g_info); } return; } void gesture_process (GSList *list1, GSList *list2, gesture_info *g_info) { stroke_type s_type; stroke_type s_type_1; stroke *a = stroke_new (MAX_P_LEN); stroke *b = stroke_new (MAX_P_LEN); gesture *g = gesture_new (2); memset (g_info, 0, sizeof (gesture_info)); GSList *l; coor *point; for (l = list1; l; l = l->next) { point = l->data; } for (l = list2; l; l = l->next) { point = l->data; } add_stroke_into_gesture (g, a); add_stroke_into_gesture (g, b); add_point_glist (&(g->s[0]), list1); add_point_glist (&(g->s[1]), list2); s_type = compute_stroke_type (g->s); s_type_1 = compute_stroke_type (&(g->s[1])); engine_debug ("stroke 1 type is: "); if (s_type == STROKE_DOT) engine_debug ("dot\n"); else if (s_type == STROKE_LINE) engine_debug ("line\n"); else engine_debug ("curve\n"); engine_debug ("stroke 2 type is: "); if (s_type_1 == STROKE_DOT) engine_debug ("dot\n"); else if (s_type_1 == STROKE_LINE) engine_debug ("line\n"); else engine_debug ("curve\n"); gesture_recog (g, g_info); stroke_del (a); stroke_del (b); gesture_del (g); return; } #if 0 gesture_type gesture_process (GSList *list1, GSList *list2) { coor *point; for (; list1; list1 = list1->next) { point = list1->data; engine_debug("list1: x/y is %d/%d\n", point->x, point->y); } for (; list2; list2 = list2->next) { point = list2->data; engine_debug("list2: x/y is %d/%d\n", point->x, point->y); } return 0; // engine_debug(); } #endif clutter-gesture-0.0.2.1/engine/gesture_recog.h000066400000000000000000000051321132100660000212470ustar00rootroot00000000000000 /* * Copyright (C) 2009 Intel Corp. * Author: Weian Chen * * 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, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #ifndef _GESTURE_RECOG_H_ #define _GESTURE_RECOG_H_ #include #include #include #include #include "config.h" #include "plugin.h" #define MAX_P_LEN 100 #define MAX_S_LEN 10 #define LINE_THRD 0.003 #define OLS_LINE_THRD 0.75 #define PARA_THRD 0.53 // the angle < PI/6, we think they are para or in the same line #define P_NUM_THRD 3 #define DIS_THRD 2.0 //NEED ADJUSTMENT #define X_AXIS_THRD 0.10 #define Y_AXIS_THRD 10.0 // PI/32 #define ARCH_THRD 2.0 typedef enum { STROKE_DOT = 0, STROKE_LINE, STROKE_CURVE }stroke_type; typedef struct coord coor; typedef struct { float x; float y; double angle; }point; typedef struct { point *p; double ex_angle; double var_angle; double ols_a; double ols_b; double ols_r; double ols_angle; int num_p; int max_num_p; stroke_type type_s; }stroke; typedef struct { stroke s[MAX_S_LEN]; int num_s; int max_num_s; }gesture; stroke *stroke_new (int n); void stroke_del (stroke *s); void add_point (stroke *s, point p); void add_point_list (stroke *s, point *p, int n); void compute_angle (stroke *s); void compute_ex_var (stroke *s); void compute_ols (stroke *s); stroke_type compute_stroke_type (stroke *s); gesture *gesture_new (int n); void gesture_del (gesture *g); void add_stroke_into_gesture (gesture *g, stroke *s); void handle_pinch (gesture *g, gesture_info *g_info); void handle_rotate (gesture *g, gesture_info *g_info); void gesture_recog (gesture *g, gesture_info *g_info); void gesture_process (GSList *list1, GSList *list2, gesture_info *g_info); #ifdef GESTURE_DEBUG #define engine_debug(format, args...) do {\ fprintf(stderr, format, ##args); \ } while(0) #else #define engine_debug(format, ...) #endif #endif clutter-gesture-0.0.2.1/engine/plugin.h000066400000000000000000000022521132100660000177100ustar00rootroot00000000000000/* * Copyright (C) 2009 Intel Corp. * Author: Forrest Zhao * * 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, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #ifndef __plugin_h__ #define __plugin_h__ struct coord { gfloat x; gfloat y; }; typedef enum { GESTURE_PINCH_IN, GESTURE_PINCH_OUT, GESTURE_ROTATE_CKW, GESTURE_ROTATE_ACKW, GESTURE_MT_NAVIGATE, GESTURE_UNDEF }gesture_type; typedef struct { gesture_type g_type; double angle; struct coord center; }gesture_info; #endif clutter-gesture-0.0.2.1/engine/stroke.c000066400000000000000000000224061132100660000177170ustar00rootroot00000000000000/* * Copyright (c) 2009, Thomas Jaeger * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #define _GNU_SOURCE #include #include #include #include #include #include #include "../clutter-gesture/gesture-events.h" #include "stroke.h" #include "plugin.h" #include "gesture_recog.h" const double stroke_infinity = 0.2; #define EPS 0.000001 struct point { double x; double y; double t; double dt; double alpha; }; struct _stroke_t { int n; int capacity; struct point *p; }; stroke_t *stroke_alloc(int n) { assert(n > 0); stroke_t *s = malloc(sizeof(stroke_t)); s->n = 0; s->capacity = n; s->p = calloc(n, sizeof(struct point)); return s; } void stroke_add_point(stroke_t *s, double x, double y) { assert(s->capacity > s->n); s->p[s->n].x = x; s->p[s->n].y = y; s->n++; } inline static double angle_difference(double alpha, double beta) { // return 1.0 - cos((alpha - beta) * M_PI); double d = alpha - beta; if (d < -1.0) d += 2.0; else if (d > 1.0) d -= 2.0; return d; } void stroke_finish(stroke_t *s) { int i; assert(s->capacity > 0); s->capacity = -1; int n = s->n - 1; double total = 0.0; s->p[0].t = 0.0; for (i = 0; i < n; i++) { total += hypot(s->p[i+1].x - s->p[i].x, s->p[i+1].y - s->p[i].y); s->p[i+1].t = total; } for (i = 0; i <= n; i++) s->p[i].t /= total; double minX = s->p[0].x, minY = s->p[0].y, maxX = minX, maxY = minY; for (i = 1; i <= n; i++) { if (s->p[i].x < minX) minX = s->p[i].x; if (s->p[i].x > maxX) maxX = s->p[i].x; if (s->p[i].y < minY) minY = s->p[i].y; if (s->p[i].y > maxY) maxY = s->p[i].y; } double scaleX = maxX - minX; double scaleY = maxY - minY; double scale = (scaleX > scaleY) ? scaleX : scaleY; if (scale < 0.001) scale = 1; for (i = 0; i <= n; i++) { s->p[i].x = (s->p[i].x-(minX+maxX)/2)/scale + 0.5; s->p[i].y = (s->p[i].y-(minY+maxY)/2)/scale + 0.5; } for (i = 0; i < n; i++) { s->p[i].dt = s->p[i+1].t - s->p[i].t; s->p[i].alpha = atan2(s->p[i+1].y - s->p[i].y, s->p[i+1].x - s->p[i].x)/M_PI; } } void stroke_free(stroke_t *s) { if (s) free(s->p); free(s); } int stroke_get_size(const stroke_t *s) { return s->n; } void stroke_get_point(const stroke_t *s, int n, double *x, double *y) { assert(n < s->n); if (x) *x = s->p[n].x; if (y) *y = s->p[n].y; } double stroke_get_time(const stroke_t *s, int n) { assert(n < s->n); return s->p[n].t; } double stroke_get_angle(const stroke_t *s, int n) { assert(n+1 < s->n); return s->p[n].alpha; } inline static double sqr(double x) { return x*x; } double stroke_angle_difference(const stroke_t *a, const stroke_t *b, int i, int j) { return fabs(angle_difference(stroke_get_angle(a, i), stroke_get_angle(b, j))); } double stroke_compare(const stroke_t *a, const stroke_t *b, int *path_x, int *path_y) { int m = a->n - 1; int n = b->n - 1; int i, j, x, y, x_, y_; double dist[m+1][n+1]; int prev_x[m+1][n+1]; int prev_y[m+1][n+1]; for (i = 0; i < m; i++) for (j = 0; j < n; j++) dist[i][j] = stroke_infinity; dist[m][n] = stroke_infinity; dist[0][0] = 0.0; for (x = 0; x < m; x++) { for (y = 0; y < n; y++) { if (dist[x][y] >= stroke_infinity) continue; double tx = a->p[x].t; double ty = b->p[y].t; int max_x = x; int max_y = y; int k = 0; inline void step(int x2, int y2) { double dtx = a->p[x2].t - tx; double dty = b->p[y2].t - ty; if (dtx >= dty * 2.2 || dty >= dtx * 2.2 || dtx < EPS || dty < EPS) return; k++; inline double ad(int i, int j) { return sqr(angle_difference(a->p[i].alpha, b->p[j].alpha)); } double d = (a->p[x].dt + b->p[y].dt) * ad(x,y); for (x_ = x+1; x_ < x2; x_++) d += a->p[x_].dt * ad(x_,y); for (y_ = y+1; y_ < y2; y_++) d += b->p[y_].dt * ad(x, y_); double new_dist = dist[x][y] + d; if (new_dist != new_dist) {abort();} if (new_dist >= dist[x2][y2]) return; prev_x[x2][y2] = x; prev_y[x2][y2] = y; dist[x2][y2] = new_dist; } while (k < 4) { int x2, y2; if (a->p[max_x+1].t - tx > b->p[max_y+1].t - ty) { max_y++; if (max_y == n) { step(m, n); break; } for (x2 = x+1; x2 <= max_x; x2++) step(x2, max_y); } else { max_x++; if (max_x == m) { step(m, n); break; } for (y2 = y+1; y2 <= max_y; y2++) step(max_x, y2); } } } } double cost = dist[m][n]; if (path_x && path_y) { if (cost < stroke_infinity) { int x = m; int y = n; int k = 0; while (x || y) { int old_x = x; x = prev_x[x][y]; y = prev_y[old_x][y]; path_x[k] = x; path_y[k] = y; k++; } } else { path_x[0] = 0; path_y[0] = 0; } } return cost; } /* ret 0 means not a match */ ClutterGestureSlideDirection st_gesture_process(void *handle, GSList *list, guint num) { struct coord *coord; GSList *l, *stroke_list = handle; stroke_t *s = stroke_alloc(num); ClutterGestureSlideDirection direction = 0; engine_debug("at the begin of st_gesture_process(\n"); for (; list; list = list->next) { coord = list->data; stroke_add_point(s, coord->x, coord->y); engine_debug("in st_gesture_process, x/y is %f/%f\n", coord->x, coord->y); } stroke_finish(s); for (l = stroke_list; l; l = l->next) { stroke_t *target_s = l->data; double cost, score; direction++; cost = stroke_compare(s, target_s, NULL, NULL); engine_debug("in gesture_recognize, cost is %f\n", cost); if (cost >= stroke_infinity) { continue; } score = MAX(1.0 - 2.5*cost, 0.0); engine_debug("in gesture_recognize, score is %f\n", score); if (score > 0.7) return direction; } return 0; } void *s_plugin_init() { GSList *stroke_list = NULL; struct coord slide_down_coords[5] = {{454, 344}, {454, 345}, {453, 355}, {451, 362}, {451, 362}}; struct coord slide_up_coords[5] = {{545, 443}, {545, 442}, {545, 426}, {545, 398}, {545, 388}}; struct coord slide_left_coords[5] = {{556, 389}, {550, 389}, {524, 389}, {488, 389}, {445, 389}}; // {{376, 383}, {374, 383}, {358, 384}, {341, 384}, {332, 381}}; struct coord slide_right_coords[5] = {{310, 389}, {312, 389}, {362, 389}, {411, 389}, {411, 389}}; stroke_t *slidedown_s = stroke_alloc(5), *slideup_s = stroke_alloc(5), *slideright_s = stroke_alloc(5), *slideleft_s = stroke_alloc(5); gint i; for (i = 0; i < 5; i++) { stroke_add_point(slideup_s, slide_up_coords[i].x, slide_up_coords[i].y); } stroke_finish(slideup_s); stroke_list = g_slist_append(stroke_list, slideup_s); for (i = 0; i < 5; i++) { stroke_add_point(slidedown_s, slide_down_coords[i].x, slide_down_coords[i].y); } stroke_finish(slidedown_s); stroke_list = g_slist_append(stroke_list, slidedown_s); for (i = 0; i < 5; i++) { stroke_add_point(slideleft_s, slide_left_coords[i].x, slide_left_coords[i].y); } stroke_finish(slideleft_s); stroke_list = g_slist_append(stroke_list, slideleft_s); for (i = 0; i < 5; i++) { stroke_add_point(slideright_s, slide_right_coords[i].x, slide_right_coords[i].y); } stroke_finish(slideright_s); stroke_list = g_slist_append(stroke_list, slideright_s); return stroke_list; } void s_plugin_shutdown(void *handle) { GSList *stroke_list = handle, *l; for (l = stroke_list; l; l = l->next) { stroke_t *s = l->data; stroke_free(s); } g_slist_free(stroke_list); } clutter-gesture-0.0.2.1/engine/stroke.h000066400000000000000000000033641132100660000177260ustar00rootroot00000000000000/* * Copyright (c) 2009, Thomas Jaeger * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef __STROKE_H__ #define __STROKE_H__ #ifdef __cplusplus extern "C" { #endif struct _stroke_t; typedef struct _stroke_t stroke_t; stroke_t *stroke_alloc(int n); void stroke_add_point(stroke_t *stroke, double x, double y); void stroke_finish(stroke_t *stroke); void stroke_free(stroke_t *stroke); int stroke_get_size(const stroke_t *stroke); void stroke_get_point(const stroke_t *stroke, int n, double *x, double *y); double stroke_get_time(const stroke_t *stroke, int n); double stroke_get_angle(const stroke_t *stroke, int n); double stroke_angle_difference(const stroke_t *a, const stroke_t *b, int i, int j); double stroke_compare(const stroke_t *a, const stroke_t *b, int *path_x, int *path_y); extern const double stroke_infinity; ClutterGestureSlideDirection st_gesture_process(void *handle, GSList *list, guint num); void *s_plugin_init(); void s_plugin_shutdown(void *handle); #ifdef __cplusplus } #endif #endif clutter-gesture-0.0.2.1/po/000077500000000000000000000000001132100660000154115ustar00rootroot00000000000000clutter-gesture-0.0.2.1/po/POTFILES000066400000000000000000000000001132100660000165470ustar00rootroot00000000000000clutter-gesture-0.0.2.1/po/POTFILES.in000066400000000000000000000000001132100660000171540ustar00rootroot00000000000000clutter-gesture-0.0.2.1/tests/000077500000000000000000000000001132100660000161355ustar00rootroot00000000000000clutter-gesture-0.0.2.1/tests/Makefile.am000066400000000000000000000025761132100660000202030ustar00rootroot00000000000000# This file is part of Clutter-Gesture. # # Copyright 2009 (c) Intel Corp. # Author: Long Bu (long.bu@intel.com) # # Clutter-Gesture 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.1 of the License, # or (at your option) any later version. # # Clutter-Gesture 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 # General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with ClutterGesture; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 # USA. noinst_PROGRAMS = test test_engine INCLUDES = -I../clutter-gesture/ LIBS = @CLUTTERGESTURE_LIBS@ test_SOURCES = test-events.c test_CFLAGS = @CLUTTERGESTURE_CFLAGS@ -g3 -O0 -ggdb -DPKGDATADIR="\"$(pkgdatadir)\"" test_LDFLAGS = $(top_builddir)/clutter-gesture/libcluttergesture-@PACKAGE_VERSION@.la test_engine_SOURCES = test_engine.c test_engine_CFLAGS = @CLUTTERGESTURE_CFLAGS@ -g3 -O0 -ggdb -DPKGDATADIR="\"$(pkgdatadir)\"" test_engine_LDFLAGS = $(top_builddir)/clutter-gesture/libcluttergesture-@PACKAGE_VERSION@.la CLEANFILES = *~ test-events, test_engine clutter-gesture-0.0.2.1/tests/test-events.c000066400000000000000000000262601132100660000205700ustar00rootroot00000000000000/*** * This file is part of Clutter-Gesture. * * Portions Copyright 2009 (c) Intel Corp. * Author: Long Bu (long.bu@intel.com) * * This file is modified based on test-events of Clutter/tests * Copyright (C) 2006 OpenedHand * * Clutter-Gesture 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.1 of the License, * or (at your option) any later version. * * Clutter-Gesture 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 * General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with ClutterGesture; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * USA. ***/ #include #include #include #include #include "clutter-gesture.h" gboolean IsFullScreen = FALSE, IsMotion = TRUE; #if 0 static void stage_state_cb (ClutterStage *stage, gpointer data) { gchar *detail = (gchar*)data; printf("[stage signal] %s\n", detail); } static gboolean blue_button_cb (ClutterActor *actor, ClutterEvent *event, gpointer data) { g_print ("blue_button_cb is called\n"); return FALSE; } static gboolean red_button_cb (ClutterActor *actor, ClutterEvent *event, gpointer data) { if (IsMotion) IsMotion = FALSE; else IsMotion = TRUE; clutter_set_motion_events_enabled (IsMotion); g_print ("*** Per actor motion events %s ***\n", IsMotion ? "enabled" : "disabled"); return FALSE; } #endif #if 0 static gboolean capture_cb (ClutterActor *actor, ClutterEvent *event, gpointer data) { g_print ("* captured event for type '%s' *\n", G_OBJECT_TYPE_NAME (actor)); return FALSE; } #endif /* static void key_focus_in_cb (ClutterActor *actor, gpointer data) { ClutterActor *focus_box = CLUTTER_ACTOR(data); if (actor == clutter_stage_get_default ()) clutter_actor_hide (focus_box); else { clutter_actor_set_position (focus_box, clutter_actor_get_x (actor) - 5, clutter_actor_get_y (actor) - 5); clutter_actor_set_size (focus_box, clutter_actor_get_width (actor) + 10, clutter_actor_get_height (actor) + 10); clutter_actor_show (focus_box); } } */ static void fill_keybuf (char *keybuf, ClutterKeyEvent *event) { char utf8[6]; int len; /* printable character, if any (ß, ∑) */ len = g_unichar_to_utf8 (event->unicode_value, utf8); utf8[len] = '\0'; sprintf(keybuf, "'%s' ", utf8); /* key combination (s, S, Delete) */ len = g_unichar_to_utf8 (clutter_keysym_to_unicode (event->keyval), utf8); utf8[len] = '\0'; if (event->modifier_state & CLUTTER_SHIFT_MASK) strcat (keybuf, ""); if (event->modifier_state & CLUTTER_LOCK_MASK) strcat (keybuf, ""); if (event->modifier_state & CLUTTER_CONTROL_MASK) strcat (keybuf, ""); if (event->modifier_state & CLUTTER_MOD1_MASK) strcat (keybuf, ""); if (event->modifier_state & CLUTTER_MOD2_MASK) strcat (keybuf, ""); if (event->modifier_state & CLUTTER_MOD3_MASK) strcat (keybuf, ""); if (event->modifier_state & CLUTTER_MOD4_MASK) strcat (keybuf, ""); if (event->modifier_state & CLUTTER_MOD5_MASK) strcat (keybuf, ""); strcat (keybuf, utf8); } static gboolean gesture_cb (ClutterGesture *gesture, ClutterGestureEvent *event, gpointer data) { const char *gesture_name[] = {"dummy", "SLIDE", "HOLD", "PINCH", "WINDOW", "ANY"}; const char *slide_dir_name [] = { "dummy", "SLIDE_UP", "SLIDE_DOWN", "SLIDE_LEFT", "SLIDE_RIGHT"}; printf("gesture_cb: event pointer %p\n", event); if (event) { printf("gesture_cb: event %s\n", gesture_name[event->type]); switch (event->type) { case GESTURE_SLIDE: { ClutterGestureSlideEvent *slide = (ClutterGestureSlideEvent *)event; printf("slide direction :%s\n", slide_dir_name[slide->direction]); break; } case GESTURE_HOLD: { printf("GESTURE_HOLD is received\n"); break; } case GESTURE_PINCH: { printf("GESTURE_PINCH event is received\n"); break; } case GESTURE_ROTATE: { printf("GESTURE_ROTATE event is received\n"); break; } default: break; } } return TRUE; } static gboolean input_cb (ClutterActor *actor, ClutterEvent *event, gpointer data) { ClutterStage *stage = CLUTTER_STAGE (clutter_stage_get_default ()); // gchar keybuf[128], *source = (gchar*)data; gchar keybuf[128]; gfloat x, y; switch (event->type) { case CLUTTER_KEY_PRESS: fill_keybuf (keybuf, &event->key); // printf ("[%s] KEY PRESS %s", source, keybuf); break; case CLUTTER_KEY_RELEASE: fill_keybuf (keybuf, &event->key); // printf ("[%s] KEY RELEASE %s", source, keybuf); break; case CLUTTER_MOTION: // g_print ("[%s] MOTION", source); break; case CLUTTER_ENTER: // g_print ("[%s] ENTER", source); break; case CLUTTER_LEAVE: // g_print ("[%s] LEAVE", source); break; case CLUTTER_BUTTON_PRESS: // g_print ("[%s] BUTTON PRESS (click count:%i)", // source, event->button.click_count); clutter_event_get_coords(event, &x, &y); // g_print("x, y is %d, %d\n", x, y); break; case CLUTTER_BUTTON_RELEASE: // g_print ("[%s] BUTTON RELEASE (click count:%i)", // source, event->button.click_count); if (clutter_event_get_source (event) == CLUTTER_ACTOR (stage)) clutter_stage_set_key_focus (stage, NULL); else if (clutter_event_get_source (event) == actor && clutter_actor_get_parent (actor) == CLUTTER_ACTOR (stage)) clutter_stage_set_key_focus (stage, actor); break; case CLUTTER_SCROLL: // g_print ("[%s] BUTTON SCROLL (click count:%i)", // source, event->button.click_count); break; case CLUTTER_STAGE_STATE: // g_print ("[%s] STAGE STATE", source); break; case CLUTTER_DESTROY_NOTIFY: // g_print ("[%s] DESTROY NOTIFY", source); break; case CLUTTER_CLIENT_MESSAGE: // g_print ("[%s] CLIENT MESSAGE", source); break; case CLUTTER_DELETE: // g_print ("[%s] DELETE", source); break; case CLUTTER_NOTHING: return FALSE; } if (clutter_event_get_source (event) == actor) { ; } // g_print (" *source*"); // g_print ("\n"); return FALSE; } int main (int argc, char *argv[]) { ClutterActor *stage, *actor, *actor1, *group; ClutterColor rcol = { 0xff, 0, 0, 0xff}, bcol = { 0, 0, 0xff, 0xff }, gcol = { 0, 0xff, 0, 0xff }; /* ycol = { 0xff, 0xff, 0, 0xff }; */ /*ncol = { 0, 0, 0, 0xff }; */ ClutterGesture *gesture = NULL; g_type_init(); clutter_x11_enable_xinput(); clutter_init (&argc, &argv); // clutter_set_motion_events_frequency(120); stage = clutter_stage_get_default (); clutter_stage_set_throttle_motion_events(CLUTTER_STAGE(stage), FALSE); clutter_set_default_frame_rate(60); // g_signal_connect (stage, "event", G_CALLBACK (input_cb), "stage"); #if 0 g_signal_connect (stage, "fullscreen", G_CALLBACK (stage_state_cb), "fullscreen"); g_signal_connect (stage, "unfullscreen", G_CALLBACK (stage_state_cb), "unfullscreen"); g_signal_connect (stage, "activate", G_CALLBACK (stage_state_cb), "activate"); g_signal_connect (stage, "deactivate", G_CALLBACK (stage_state_cb), "deactivate"); #endif group = clutter_group_new (); clutter_container_add (CLUTTER_CONTAINER (stage), group, NULL); // clutter_actor_set_position (group, 100, 350); // 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); actor = clutter_rectangle_new_with_color (&rcol); clutter_actor_set_size (actor, 200, 200); clutter_actor_set_position (actor, 100, 100); clutter_actor_set_reactive (actor, TRUE); clutter_container_add (CLUTTER_CONTAINER (group), actor, NULL); g_signal_connect (actor, "event", G_CALLBACK (input_cb), "red box"); /* g_signal_connect (actor, "focus-in", G_CALLBACK (key_focus_in_cb), focus_box); */ clutter_stage_set_key_focus (CLUTTER_STAGE (stage), actor); /* g_signal_connect (actor, "focus-in", G_CALLBACK (key_focus_in_cb), focus_box); */ actor1 = clutter_rectangle_new_with_color (&gcol); clutter_actor_set_size (actor1, 50, 50); clutter_actor_set_position (actor1, 300, 300); //clutter_actor_set_depth (actor1, 10); clutter_actor_set_reactive (actor1, TRUE); clutter_container_add (CLUTTER_CONTAINER (group), actor1, NULL); clutter_actor_set_reactive (group, TRUE); // g_signal_connect (group, "event", G_CALLBACK (input_cb), "group"); clutter_actor_show(actor1); actor = clutter_rectangle_new_with_color (&bcol); clutter_actor_set_size (actor, 50, 50); clutter_actor_set_position (actor, 400, 400); clutter_actor_set_reactive (actor, TRUE); clutter_container_add (CLUTTER_CONTAINER(stage), actor, NULL); g_signal_connect (actor, "event", G_CALLBACK (input_cb), "blue box"); /* g_signal_connect (actor, "focus-in", G_CALLBACK (key_focus_in_cb), focus_box); */ #if 0 /* non reactive */ actor = clutter_rectangle_new_with_color (&ncol); clutter_actor_set_size (actor, 400, 50); clutter_actor_set_position (actor, 100, 250); clutter_container_add (CLUTTER_CONTAINER(stage), actor, NULL); g_signal_connect (actor, "event", G_CALLBACK (input_cb), "blue box"); /* g_signal_connect (actor, "focus-in", G_CALLBACK (key_focus_in_cb), focus_box); g_signal_connect (stage, "focus-in", G_CALLBACK (key_focus_in_cb), focus_box); */ /* non reactive group, with reactive child */ actor = clutter_rectangle_new_with_color (&ycol); clutter_actor_set_size (actor, 100, 100); clutter_actor_set_reactive (actor, TRUE); g_signal_connect (actor, "event", G_CALLBACK (input_cb), "yellow box"); #endif /* note group not reactive */ clutter_actor_show_all (group); clutter_actor_show_all (CLUTTER_ACTOR (stage)); gesture = clutter_gesture_new(CLUTTER_ACTOR(stage)); clutter_gesture_set_gesture_mask(gesture, group, GESTURE_MASK_SLIDE | GESTURE_MASK_HOLD); g_signal_connect (gesture, "gesture-slide-event", G_CALLBACK (gesture_cb), (gpointer)0x11223344); g_signal_connect (gesture, "gesture-hold-event", G_CALLBACK (gesture_cb), (gpointer)0x11223344); clutter_main(); return 0; } clutter-gesture-0.0.2.1/tests/test_engine.c000066400000000000000000000243171132100660000206140ustar00rootroot00000000000000/*** * This file is part of Clutter-Gesture. * * Portions Copyright 2009 (c) Intel Corp. * Author: Forrest Zhao * * This file is modifed based on clutter/tests * * Clutter-Gesture 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.1 of the License, * or (at your option) any later version. * * Clutter-Gesture 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 * General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with ClutterGesture; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * USA. ***/ #include #include #include #include "clutter-gesture.h" #include "../engine/engine.h" #include "gesture-events.h" gboolean IsFullScreen = FALSE, IsMotion = TRUE; handle_t handle; static void stage_state_cb (ClutterStage *stage, gpointer data) { gchar *detail = (gchar*)data; printf("[stage signal] %s\n", detail); } static gboolean blue_button_cb (ClutterActor *actor, ClutterEvent *event, gpointer data) { ClutterActor *stage; stage = clutter_stage_get_default (); if (IsFullScreen) IsFullScreen = FALSE; else IsFullScreen = TRUE; g_object_set (stage, "fullscreen", IsFullScreen, NULL); g_print ("*** Fullscreen %s ***\n", IsFullScreen ? "enabled" : "disabled"); return FALSE; } static gboolean red_button_cb (ClutterActor *actor, ClutterEvent *event, gpointer data) { if (IsMotion) IsMotion = FALSE; else IsMotion = TRUE; clutter_set_motion_events_enabled (IsMotion); g_print ("*** Per actor motion events %s ***\n", IsMotion ? "enabled" : "disabled"); return FALSE; } static gboolean capture_cb (ClutterActor *actor, ClutterEvent *event, gpointer data) { g_print ("* captured event for type '%s' *\n", G_OBJECT_TYPE_NAME (actor)); fill_event(handle, actor, event); return FALSE; } static void key_focus_in_cb (ClutterActor *actor, gpointer data) { ClutterActor *focus_box = CLUTTER_ACTOR(data); if (actor == clutter_stage_get_default ()) clutter_actor_hide (focus_box); else { clutter_actor_set_position (focus_box, clutter_actor_get_x (actor) - 5, clutter_actor_get_y (actor) - 5); clutter_actor_set_size (focus_box, clutter_actor_get_width (actor) + 10, clutter_actor_get_height (actor) + 10); clutter_actor_show (focus_box); } } static void fill_keybuf (char *keybuf, ClutterKeyEvent *event) { char utf8[6]; int len; /* printable character, if any (ß, ∑) */ len = g_unichar_to_utf8 (event->unicode_value, utf8); utf8[len] = '\0'; sprintf(keybuf, "'%s' ", utf8); /* key combination (s, S, Delete) */ len = g_unichar_to_utf8 (clutter_keysym_to_unicode (event->keyval), utf8); utf8[len] = '\0'; if (event->modifier_state & CLUTTER_SHIFT_MASK) strcat (keybuf, ""); if (event->modifier_state & CLUTTER_LOCK_MASK) strcat (keybuf, ""); if (event->modifier_state & CLUTTER_CONTROL_MASK) strcat (keybuf, ""); if (event->modifier_state & CLUTTER_MOD1_MASK) strcat (keybuf, ""); if (event->modifier_state & CLUTTER_MOD2_MASK) strcat (keybuf, ""); if (event->modifier_state & CLUTTER_MOD3_MASK) strcat (keybuf, ""); if (event->modifier_state & CLUTTER_MOD4_MASK) strcat (keybuf, ""); if (event->modifier_state & CLUTTER_MOD5_MASK) strcat (keybuf, ""); strcat (keybuf, utf8); } static gboolean input_cb (ClutterActor *actor, ClutterEvent *event, gpointer data) { ClutterStage *stage = CLUTTER_STAGE (clutter_stage_get_default ()); gchar keybuf[128], *source = (gchar*)data; switch (event->type) { case CLUTTER_KEY_PRESS: fill_keybuf (keybuf, &event->key); printf ("[%s] KEY PRESS %s", source, keybuf); break; case CLUTTER_KEY_RELEASE: fill_keybuf (keybuf, &event->key); printf ("[%s] KEY RELEASE %s", source, keybuf); break; case CLUTTER_MOTION: g_print ("[%s] MOTION", source); break; case CLUTTER_ENTER: g_print ("[%s] ENTER", source); break; case CLUTTER_LEAVE: g_print ("[%s] LEAVE", source); break; case CLUTTER_BUTTON_PRESS: g_print ("[%s] BUTTON PRESS (click count:%i)", source, event->button.click_count); break; case CLUTTER_BUTTON_RELEASE: g_print ("[%s] BUTTON RELEASE (click count:%i)", source, event->button.click_count); if (clutter_event_get_source (event) == CLUTTER_ACTOR (stage)) clutter_stage_set_key_focus (stage, NULL); else if (clutter_event_get_source (event) == actor && clutter_actor_get_parent (actor) == CLUTTER_ACTOR (stage)) clutter_stage_set_key_focus (stage, actor); break; case CLUTTER_SCROLL: g_print ("[%s] BUTTON SCROLL (click count:%i)", source, event->button.click_count); break; case CLUTTER_STAGE_STATE: g_print ("[%s] STAGE STATE", source); break; case CLUTTER_DESTROY_NOTIFY: g_print ("[%s] DESTROY NOTIFY", source); break; case CLUTTER_CLIENT_MESSAGE: g_print ("[%s] CLIENT MESSAGE", source); break; case CLUTTER_DELETE: g_print ("[%s] DELETE", source); break; case CLUTTER_NOTHING: return FALSE; } if (clutter_event_get_source (event) == actor) g_print (" *source*"); g_print ("\n"); return FALSE; } gboolean recognize_callback(ClutterActor *target_actor, event_type_t event_type, guint flag, GSList *event_list, gint number, void *data) { ClutterGestureSlideEvent *gesture_event = event_list->data; g_print("recognize_callback is called, event_type is %d, number is %d, gesture_event_type is %d, x is %f, y is %f, direction is %d\n", event_type, number, gesture_event->type, gesture_event->x_start, gesture_event->y_start, gesture_event->direction); return TRUE; } int main (int argc, char *argv[]) { ClutterActor *stage, *actor, *focus_box, *group; ClutterColor rcol = { 0xff, 0, 0, 0xff}, bcol = { 0, 0, 0xff, 0xff }, gcol = { 0, 0xff, 0, 0xff }, ycol = { 0xff, 0xff, 0, 0xff }, ncol = { 0, 0, 0, 0xff }; ClutterGesture *gesture = NULL; clutter_init (&argc, &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", G_CALLBACK (stage_state_cb), "unfullscreen"); g_signal_connect (stage, "activate", G_CALLBACK (stage_state_cb), "activate"); 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); actor = clutter_rectangle_new_with_color (&rcol); clutter_actor_set_size (actor, 100, 100); clutter_actor_set_position (actor, 100, 100); clutter_actor_set_reactive (actor, TRUE); clutter_container_add (CLUTTER_CONTAINER (stage), actor, NULL); g_signal_connect (actor, "event", G_CALLBACK (input_cb), "red box"); g_signal_connect (actor, "focus-in", G_CALLBACK (key_focus_in_cb), focus_box); /* Toggle motion - enter/leave capture */ g_signal_connect (actor, "button-press-event", G_CALLBACK (red_button_cb), NULL); clutter_stage_set_key_focus (CLUTTER_STAGE (stage), actor); actor = clutter_rectangle_new_with_color (&gcol); clutter_actor_set_size (actor, 100, 100); clutter_actor_set_position (actor, 250, 100); clutter_actor_set_reactive (actor, TRUE); clutter_container_add (CLUTTER_CONTAINER (stage), actor, NULL); g_signal_connect (actor, "event", G_CALLBACK (input_cb), "green box"); 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); clutter_actor_set_reactive (actor, TRUE); clutter_container_add (CLUTTER_CONTAINER(stage), actor, NULL); g_signal_connect (actor, "event", G_CALLBACK (input_cb), "blue box"); g_signal_connect (actor, "focus-in", G_CALLBACK (key_focus_in_cb), focus_box); /* Fullscreen */ g_signal_connect (actor, "button-press-event", G_CALLBACK (blue_button_cb), NULL); /* non reactive */ actor = clutter_rectangle_new_with_color (&ncol); clutter_actor_set_size (actor, 400, 50); clutter_actor_set_position (actor, 100, 250); clutter_container_add (CLUTTER_CONTAINER(stage), actor, NULL); g_signal_connect (actor, "event", G_CALLBACK (input_cb), "blue box"); g_signal_connect (actor, "focus-in", G_CALLBACK (key_focus_in_cb), focus_box); g_signal_connect (stage, "focus-in", G_CALLBACK (key_focus_in_cb), focus_box); /* non reactive group, with reactive child */ actor = clutter_rectangle_new_with_color (&ycol); clutter_actor_set_size (actor, 100, 100); clutter_actor_set_reactive (actor, TRUE); g_signal_connect (actor, "event", G_CALLBACK (input_cb), "yellow box"); /* note group not reactive */ group = clutter_group_new (); clutter_container_add (CLUTTER_CONTAINER (group), actor, NULL); clutter_container_add (CLUTTER_CONTAINER (stage), group, NULL); clutter_actor_set_position (group, 100, 350); clutter_actor_show_all (group); clutter_actor_show_all (CLUTTER_ACTOR (stage)); gesture = clutter_gesture_new(CLUTTER_ACTOR(stage)); handle = engine_init(recognize_callback, NULL); set_gesture_mask(handle, stage, 1); clutter_main(); return 0; }