diff --git a/src/sugar3/event-controller/Makefile.am b/src/sugar3/event-controller/Makefile.am index 65b0f1c8..f1f5a795 100644 --- a/src/sugar3/event-controller/Makefile.am +++ b/src/sugar3/event-controller/Makefile.am @@ -16,6 +16,7 @@ eventcontroller_h_sources = \ sugar-long-press-controller.h \ sugar-rotate-controller.h \ sugar-swipe-controller.h \ + sugar-touch-controller.h \ sugar-zoom-controller.h eventcontroller_c_sources = \ @@ -23,6 +24,7 @@ eventcontroller_c_sources = \ sugar-long-press-controller.c \ sugar-rotate-controller.c \ sugar-swipe-controller.c \ + sugar-touch-controller.c \ sugar-zoom-controller.c diff --git a/src/sugar3/event-controller/sugar-touch-controller.c b/src/sugar3/event-controller/sugar-touch-controller.c new file mode 100644 index 00000000..5b5b24e3 --- /dev/null +++ b/src/sugar3/event-controller/sugar-touch-controller.c @@ -0,0 +1,309 @@ +/* + * Copyright (C) 2012, One Laptop Per Child. + * + * 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. + * + * Author(s): Carlos Garnacho + */ + +#include "sugar-touch-controller.h" +#define TOUCHES_IN_RANGE(t,p) ((t) >= (p)->min_touches && (t) <= (p)->max_touches) + +typedef struct _SugarTouchControllerPriv SugarTouchControllerPriv; +typedef struct _SugarTouch SugarTouch; + +enum { + PROP_MIN_TOUCHES = 1, + PROP_MAX_TOUCHES +}; + +struct _SugarTouchControllerPriv +{ + GHashTable *touches; + gint min_touches; + gint max_touches; +}; + +G_DEFINE_ABSTRACT_TYPE (SugarTouchController, sugar_touch_controller, + SUGAR_TYPE_EVENT_CONTROLLER) + +static void +sugar_touch_controller_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + SugarTouchControllerPriv *priv; + + priv = SUGAR_TOUCH_CONTROLLER (object)->_priv; + + switch (prop_id) + { + case PROP_MIN_TOUCHES: + g_value_set_int (value, priv->min_touches); + break; + case PROP_MAX_TOUCHES: + g_value_set_int (value, priv->max_touches); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sugar_touch_controller_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + SugarTouchControllerPriv *priv; + + priv = SUGAR_TOUCH_CONTROLLER (object)->_priv; + + switch (prop_id) + { + case PROP_MIN_TOUCHES: + priv->min_touches = g_value_get_int (value); + break; + case PROP_MAX_TOUCHES: + priv->max_touches = g_value_get_int (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sugar_touch_controller_finalize (GObject *object) +{ + SugarTouchControllerPriv *priv; + + priv = SUGAR_TOUCH_CONTROLLER (object)->_priv; + g_hash_table_destroy (priv->touches); + + G_OBJECT_CLASS (sugar_touch_controller_parent_class)->finalize (object); +} + +static gboolean +sugar_touch_controller_handle_event (SugarEventController *controller, + GdkEvent *event) +{ + SugarTouchControllerPriv *priv; + GdkEventSequence *sequence; + gboolean handled = TRUE; + GdkPoint *point; + gint n_touches, prev_n_touches; + gboolean is_in_range, was_in_range; + + priv = SUGAR_TOUCH_CONTROLLER (controller)->_priv; + sequence = gdk_event_get_event_sequence (event); + prev_n_touches = g_hash_table_size (priv->touches); + was_in_range = TOUCHES_IN_RANGE (prev_n_touches, priv); + + if (!sequence) + return FALSE; + + switch (event->type) + { + case GDK_TOUCH_BEGIN: + point = g_new0 (GdkPoint, 1); + point->x = event->touch.x; + point->y = event->touch.y; + g_hash_table_insert (priv->touches, sequence, point); + break; + case GDK_TOUCH_END: + g_hash_table_remove (priv->touches, sequence); + break; + case GDK_TOUCH_UPDATE: + point = g_hash_table_lookup (priv->touches, sequence); + point->x = event->touch.x; + point->y = event->touch.y; + break; + default: + handled = FALSE; + } + + n_touches = g_hash_table_size (priv->touches); + is_in_range = TOUCHES_IN_RANGE (n_touches, priv); + + if (handled) + { + if (is_in_range) + { + if (!was_in_range) + g_signal_emit_by_name (controller, "began"); + else + g_signal_emit_by_name (controller, "updated"); + } + else if (was_in_range) + g_signal_emit_by_name (controller, "ended"); + } + + return handled; +} + +static void +sugar_touch_controller_reset (SugarEventController *controller) +{ + SugarTouchControllerPriv *priv; + gint n_touches; + + priv = SUGAR_TOUCH_CONTROLLER (controller)->_priv; + n_touches = g_hash_table_size (priv->touches); + + if (TOUCHES_IN_RANGE (n_touches, priv)) + g_signal_emit_by_name (G_OBJECT (controller), "ended"); + + g_hash_table_remove_all (priv->touches); + g_object_notify (G_OBJECT (controller), "state"); +} + +static void +sugar_touch_controller_class_init (SugarTouchControllerClass *klass) +{ + SugarEventControllerClass *controller_class = SUGAR_EVENT_CONTROLLER_CLASS (klass); + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + controller_class->handle_event = sugar_touch_controller_handle_event; + controller_class->reset = sugar_touch_controller_reset; + + object_class->get_property = sugar_touch_controller_get_property; + object_class->set_property = sugar_touch_controller_set_property; + object_class->finalize = sugar_touch_controller_finalize; + + g_object_class_install_property (object_class, + PROP_MIN_TOUCHES, + g_param_spec_int ("min-touches", + "Minimum number of touches", + "Minimum Number of touches", + 1, G_MAXINT, 1, + G_PARAM_CONSTRUCT | + G_PARAM_READWRITE | + G_PARAM_STATIC_NAME | + G_PARAM_STATIC_NICK | + G_PARAM_STATIC_BLURB)); + g_object_class_install_property (object_class, + PROP_MAX_TOUCHES, + g_param_spec_int ("max-touches", + "Maximum number of touches", + "Maximum Number of touches", + 1, G_MAXINT, 1, + G_PARAM_CONSTRUCT | + G_PARAM_READWRITE | + G_PARAM_STATIC_NAME | + G_PARAM_STATIC_NICK | + G_PARAM_STATIC_BLURB)); + + g_type_class_add_private (object_class, sizeof (SugarTouchControllerPriv)); +} + +static void +sugar_touch_controller_init (SugarTouchController *controller) +{ + SugarTouchControllerPriv *priv; + controller->_priv = priv = G_TYPE_INSTANCE_GET_PRIVATE (controller, + SUGAR_TYPE_TOUCH_CONTROLLER, + SugarTouchControllerPriv); + priv->touches = g_hash_table_new_full (NULL, NULL, NULL, + (GDestroyNotify) g_free); +} + +gboolean +sugar_touch_controller_get_center (SugarTouchController *controller, + gint *center_x, + gint *center_y) +{ + SugarTouchControllerPriv *priv; + GHashTableIter iter; + GdkPoint *point; + gint x1, y1, x2, y2, dx, dy; + + g_return_val_if_fail (SUGAR_IS_TOUCH_CONTROLLER (controller), FALSE); + + priv = controller->_priv; + g_hash_table_iter_init (&iter, priv->touches); + + while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &point)) + { + x1 = MIN (x1, point->x); + y1 = MIN (y1, point->y); + x2 = MAX (x2, point->x); + y2 = MAX (y2, point->x); + } + + if (center_x) + { + dx = x2 - x1; + *center_x = x1 + (ABS (dx) / 2); + } + + if (center_y) + { + dy = y2 - y1; + *center_y = y1 + (ABS (dy) / 2); + } +} + +gint +sugar_touch_controller_get_num_touches (SugarTouchController *controller) +{ + SugarTouchControllerPriv *priv; + + g_return_val_if_fail (SUGAR_IS_TOUCH_CONTROLLER (controller), 0); + + priv = controller->_priv; + + return g_hash_table_size (priv->touches); +} + +GList * +sugar_touch_controller_get_sequences (SugarTouchController *controller) +{ + SugarTouchControllerPriv *priv; + + g_return_val_if_fail (SUGAR_IS_TOUCH_CONTROLLER (controller), NULL); + + priv = controller->_priv; + + return g_hash_table_get_keys (priv->touches); +} + +gboolean +sugar_touch_controller_get_coords (SugarTouchController *controller, + GdkEventSequence *sequence, + gint *x, + gint *y) +{ + SugarTouchControllerPriv *priv; + GdkPoint *point; + + g_return_val_if_fail (SUGAR_IS_TOUCH_CONTROLLER (controller), FALSE); + g_return_val_if_fail (sequence != NULL, FALSE); + + priv = controller->_priv; + point = g_hash_table_lookup (priv->touches, sequence); + + if (!point) + return FALSE; + + if (x) + *x = point->x; + + if (y) + *y = point->y; + + return TRUE; +} diff --git a/src/sugar3/event-controller/sugar-touch-controller.h b/src/sugar3/event-controller/sugar-touch-controller.h new file mode 100644 index 00000000..7d8e9492 --- /dev/null +++ b/src/sugar3/event-controller/sugar-touch-controller.h @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2012, One Laptop Per Child. + * + * 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. + * + * Author(s): Carlos Garnacho + */ + +#if !defined (__SUGAR_CONTROLLERS_H_INSIDE__) && !defined (SUGAR_TOOLKIT_COMPILATION) +#error "Only can be included directly." +#endif + +#ifndef __SUGAR_TOUCH_CONTROLLER_H__ +#define __SUGAR_TOUCH_CONTROLLER_H__ + +#include "sugar-event-controller.h" +#include + +G_BEGIN_DECLS + +#define SUGAR_TYPE_TOUCH_CONTROLLER (sugar_touch_controller_get_type ()) +#define SUGAR_TOUCH_CONTROLLER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), SUGAR_TYPE_TOUCH_CONTROLLER, SugarTouchController)) +#define SUGAR_TOUCH_CONTROLLER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), SUGAR_TYPE_TOUCH_CONTROLLER, SugarTouchControllerClass)) +#define SUGAR_IS_TOUCH_CONTROLLER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), SUGAR_TYPE_TOUCH_CONTROLLER)) +#define SUGAR_IS_TOUCH_CONTROLLER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), SUGAR_TYPE_TOUCH_CONTROLLER)) +#define SUGAR_TOUCH_CONTROLLER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), SUGAR_TYPE_TOUCH_CONTROLLER, SugarTouchControllerClass)) + +typedef struct _SugarTouchController SugarTouchController; +typedef struct _SugarTouchControllerClass SugarTouchControllerClass; + +struct _SugarTouchController +{ + SugarEventController parent_instance; + gpointer _priv; +}; + +struct _SugarTouchControllerClass +{ + SugarEventControllerClass parent_class; +}; + +GType sugar_touch_controller_get_type (void) G_GNUC_CONST; +gboolean sugar_touch_controller_get_center (SugarTouchController *controller, + gint *x, + gint *y); + +gint sugar_touch_controller_get_num_touches (SugarTouchController *controller); +GList * sugar_touch_controller_get_sequences (SugarTouchController *controller); +gboolean sugar_touch_controller_get_coords (SugarTouchController *controller, + GdkEventSequence *sequence, + gint *x, + gint *y); + +G_END_DECLS + +#endif /* __SUGAR_TOUCH_CONTROLLER_H__ */