event-controller: Add separate library for event controllers

SugarEventController is an abstract object that attaches to a widget
and interprets an arbitrary set of events. Implementations of that
object get to define the sequence of events that trigger these.

The basic touch gestures (long press, rotate, swipe, zoom) have
been implemented on top of that object.

Signed-off-by: Carlos Garnacho <carlos@lanedo.com>
Acked-by: Simon Schampijer <simon@laptop.org>
master
Carlos Garnacho 12 years ago committed by Simon Schampijer
parent 4cf9ce0357
commit ade02f958b

@ -25,6 +25,9 @@ PKG_CHECK_MODULES(EXT, gtk+-3.0 gdk-3.0 gdk-pixbuf-2.0 sm ice alsa librsvg-2.0)
PYGTK_DEFSDIR=`$PKG_CONFIG --variable=defsdir pygtk-2.0`
AC_SUBST(PYGTK_DEFSDIR)
GLIB_MKENUMS=`$PKG_CONFIG glib-2.0 --variable=glib_mkenums`
AC_SUBST(GLIB_MKENUMS)
# Setup GETTEXT
#
ALL_LINGUAS="af am ar aym bg bi bn_IN bn ca cs da de dz el en es fa_AF fa ff fil fr gu ha he hi ht hu id ig is it ja km ko kos mg mi mk ml mn mr ms mvo nb ne nl pa pap pl ps pt_BR pt quz ro ru rw sd si sk sl sq sv sw ta te th tr tvl tzo ug ur vi wa yo zh_CN zh_TW"
@ -45,6 +48,7 @@ src/sugar3/Makefile
src/sugar3/activity/Makefile
src/sugar3/bundle/Makefile
src/sugar3/graphics/Makefile
src/sugar3/event-controller/Makefile
src/sugar3/presence/Makefile
src/sugar3/datastore/Makefile
src/sugar3/dispatch/Makefile

@ -1,4 +1,4 @@
SUBDIRS = activity bundle graphics presence datastore dispatch
SUBDIRS = event-controller activity bundle graphics presence datastore dispatch
sugardir = $(pythondir)/sugar3
sugar_PYTHON = \

@ -0,0 +1,51 @@
lib_LTLIBRARIES = libsugar-eventcontroller.la
libsugar_eventcontroller_la_LDFLAGS = $(LDADD)
libsugar_eventcontroller_la_LIBADD = $(EXT_LIBS)
libsugar_eventcontrollerincludedir = $(includedir)/sugar-3.0/event-controller/
libsugar_eventcontroller_la_CFLAGS = \
$(EXT_CFLAGS) \
$(WARN_FLAGS) \
-DSUGAR_TOOLKIT_COMPILATION
eventcontroller_h_sources = \
sugar-event-controller.h \
sugar-event-controllers.h \
sugar-long-press-controller.h \
sugar-rotate-controller.h \
sugar-swipe-controller.h \
sugar-zoom-controller.h
eventcontroller_c_sources = \
sugar-event-controller.c \
sugar-long-press-controller.c \
sugar-rotate-controller.c \
sugar-swipe-controller.c \
sugar-zoom-controller.c
libsugar_eventcontroller_la_SOURCES = \
$(BUILT_SOURCES) \
$(eventcontroller_h_sources) \
$(eventcontroller_c_sources)
libsugar_eventcontrollerinclude_HEADERS = \
$(eventcontroller_h_sources)
BUILT_SOURCES = \
sugar-enum-types.c \
sugar-enum-types.h
sugar-enum-types.h: sugar-enum-types.h.template $(eventcontroller_h_sources)
$(AM_V_GEN) (cd $(srcdir) && $(GLIB_MKENUMS) --template sugar-enum-types.h.template $(eventcontroller_h_sources)) > $@
sugar-enum-types.c: sugar-enum-types.c.template $(eventcontroller_h_sources)
$(AM_V_GEN) (cd $(srcdir) && $(GLIB_MKENUMS) --template sugar-enum-types.c.template $(eventcontroller_h_sources)) > $@
EXTRA_DIST = \
sugar-enum-types.c.template \
sugar-enum-types.h.template
CLEANFILES = $(BUILT_SOURCES)

@ -0,0 +1,35 @@
/*** BEGIN file-header ***/
#include "sugar-event-controllers.h"
/*** END file-header ***/
/*** BEGIN file-production ***/
/* enumerations from "@filename@" */
/*** END file-production ***/
/*** BEGIN value-header ***/
GType
@enum_name@_get_type (void)
{
static GType etype = 0;
if (G_UNLIKELY(etype == 0)) {
static const G@Type@Value values[] = {
/*** END value-header ***/
/*** BEGIN value-production ***/
{ @VALUENAME@, "@VALUENAME@", "@valuenick@" },
/*** END value-production ***/
/*** BEGIN value-tail ***/
{ 0, NULL, NULL }
};
etype = g_@type@_register_static (g_intern_static_string ("@EnumName@"), values);
}
return etype;
}
/*** END value-tail ***/
/*** BEGIN file-tail ***/
/*** END file-tail ***/

@ -0,0 +1,28 @@
/*** BEGIN file-header ***/
#if !defined (__SUGAR_CONTROLLERS_H_INSIDE__) && !defined (SUGAR_TOOLKIT_COMPILATION)
#error "Only <sugar/event-controller/sugar-event-controllers.h> can be included directly."
#endif
#ifndef __SUGAR_TYPE_BUILTINS_H__
#define __SUGAR_TYPE_BUILTINS_H__
#include <glib-object.h>
G_BEGIN_DECLS
/*** END file-header ***/
/*** BEGIN file-production ***/
/* enumerations from "@filename@" */
/*** END file-production ***/
/*** BEGIN value-header ***/
GType @enum_name@_get_type (void) G_GNUC_CONST;
#define @ENUMPREFIX@_TYPE_@ENUMSHORT@ (@enum_name@_get_type ())
/*** END value-header ***/
/*** BEGIN file-tail ***/
G_END_DECLS
#endif /* __SUGAR_TYPE_BUILTINS_H__ */
/*** END file-tail ***/

@ -0,0 +1,378 @@
/*
* 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 <carlos@lanedo.com>
*/
#include "sugar-event-controller.h"
#include "sugar-enum-types.h"
typedef struct _SugarControllerItem SugarControllerItem;
typedef struct _SugarControllerWidgetData SugarControllerWidgetData;
enum {
PROP_STATE = 1
};
enum {
STARTED,
FINISHED,
LAST_SIGNAL
};
struct _SugarControllerItem
{
SugarEventController *controller;
SugarEventControllerFlags flags;
guint notify_handler_id;
};
struct _SugarControllerWidgetData
{
GArray *controllers;
guint event_handler_id;
GtkWidget *widget;
SugarEventController *current_exclusive;
};
G_DEFINE_ABSTRACT_TYPE (SugarEventController, sugar_event_controller, G_TYPE_OBJECT)
static guint signals[LAST_SIGNAL] = { 0 };
static GQuark quark_widget_controller_data = 0;
static void
sugar_event_controller_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
switch (prop_id)
{
case PROP_STATE:
{
SugarEventControllerState state;
state = sugar_event_controller_get_state (SUGAR_EVENT_CONTROLLER (object));
g_value_set_enum (value, state);
break;
}
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
sugar_event_controller_class_init (SugarEventControllerClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->get_property = sugar_event_controller_get_property;
g_object_class_install_property (object_class,
PROP_STATE,
g_param_spec_enum ("state",
"State",
"Controller state",
SUGAR_TYPE_EVENT_CONTROLLER_STATE,
SUGAR_EVENT_CONTROLLER_STATE_NONE,
G_PARAM_READABLE |
G_PARAM_STATIC_NAME |
G_PARAM_STATIC_NICK |
G_PARAM_STATIC_BLURB));
signals[STARTED] =
g_signal_new ("started",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (SugarEventControllerClass, started),
NULL, NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
signals[FINISHED] =
g_signal_new ("finished",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (SugarEventControllerClass, finished),
NULL, NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
quark_widget_controller_data = g_quark_from_static_string ("sugar-widget-controller-data");
}
static void
sugar_event_controller_init (SugarEventController *controller)
{
}
static gboolean
_sugar_event_controller_widget_event (GtkWidget *widget,
GdkEvent *event,
gpointer user_data)
{
SugarControllerWidgetData *data;
gboolean handled = FALSE;
guint i;
data = g_object_get_qdata (G_OBJECT (widget),
quark_widget_controller_data);
if (!data || !data->controllers || data->controllers->len == 0)
return FALSE;
for (i = 0; i < data->controllers->len; i++)
{
SugarEventControllerState state;
SugarControllerItem *item;
item = &g_array_index (data->controllers, SugarControllerItem, i);
if (data->current_exclusive &&
data->current_exclusive != item->controller)
continue;
if (!sugar_event_controller_handle_event (item->controller, event))
continue;
state = sugar_event_controller_get_state (item->controller);
/* Consider events handled once the
* controller recognizes the action
*/
if (state == SUGAR_EVENT_CONTROLLER_STATE_RECOGNIZED)
handled = TRUE;
}
return handled;
}
gboolean
sugar_event_controller_handle_event (SugarEventController *controller,
GdkEvent *event)
{
SugarEventControllerClass *controller_class;
g_return_val_if_fail (SUGAR_IS_EVENT_CONTROLLER (controller), FALSE);
g_return_val_if_fail (event != NULL, FALSE);
controller_class = SUGAR_EVENT_CONTROLLER_GET_CLASS (controller);
if (!controller_class->handle_event)
return FALSE;
return controller_class->handle_event (controller, event);
}
static SugarControllerWidgetData *
_sugar_event_controller_widget_data_new (GtkWidget *widget)
{
SugarControllerWidgetData *data;
data = g_slice_new0 (SugarControllerWidgetData);
data->widget = widget;
data->controllers = g_array_new (FALSE, TRUE, sizeof (SugarControllerItem));
data->event_handler_id =
g_signal_connect (widget, "event",
G_CALLBACK (_sugar_event_controller_widget_event),
NULL);
return data;
}
static void
_sugar_event_controller_widget_data_free (SugarControllerWidgetData *data)
{
guint i;
if (g_signal_handler_is_connected (data->widget, data->event_handler_id))
g_signal_handler_disconnect (data->widget, data->event_handler_id);
for (i = 0; i < data->controllers->len; i++)
{
SugarControllerItem *item;
item = &g_array_index (data->controllers, SugarControllerItem, i);
g_signal_handler_disconnect (item->controller, item->notify_handler_id);
g_object_unref (item->controller);
}
g_array_unref (data->controllers);
g_slice_free (SugarControllerWidgetData, data);
}
static void
_sugar_event_controller_state_notify (SugarEventController *controller,
GParamSpec *pspec,
GtkWidget *widget)
{
SugarControllerWidgetData *data;
SugarControllerItem *item, *ptr;
SugarEventControllerState state;
guint i;
data = g_object_get_qdata (G_OBJECT (widget), quark_widget_controller_data);
state = sugar_event_controller_get_state (controller);
if (!data)
return;
if (state == SUGAR_EVENT_CONTROLLER_STATE_NONE &&
data->current_exclusive == controller)
data->current_exclusive = NULL;
else if (!data->current_exclusive &&
state == SUGAR_EVENT_CONTROLLER_STATE_RECOGNIZED)
{
for (i = 0; i < data->controllers->len; i++)
{
ptr = &g_array_index (data->controllers, SugarControllerItem, i);
if (ptr->controller == controller)
{
item = ptr;
break;
}
}
if (!item)
return;
if ((item->flags & SUGAR_EVENT_CONTROLLER_FLAG_EXCLUSIVE) != 0)
{
data->current_exclusive = controller;
/* Reset all other controllers */
for (i = 0; i < data->controllers->len; i++)
{
ptr = &g_array_index (data->controllers, SugarControllerItem, i);
if (ptr->controller != controller)
sugar_event_controller_reset (ptr->controller);
}
}
}
}
gboolean
sugar_event_controller_attach (SugarEventController *controller,
GtkWidget *widget,
SugarEventControllerFlags flags)
{
SugarControllerWidgetData *data;
SugarControllerItem *ptr, item;
guint i;
g_return_val_if_fail (SUGAR_IS_EVENT_CONTROLLER (controller), FALSE);
g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
data = g_object_get_qdata (G_OBJECT (widget), quark_widget_controller_data);
if (!data)
{
data = _sugar_event_controller_widget_data_new (widget);
g_object_set_qdata_full (G_OBJECT (widget),
quark_widget_controller_data, data,
(GDestroyNotify) _sugar_event_controller_widget_data_free);
}
for (i = 0; i < data->controllers->len; i++)
{
ptr = &g_array_index (data->controllers, SugarControllerItem, i);
if (ptr->controller == controller)
return FALSE;
}
item.controller = g_object_ref (controller);
item.flags = flags;
item.notify_handler_id = g_signal_connect (controller, "notify::state",
G_CALLBACK (_sugar_event_controller_state_notify),
widget);
g_array_append_val (data->controllers, item);
return TRUE;
}
gboolean
sugar_event_controller_detach (SugarEventController *controller,
GtkWidget *widget)
{
SugarControllerWidgetData *data;
SugarControllerItem *item;
gboolean removed = FALSE;
guint i;
g_return_val_if_fail (SUGAR_IS_EVENT_CONTROLLER (controller), FALSE);
g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
data = g_object_get_qdata (G_OBJECT (widget), quark_widget_controller_data);
if (!data)
return FALSE;
for (i = 0; i < data->controllers->len; i++)
{
item = &g_array_index (data->controllers, SugarControllerItem, i);
if (item->controller == controller)
{
sugar_event_controller_reset (item->controller);
g_object_unref (item->controller);
g_signal_handler_disconnect (item->controller,
item->notify_handler_id);
g_array_remove_index_fast (data->controllers, i);
removed = TRUE;
}
}
if (data->controllers->len == 0)
g_object_set_qdata (G_OBJECT (widget), quark_widget_controller_data, NULL);
return removed;
}
gboolean
sugar_event_controller_reset (SugarEventController *controller)
{
SugarEventControllerClass *controller_class;
g_return_val_if_fail (SUGAR_IS_EVENT_CONTROLLER (controller), FALSE);
controller_class = SUGAR_EVENT_CONTROLLER_GET_CLASS (controller);
if (!controller_class->reset)
return FALSE;
controller_class->reset (controller);
return sugar_event_controller_get_state (controller) ==
SUGAR_EVENT_CONTROLLER_STATE_NONE;
}
SugarEventControllerState
sugar_event_controller_get_state (SugarEventController *controller)
{
SugarEventControllerClass *controller_class;
g_return_val_if_fail (SUGAR_IS_EVENT_CONTROLLER (controller),
SUGAR_EVENT_CONTROLLER_STATE_NONE);
controller_class = SUGAR_EVENT_CONTROLLER_GET_CLASS (controller);
if (!controller_class->get_state)
return SUGAR_EVENT_CONTROLLER_STATE_NONE;
return controller_class->get_state (controller);
}

@ -0,0 +1,89 @@
/*
* 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 <carlos@lanedo.com>
*/
#if !defined (__SUGAR_CONTROLLERS_H_INSIDE__) && !defined (SUGAR_TOOLKIT_COMPILATION)
#error "Only <sugar/event-controller/sugar-event-controllers.h> can be included directly."
#endif
#ifndef __SUGAR_EVENT_CONTROLLER_H__
#define __SUGAR_EVENT_CONTROLLER_H__
#include <gtk/gtk.h>
G_BEGIN_DECLS
#define SUGAR_TYPE_EVENT_CONTROLLER (sugar_event_controller_get_type ())
#define SUGAR_EVENT_CONTROLLER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), SUGAR_TYPE_EVENT_CONTROLLER, SugarEventController))
#define SUGAR_EVENT_CONTROLLER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), SUGAR_TYPE_EVENT_CONTROLLER, SugarEventControllerClass))
#define SUGAR_IS_EVENT_CONTROLLER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), SUGAR_TYPE_EVENT_CONTROLLER))
#define SUGAR_IS_EVENT_CONTROLLER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), SUGAR_TYPE_EVENT_CONTROLLER))
#define SUGAR_EVENT_CONTROLLER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), SUGAR_TYPE_EVENT_CONTROLLER, SugarEventControllerClass))
typedef struct _SugarEventController SugarEventController;
typedef struct _SugarEventControllerClass SugarEventControllerClass;
typedef enum {
SUGAR_EVENT_CONTROLLER_STATE_NONE,
SUGAR_EVENT_CONTROLLER_STATE_COLLECTING,
SUGAR_EVENT_CONTROLLER_STATE_RECOGNIZED,
SUGAR_EVENT_CONTROLLER_STATE_NOT_RECOGNIZED
} SugarEventControllerState;
typedef enum {
SUGAR_EVENT_CONTROLLER_FLAG_NONE = 0,
SUGAR_EVENT_CONTROLLER_FLAG_EXCLUSIVE = 1 << 0
} SugarEventControllerFlags;
struct _SugarEventController
{
GObject parent_instance;
gpointer _priv;
};
struct _SugarEventControllerClass
{
GObjectClass parent_class;
/* Signals */
void (* started) (SugarEventController *controller);
void (* finished) (SugarEventController *controller);
/* vmethods */
gboolean (* handle_event) (SugarEventController *controller,
GdkEvent *event);
SugarEventControllerState (* get_state) (SugarEventController *controller);
void (* reset) (SugarEventController *controller);
};
GType sugar_event_controller_get_type (void) G_GNUC_CONST;
gboolean sugar_event_controller_handle_event (SugarEventController *controller,
GdkEvent *event);
gboolean sugar_event_controller_attach (SugarEventController *controller,
GtkWidget *widget,
SugarEventControllerFlags flags);
gboolean sugar_event_controller_reset (SugarEventController *controller);
SugarEventControllerState
sugar_event_controller_get_state (SugarEventController *controller);
G_END_DECLS
#endif /* __SUGAR_EVENT_CONTROLLER_H__ */

@ -0,0 +1,35 @@
/*
* 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 <carlos@lanedo.com>
*/
#ifndef __SUGAR_EVENT_CONTROLLERS_H__
#define __SUGAR_EVENT_CONTROLLERS_H__
#define __SUGAR_CONTROLLERS_H_INSIDE__
#include "sugar-event-controller.h"
#include "sugar-long-press-controller.h"
#include "sugar-rotate-controller.h"
#include "sugar-zoom-controller.h"
#include "sugar-swipe-controller.h"
#undef __SUGAR_CONTROLLERS_H_INSIDE__
#endif /* __SUGAR_EVENT_CONTROLLERS_H__ */

@ -0,0 +1,306 @@
/*
* 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 <carlos@lanedo.com>
*/
#include "sugar-long-press-controller.h"
#define DEFAULT_THRESHOLD 32
#define DEFAULT_TIMEOUT 800
typedef struct _SugarLongPressControllerPriv SugarLongPressControllerPriv;
enum {
PROP_0,
PROP_THRESHOLD,
PROP_TIMEOUT
};
struct _SugarLongPressControllerPriv
{
GdkDevice *device;
GdkEventSequence *sequence;
gint x;
gint y;
guint timeout_id;
guint threshold;
guint timeout;
guint cancelled : 1;
guint triggered : 1;
};
G_DEFINE_TYPE (SugarLongPressController,
sugar_long_press_controller,
SUGAR_TYPE_EVENT_CONTROLLER)
static void
sugar_long_press_controller_init (SugarLongPressController *controller)
{
SugarLongPressControllerPriv *priv;
controller->_priv = priv = G_TYPE_INSTANCE_GET_PRIVATE (controller,
SUGAR_TYPE_LONG_PRESS_CONTROLLER,
SugarLongPressControllerPriv);
priv->threshold = DEFAULT_THRESHOLD;
priv->timeout = DEFAULT_TIMEOUT;
priv->x = priv->y = -1;
}
static void
_sugar_long_press_controller_unset_device (SugarLongPressController *controller)
{
SugarLongPressControllerPriv *priv = controller->_priv;
if (priv->device)
{
g_object_unref (priv->device);
priv->device = NULL;
}
priv->sequence = NULL;
priv->x = priv->y = -1;
priv->cancelled = priv->triggered = FALSE;
}
static gboolean
_sugar_long_press_controller_cancel (SugarLongPressController *controller)
{
SugarLongPressControllerPriv *priv = controller->_priv;
if (priv->timeout_id)
{
g_source_remove (priv->timeout_id);
priv->timeout_id = 0;
priv->cancelled = TRUE;
g_object_notify (G_OBJECT (controller), "state");
return TRUE;
}
return FALSE;
}
static void
sugar_long_press_controller_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
SugarLongPressControllerPriv *priv = SUGAR_LONG_PRESS_CONTROLLER (object)->_priv;
switch (prop_id)
{
case PROP_THRESHOLD:
g_value_set_uint (value, priv->threshold);
break;
case PROP_TIMEOUT:
g_value_set_uint (value, priv->timeout);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
sugar_long_press_controller_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
SugarLongPressControllerPriv *priv = SUGAR_LONG_PRESS_CONTROLLER (object)->_priv;
switch (prop_id)
{
case PROP_THRESHOLD:
priv->threshold = g_value_get_uint (value);
break;
case PROP_TIMEOUT:
priv->timeout = g_value_get_uint (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
sugar_long_press_controller_finalize (GObject *object)
{
SugarLongPressController *controller = SUGAR_LONG_PRESS_CONTROLLER (object);
_sugar_long_press_controller_cancel (controller);
_sugar_long_press_controller_unset_device (controller);
G_OBJECT_CLASS (sugar_long_press_controller_parent_class)->finalize (object);
}
static gboolean
_sugar_long_press_controller_timeout (gpointer user_data)
{
SugarLongPressController *controller = user_data;
SugarLongPressControllerPriv *priv = controller->_priv;
priv->timeout_id = 0;
priv->triggered = TRUE;
g_signal_emit_by_name (controller, "started");
return FALSE;
}
static SugarEventControllerState
sugar_long_press_controller_get_state (SugarEventController *controller)
{
SugarLongPressControllerPriv *priv;
priv = SUGAR_LONG_PRESS_CONTROLLER (controller)->_priv;
if (priv->device)
{
if (priv->timeout_id)
return SUGAR_EVENT_CONTROLLER_STATE_COLLECTING;
else if (priv->cancelled)
return SUGAR_EVENT_CONTROLLER_STATE_NOT_RECOGNIZED;
else if (priv->triggered)
return SUGAR_EVENT_CONTROLLER_STATE_RECOGNIZED;
}
return SUGAR_EVENT_CONTROLLER_STATE_NONE;
}
static void
sugar_long_press_controller_reset (SugarEventController *controller)
{
SugarLongPressControllerPriv *priv;
priv = SUGAR_LONG_PRESS_CONTROLLER (controller)->_priv;
if (priv->triggered)
g_signal_emit_by_name (controller, "finished");
_sugar_long_press_controller_cancel (SUGAR_LONG_PRESS_CONTROLLER (controller));
_sugar_long_press_controller_unset_device (SUGAR_LONG_PRESS_CONTROLLER (controller));
g_object_notify (G_OBJECT (controller), "state");
}
static gboolean
sugar_long_press_controller_handle_event (SugarEventController *controller,
GdkEvent *event)
{
SugarLongPressControllerPriv *priv;
GdkEventSequence *sequence;
gboolean handled = TRUE;
GdkDevice *device;
priv = SUGAR_LONG_PRESS_CONTROLLER (controller)->_priv;
device = gdk_event_get_device (event);
sequence = gdk_event_get_event_sequence (event);
if (priv->device)
{
if (priv->device != device)
return FALSE;
if (sequence && priv->sequence != sequence)
{
/* Another touch is simultaneously operating,
* give up on recognizing a long press.
*/
_sugar_long_press_controller_cancel (SUGAR_LONG_PRESS_CONTROLLER (controller));
return FALSE;
}
}
switch (event->type)
{
case GDK_TOUCH_BEGIN:
priv->device = g_object_ref (device);
priv->x = event->touch.x;
priv->y = event->touch.y;
priv->sequence = sequence;
priv->timeout_id =
gdk_threads_add_timeout (priv->timeout,
_sugar_long_press_controller_timeout,
controller);
g_object_notify (G_OBJECT (controller), "state");
break;
case GDK_TOUCH_UPDATE:
if (ABS (priv->x - event->touch.x) > priv->threshold ||
ABS (priv->y - event->touch.y) > priv->threshold)
_sugar_long_press_controller_cancel (SUGAR_LONG_PRESS_CONTROLLER (controller));
break;
case GDK_TOUCH_END:
sugar_event_controller_reset (controller);
break;
default:
handled = FALSE;
break;
}
return handled;
}
static void
sugar_long_press_controller_class_init (SugarLongPressControllerClass *klass)
{
SugarEventControllerClass *controller_class;
GObjectClass *object_class;
object_class = G_OBJECT_CLASS (klass);
object_class->get_property = sugar_long_press_controller_get_property;
object_class->set_property = sugar_long_press_controller_set_property;
object_class->finalize = sugar_long_press_controller_finalize;
controller_class = SUGAR_EVENT_CONTROLLER_CLASS (klass);
controller_class->handle_event = sugar_long_press_controller_handle_event;
controller_class->get_state = sugar_long_press_controller_get_state;
controller_class->reset = sugar_long_press_controller_reset;
g_object_class_install_property (object_class,
PROP_THRESHOLD,
g_param_spec_uint ("threshold",
"Threshold",
"Threshold in pixels where the long "
"press operation remains valid",
0, G_MAXUINT, DEFAULT_THRESHOLD,
G_PARAM_READWRITE |
G_PARAM_STATIC_NAME |
G_PARAM_STATIC_NICK |
G_PARAM_STATIC_BLURB));
g_object_class_install_property (object_class,
PROP_TIMEOUT,
g_param_spec_uint ("timeout",
"Timeout",
"Value in milliseconds to timeout the triggering",
0, G_MAXUINT, DEFAULT_TIMEOUT,
G_PARAM_READWRITE |
G_PARAM_STATIC_NAME |
G_PARAM_STATIC_NICK |
G_PARAM_STATIC_BLURB));
g_type_class_add_private (klass, sizeof (SugarLongPressControllerPriv));
}
SugarEventController *
sugar_long_press_controller_new (void)
{
return g_object_new (SUGAR_TYPE_LONG_PRESS_CONTROLLER, NULL);
}

@ -0,0 +1,60 @@
/*
* 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 <carlos@lanedo.com>
*/
#if !defined (__SUGAR_CONTROLLERS_H_INSIDE__) && !defined (SUGAR_TOOLKIT_COMPILATION)
#error "Only <sugar/event-controller/sugar-event-controllers.h> can be included directly."
#endif
#ifndef __SUGAR_LONG_PRESS_CONTROLLER_H__
#define __SUGAR_LONG_PRESS_CONTROLLER_H__
#include "sugar-event-controller.h"
#include <gtk/gtk.h>
G_BEGIN_DECLS
#define SUGAR_TYPE_LONG_PRESS_CONTROLLER (sugar_long_press_controller_get_type ())
#define SUGAR_LONG_PRESS_CONTROLLER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), SUGAR_TYPE_LONG_PRESS_CONTROLLER, SugarLongPressController))
#define SUGAR_LONG_PRESS_CONTROLLER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), SUGAR_TYPE_LONG_PRESS_CONTROLLER, SugarLongPressControllerClass))
#define SUGAR_IS_LONG_PRESS_CONTROLLER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), SUGAR_TYPE_LONG_PRESS_CONTROLLER))
#define SUGAR_IS_LONG_PRESS_CONTROLLER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), SUGAR_TYPE_LONG_PRESS_CONTROLLER))
#define SUGAR_LONG_PRESS_CONTROLLER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), SUGAR_TYPE_LONG_PRESS_CONTROLLER, SugarLongPressControllerClass))
typedef struct _SugarLongPressController SugarLongPressController;
typedef struct _SugarLongPressControllerClass SugarLongPressControllerClass;
struct _SugarLongPressController
{
SugarEventController parent_instance;
gpointer _priv;
};
struct _SugarLongPressControllerClass
{
SugarEventControllerClass parent_class;
};
GType sugar_long_press_controller_get_type (void) G_GNUC_CONST;
SugarEventController * sugar_long_press_controller_new (void);
G_END_DECLS
#endif /* __SUGAR_LONG_PRESS_CONTROLLER_H__ */

@ -0,0 +1,296 @@
/*
* 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 <carlos@lanedo.com>
*/
#include <math.h>
#include "sugar-rotate-controller.h"
typedef struct _SugarRotateControllerPriv SugarRotateControllerPriv;
typedef struct _SugarTouch SugarTouch;
enum {
ANGLE_CHANGED,
LAST_SIGNAL
};
struct _SugarTouch
{
GdkEventSequence *sequence;
gint x;
gint y;
guint set : 1;
};
struct _SugarRotateControllerPriv
{
GdkDevice *device;
SugarTouch touches[2];
gdouble initial_angle;
};
static guint signals[LAST_SIGNAL] = { 0 };
G_DEFINE_TYPE (SugarRotateController,
sugar_rotate_controller,
SUGAR_TYPE_EVENT_CONTROLLER)
static void
sugar_rotate_controller_init (SugarRotateController *controller)
{
controller->_priv = G_TYPE_INSTANCE_GET_PRIVATE (controller,
SUGAR_TYPE_ROTATE_CONTROLLER,
SugarRotateControllerPriv);
}
static void
sugar_rotate_controller_finalize (GObject *object)
{
G_OBJECT_CLASS (sugar_rotate_controller_parent_class)->finalize (object);
}
static SugarTouch *
_sugar_rotate_controller_find_touch (SugarRotateController *controller,
GdkEventSequence *sequence)
{
SugarRotateControllerPriv *priv;
gint unset = -1, i;
priv = controller->_priv;
for (i = 0; i < 2; i++)
{
if (priv->touches[i].sequence == sequence)
return &priv->touches[i];
else if (!priv->touches[i].set && unset < 0)
unset = i;
}
if (unset < 0)
return NULL;
priv->touches[unset].sequence = sequence;
priv->touches[unset].set = TRUE;
return &priv->touches[unset];
}
static gboolean
_sugar_rotate_controller_get_angle (SugarRotateController *controller,
gdouble *angle)
{
SugarRotateControllerPriv *priv;
gdouble dx, dy;
priv = controller->_priv;
if (!priv->touches[0].set || !priv->touches[1].set)
return FALSE;
dx = priv->touches[0].x - priv->touches[1].x;
dy = priv->touches[0].y - priv->touches[1].y;
*angle = atan2 (dx, dy);
/* Invert angle */
*angle = (2 * G_PI) - *angle;
/* And constraint it to 0°-360° */
*angle = fmod (*angle, 2 * G_PI);
return TRUE;
}
static gboolean
_sugar_rotate_controller_check_emit (SugarRotateController *controller)
{
SugarRotateControllerPriv *priv;
gdouble angle;
if (!_sugar_rotate_controller_get_angle (controller, &angle))
return FALSE;
priv = controller->_priv;
g_signal_emit (controller, signals[ANGLE_CHANGED], 0,
angle, angle - priv->initial_angle);
return TRUE;
}
static gboolean
sugar_rotate_controller_handle_event (SugarEventController *controller,
GdkEvent *event)
{
SugarRotateControllerPriv *priv;
GdkEventSequence *sequence;
gboolean handled = TRUE;
GdkDevice *device;
SugarTouch *touch;
priv = SUGAR_ROTATE_CONTROLLER (controller)->_priv;
device = gdk_event_get_device (event);
sequence = gdk_event_get_event_sequence (event);
if (priv->device && priv->device != device)
return FALSE;
touch = _sugar_rotate_controller_find_touch (SUGAR_ROTATE_CONTROLLER (controller),
sequence);
if (!touch)
return FALSE;
switch (event->type)
{
case GDK_TOUCH_BEGIN:
touch->x = event->touch.x;
touch->y = event->touch.y;
if (!priv->device)
priv->device = g_object_ref (device);
if (priv->touches[0].set && priv->touches[1].set)
{
_sugar_rotate_controller_get_angle (SUGAR_ROTATE_CONTROLLER (controller),
&priv->initial_angle);
g_signal_emit_by_name (G_OBJECT (controller), "started");
g_object_notify (G_OBJECT (controller), "state");
}
break;
case GDK_TOUCH_END:
touch->sequence = NULL;
touch->set = FALSE;
if (!priv->touches[0].set && !priv->touches[1].set)
{
g_object_unref (priv->device);
priv->device = NULL;
}
else if (priv->touches[0].set || priv->touches[1].set)
{
g_signal_emit_by_name (G_OBJECT (controller), "finished");
g_object_notify (G_OBJECT (controller), "state");
}
break;
case GDK_TOUCH_UPDATE:
touch->x = event->touch.x;
touch->y = event->touch.y;
_sugar_rotate_controller_check_emit (SUGAR_ROTATE_CONTROLLER (controller));
break;
default:
handled = FALSE;
break;
}
return handled;
}
SugarEventControllerState
sugar_rotate_controller_get_state (SugarEventController *controller)
{
SugarRotateControllerPriv *priv;
priv = SUGAR_ROTATE_CONTROLLER (controller)->_priv;
if (priv->device)
{
if (priv->touches[0].set && priv->touches[1].set)
return SUGAR_EVENT_CONTROLLER_STATE_RECOGNIZED;
else if (priv->touches[0].set || priv->touches[1].set)
return SUGAR_EVENT_CONTROLLER_STATE_COLLECTING;
}
return SUGAR_EVENT_CONTROLLER_STATE_NONE;
}
void
sugar_rotate_controller_reset (SugarEventController *controller)
{
SugarRotateControllerPriv *priv;
priv = SUGAR_ROTATE_CONTROLLER (controller)->_priv;
if (priv->touches[0].set && priv->touches[1].set)
g_signal_emit_by_name (G_OBJECT (controller), "finished");
priv->touches[0].sequence = NULL;
priv->touches[0].set = FALSE;
priv->touches[1].sequence = NULL;
priv->touches[1].set = FALSE;
if (priv->device)
{
g_object_unref (priv->device);
priv->device = NULL;
}
g_object_notify (G_OBJECT (controller), "state");
}
static void
sugar_rotate_controller_class_init (SugarRotateControllerClass *klass)
{
SugarEventControllerClass *controller_class;
GObjectClass *object_class;
object_class = G_OBJECT_CLASS (klass);
object_class->finalize = sugar_rotate_controller_finalize;
controller_class = SUGAR_EVENT_CONTROLLER_CLASS (klass);
controller_class->handle_event = sugar_rotate_controller_handle_event;
controller_class->get_state = sugar_rotate_controller_get_state;
controller_class->reset = sugar_rotate_controller_reset;
signals[ANGLE_CHANGED] =
g_signal_new ("angle-changed",
SUGAR_TYPE_ROTATE_CONTROLLER,
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (SugarRotateControllerClass, angle_changed),
NULL, NULL,
g_cclosure_marshal_generic,
G_TYPE_NONE, 2,
G_TYPE_DOUBLE, G_TYPE_DOUBLE);
g_type_class_add_private (klass, sizeof (SugarRotateControllerPriv));
}
SugarEventController *
sugar_rotate_controller_new (void)
{
return g_object_new (SUGAR_TYPE_ROTATE_CONTROLLER, NULL);
}
gboolean
sugar_rotate_controller_get_angle_delta (SugarRotateController *controller,
gdouble *delta)
{
SugarRotateControllerPriv *priv;
gdouble angle;
g_return_val_if_fail (SUGAR_IS_ROTATE_CONTROLLER (controller), FALSE);
if (!_sugar_rotate_controller_get_angle (controller, &angle))
return FALSE;
priv = controller->_priv;
if (delta)
*delta = angle - priv->initial_angle;
return TRUE;
}

@ -0,0 +1,68 @@
/*
* 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 <carlos@lanedo.com>
*/
#if !defined (__SUGAR_CONTROLLERS_H_INSIDE__) && !defined (SUGAR_TOOLKIT_COMPILATION)
#error "Only <sugar/event-controller/sugar-event-controllers.h> can be included directly."
#endif
#ifndef __SUGAR_ROTATE_CONTROLLER_H__
#define __SUGAR_ROTATE_CONTROLLER_H__
#include "sugar-event-controller.h"
#include <gtk/gtk.h>
G_BEGIN_DECLS
#define SUGAR_TYPE_ROTATE_CONTROLLER (sugar_rotate_controller_get_type ())
#define SUGAR_ROTATE_CONTROLLER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), SUGAR_TYPE_ROTATE_CONTROLLER, SugarRotateController))
#define SUGAR_ROTATE_CONTROLLER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), SUGAR_TYPE_ROTATE_CONTROLLER, SugarRotateControllerClass))
#define SUGAR_IS_ROTATE_CONTROLLER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), SUGAR_TYPE_ROTATE_CONTROLLER))
#define SUGAR_IS_ROTATE_CONTROLLER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), SUGAR_TYPE_ROTATE_CONTROLLER))
#define SUGAR_ROTATE_CONTROLLER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), SUGAR_TYPE_ROTATE_CONTROLLER, SugarRotateControllerClass))
typedef struct _SugarRotateController SugarRotateController;
typedef struct _SugarRotateControllerClass SugarRotateControllerClass;
struct _SugarRotateController
{
SugarEventController parent_instance;
gpointer _priv;
};
struct _SugarRotateControllerClass
{
SugarEventControllerClass parent_class;
void (* angle_changed) (SugarRotateController *controller,
gdouble angle,
gdouble delta);
};
GType sugar_rotate_controller_get_type (void) G_GNUC_CONST;
SugarEventController * sugar_rotate_controller_new (void);
gboolean sugar_rotate_controller_get_angle_delta (SugarRotateController *controller,
gdouble *delta);
G_END_DECLS
#endif /* __SUGAR_ROTATE_CONTROLLER_H__ */

@ -0,0 +1,322 @@
/*
* 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 <carlos@lanedo.com>
*/
#include "sugar-swipe-controller.h"
#include "sugar-enum-types.h"
#define CHECK_TIME 100
#define SWIPE_PX_THRESHOLD 80
#define PROPORTION_FACTOR_THRESHOLD 4
typedef struct _SugarSwipeControllerPriv SugarSwipeControllerPriv;
typedef struct _SugarEventData SugarEventData;
enum {
SWIPE_FINISHED,
LAST_SIGNAL
};
struct _SugarEventData
{
gint x;
gint y;
guint32 time;
};
struct _SugarSwipeControllerPriv
{
GdkDevice *device;
GdkEventSequence *sequence;
GArray *event_data;
guint swiped : 1;
};
static guint signals[LAST_SIGNAL] = { 0 };
G_DEFINE_TYPE (SugarSwipeController,
sugar_swipe_controller,
SUGAR_TYPE_EVENT_CONTROLLER)
static void
sugar_swipe_controller_init (SugarSwipeController *controller)
{
SugarSwipeControllerPriv *priv;
controller->_priv = priv = G_TYPE_INSTANCE_GET_PRIVATE (controller,
SUGAR_TYPE_SWIPE_CONTROLLER,
SugarSwipeControllerPriv);
priv->event_data = g_array_new (FALSE, FALSE, sizeof (SugarEventData));
}
static void
sugar_swipe_controller_finalize (GObject *object)
{
G_OBJECT_CLASS (sugar_swipe_controller_parent_class)->finalize (object);
}
static void
_sugar_swipe_controller_clear_events (SugarSwipeController *controller)
{
SugarSwipeControllerPriv *priv;
priv = controller->_priv;
if (priv->event_data &&
priv->event_data->len > 0)
g_array_remove_range (priv->event_data, 0, priv->event_data->len);
priv->swiped = FALSE;
}
static void
_sugar_swipe_controller_store_event (SugarSwipeController *controller,
GdkEvent *event)
{
SugarSwipeControllerPriv *priv;
SugarEventData data;
gdouble x, y;
guint32 time;
guint i;
priv = controller->_priv;
if (!gdk_event_get_coords (event, &x, &y))
return;
time = gdk_event_get_time (event);
/* Remove event data older than CHECK_TIME, won't
* be used for calculations.
*/
for (i = 0; i < priv->event_data->len; i++)
{
SugarEventData *ptr;
ptr = &g_array_index (priv->event_data, SugarEventData, i);
if (ptr->time > time - CHECK_TIME)
break;
}
if (i > 0)
g_array_remove_range (priv->event_data, 0, i);
/* And insert current event data */
data.x = x;
data.y = y;
data.time = time;
g_array_append_val (priv->event_data, data);
}
static gboolean
_sugar_swipe_controller_get_direction (SugarEventData *from,
SugarEventData *to,
SugarSwipeDirection *direction)
{
gdouble dx, dy;
if (!from || !to)
return FALSE;
dx = to->x - from->x;
dy = to->y - from->y;
if (ABS (dx) > SWIPE_PX_THRESHOLD &&
ABS (dx) > ABS (dy) * PROPORTION_FACTOR_THRESHOLD)
{
if (dx < 0)
*direction = SUGAR_SWIPE_DIRECTION_LEFT;
else
*direction = SUGAR_SWIPE_DIRECTION_RIGHT;
return TRUE;
}
else if (ABS (dy) > SWIPE_PX_THRESHOLD &&
ABS (dy) > ABS (dx) * PROPORTION_FACTOR_THRESHOLD)
{
if (dy < 0)
*direction = SUGAR_SWIPE_DIRECTION_UP;
else
*direction = SUGAR_SWIPE_DIRECTION_DOWN;
return TRUE;
}
return FALSE;
}
static void
_sugar_swipe_controller_check_emit (SugarSwipeController *controller)
{
SugarSwipeControllerPriv *priv;
SugarEventData *last, *check;
SugarSwipeDirection direction;
gint i;
priv = controller->_priv;
if (!priv->event_data || priv->event_data->len == 0)
return;
last = &g_array_index (priv->event_data, SugarEventData,
priv->event_data->len - 1);
for (i = priv->event_data->len - 1; i >= 0; i--)
{
check = &g_array_index (priv->event_data, SugarEventData, i);
if (check->time < last->time - CHECK_TIME)
break;
}
if (_sugar_swipe_controller_get_direction (check, last, &direction))
{
priv->swiped = TRUE;
g_signal_emit_by_name (G_OBJECT (controller), "started");
g_object_notify (G_OBJECT (controller), "state");
g_signal_emit (controller, signals[SWIPE_FINISHED], 0, direction);
g_signal_emit_by_name (G_OBJECT (controller), "finished");
}
}
static gboolean
sugar_swipe_controller_handle_event (SugarEventController *controller,
GdkEvent *event)
{
SugarSwipeControllerPriv *priv;
SugarSwipeController *swipe;
GdkEventSequence *sequence;
gboolean handled = TRUE;
GdkDevice *device;
device = gdk_event_get_device (event);
sequence = gdk_event_get_event_sequence (event);
if (!device || !sequence)
return FALSE;
swipe = SUGAR_SWIPE_CONTROLLER (controller);
priv = swipe->_priv;
if ((priv->device && priv->device != device) ||
(priv->sequence && priv->sequence != sequence))
return FALSE;
switch (event->type)
{
case GDK_TOUCH_BEGIN:
priv->device = g_object_ref (device);
priv->sequence = sequence;
_sugar_swipe_controller_clear_events (swipe);
_sugar_swipe_controller_store_event (swipe, event);
g_object_notify (G_OBJECT (controller), "state");
break;
case GDK_TOUCH_END:
if (priv->device)
g_object_unref (priv->device);
priv->device = NULL;
priv->sequence = NULL;
_sugar_swipe_controller_store_event (swipe, event);
_sugar_swipe_controller_check_emit (swipe);
_sugar_swipe_controller_clear_events (swipe);
g_object_notify (G_OBJECT (controller), "state");
break;
case GDK_TOUCH_UPDATE:
_sugar_swipe_controller_store_event (swipe, event);
break;
default:
handled = FALSE;
break;
}
return handled;
}
SugarEventControllerState
sugar_swipe_controller_get_state (SugarEventController *controller)
{
SugarSwipeControllerPriv *priv;
priv = SUGAR_SWIPE_CONTROLLER (controller)->_priv;
if (priv->device)
{
if (priv->swiped)
return SUGAR_EVENT_CONTROLLER_STATE_RECOGNIZED;
else if (priv->event_data->len > 0)
return SUGAR_EVENT_CONTROLLER_STATE_COLLECTING;
}
return SUGAR_EVENT_CONTROLLER_STATE_NONE;
}
void
sugar_swipe_controller_reset (SugarEventController *controller)
{
SugarSwipeControllerPriv *priv;
SugarSwipeController *swipe;
swipe = SUGAR_SWIPE_CONTROLLER (controller);
priv = swipe->_priv;
if (priv->device)
{
g_object_unref (priv->device);
priv->device = NULL;
}
_sugar_swipe_controller_clear_events (swipe);
g_object_notify (G_OBJECT (controller), "state");
}
static void
sugar_swipe_controller_class_init (SugarSwipeControllerClass *klass)
{
SugarEventControllerClass *controller_class;
GObjectClass *object_class;
object_class = G_OBJECT_CLASS (klass);
object_class->finalize = sugar_swipe_controller_finalize;
controller_class = SUGAR_EVENT_CONTROLLER_CLASS (klass);
controller_class->handle_event = sugar_swipe_controller_handle_event;
controller_class->get_state = sugar_swipe_controller_get_state;
controller_class->reset = sugar_swipe_controller_reset;
signals[SWIPE_FINISHED] =
g_signal_new ("swipe-finished",
SUGAR_TYPE_SWIPE_CONTROLLER,
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (SugarSwipeControllerClass, swipe_finished),
NULL, NULL,
g_cclosure_marshal_VOID__ENUM,
G_TYPE_NONE, 1,
SUGAR_TYPE_SWIPE_DIRECTION);
g_type_class_add_private (klass, sizeof (SugarSwipeControllerPriv));
}
SugarEventController *
sugar_swipe_controller_new (void)
{
return g_object_new (SUGAR_TYPE_SWIPE_CONTROLLER, NULL);
}

@ -0,0 +1,70 @@
/*
* 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 <carlos@lanedo.com>
*/
#if !defined (__SUGAR_CONTROLLERS_H_INSIDE__) && !defined (SUGAR_TOOLKIT_COMPILATION)
#error "Only <sugar/event-controller/sugar-event-controllers.h> can be included directly."
#endif
#ifndef __SUGAR_SWIPE_CONTROLLER_H__
#define __SUGAR_SWIPE_CONTROLLER_H__
#include "sugar-event-controller.h"
#include <gtk/gtk.h>
G_BEGIN_DECLS
#define SUGAR_TYPE_SWIPE_CONTROLLER (sugar_swipe_controller_get_type ())
#define SUGAR_SWIPE_CONTROLLER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), SUGAR_TYPE_SWIPE_CONTROLLER, SugarSwipeController))
#define SUGAR_SWIPE_CONTROLLER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), SUGAR_TYPE_SWIPE_CONTROLLER, SugarSwipeControllerClass))
#define SUGAR_IS_SWIPE_CONTROLLER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), SUGAR_TYPE_SWIPE_CONTROLLER))
#define SUGAR_IS_SWIPE_CONTROLLER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), SUGAR_TYPE_SWIPE_CONTROLLER))
#define SUGAR_SWIPE_CONTROLLER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), SUGAR_TYPE_SWIPE_CONTROLLER, SugarSwipeControllerClass))
typedef struct _SugarSwipeController SugarSwipeController;
typedef struct _SugarSwipeControllerClass SugarSwipeControllerClass;
typedef enum {
SUGAR_SWIPE_DIRECTION_LEFT,
SUGAR_SWIPE_DIRECTION_RIGHT,
SUGAR_SWIPE_DIRECTION_UP,
SUGAR_SWIPE_DIRECTION_DOWN
} SugarSwipeDirection;
struct _SugarSwipeController
{
SugarEventController parent_instance;
gpointer _priv;
};
struct _SugarSwipeControllerClass
{
SugarEventControllerClass parent_class;
void (* swipe_finished) (SugarSwipeController *controller,
SugarSwipeDirection direction);
};
GType sugar_swipe_controller_get_type (void) G_GNUC_CONST;
SugarEventController * sugar_swipe_controller_new (void);
G_END_DECLS
#endif /* __SUGAR_SWIPE_CONTROLLER_H__ */

@ -0,0 +1,292 @@
/*
* 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 <carlos@lanedo.com>
*/
#include <math.h>
#include "sugar-zoom-controller.h"
typedef struct _SugarZoomControllerPriv SugarZoomControllerPriv;
typedef struct _SugarTouch SugarTouch;
enum {
ZOOM_CHANGED,
LAST_SIGNAL
};
struct _SugarTouch
{
GdkEventSequence *sequence;
gint x;
gint y;
guint set : 1;
};
struct _SugarZoomControllerPriv
{
GdkDevice *device;
SugarTouch touches[2];
gdouble initial_distance;
};
static guint signals[LAST_SIGNAL] = { 0 };
G_DEFINE_TYPE (SugarZoomController,
sugar_zoom_controller,
SUGAR_TYPE_EVENT_CONTROLLER)
static void
sugar_zoom_controller_init (SugarZoomController *controller)
{
controller->_priv = G_TYPE_INSTANCE_GET_PRIVATE (controller,
SUGAR_TYPE_ZOOM_CONTROLLER,
SugarZoomControllerPriv);
}
static void
sugar_zoom_controller_finalize (GObject *object)
{
G_OBJECT_CLASS (sugar_zoom_controller_parent_class)->finalize (object);
}
static SugarTouch *
_sugar_zoom_controller_find_touch (SugarZoomController *controller,
GdkEventSequence *sequence)
{
SugarZoomControllerPriv *priv;
gint unset = -1, i;
priv = controller->_priv;
for (i = 0; i < 2; i++)
{
if (priv->touches[i].sequence == sequence)
return &priv->touches[i];
else if (!priv->touches[i].set && unset < 0)
unset = i;
}
if (unset < 0)
return NULL;
priv->touches[unset].sequence = sequence;
priv->touches[unset].set = TRUE;
return &priv->touches[unset];
}
static gboolean
_sugar_zoom_controller_get_distance (SugarZoomController *controller,
gdouble *distance)
{
SugarZoomControllerPriv *priv;
gdouble dx, dy;
priv = controller->_priv;
if (!priv->touches[0].set || !priv->touches[1].set)
return FALSE;
dx = priv->touches[0].x - priv->touches[1].x;
dy = priv->touches[0].y - priv->touches[1].y;
*distance = sqrt ((dx * dx) + (dy * dy));
return TRUE;
}
static gboolean
_sugar_zoom_controller_check_emit (SugarZoomController *controller)
{
SugarZoomControllerPriv *priv;
gdouble distance, zoom;
if (!_sugar_zoom_controller_get_distance (controller, &distance))
return FALSE;
priv = controller->_priv;
if (distance == 0 || priv->initial_distance == 0)
return FALSE;
zoom = distance / priv->initial_distance;
g_signal_emit (controller, signals[ZOOM_CHANGED], 0, zoom);
return TRUE;
}
static gboolean
sugar_zoom_controller_handle_event (SugarEventController *controller,
GdkEvent *event)
{
SugarZoomControllerPriv *priv;
GdkEventSequence *sequence;
gboolean handled = TRUE;
GdkDevice *device;
SugarTouch *touch;
priv = SUGAR_ZOOM_CONTROLLER (controller)->_priv;
device = gdk_event_get_device (event);
sequence = gdk_event_get_event_sequence (event);
if (priv->device && priv->device != device)
return FALSE;
touch = _sugar_zoom_controller_find_touch (SUGAR_ZOOM_CONTROLLER (controller),
sequence);
if (!touch)
return FALSE;
switch (event->type)
{
case GDK_TOUCH_BEGIN:
touch->x = event->touch.x;
touch->y = event->touch.y;
if (!priv->device)
priv->device = g_object_ref (device);
if (priv->touches[0].set && priv->touches[1].set)
{
_sugar_zoom_controller_get_distance (SUGAR_ZOOM_CONTROLLER (controller),
&priv->initial_distance);
g_signal_emit_by_name (G_OBJECT (controller), "started");
g_object_notify (G_OBJECT (controller), "state");
}
break;
case GDK_TOUCH_END:
touch->sequence = NULL;
touch->set = FALSE;
if (!priv->touches[0].set && !priv->touches[1].set)
{
g_object_unref (priv->device);
priv->device = NULL;
}
else if (!priv->touches[0].set || priv->touches[1].set)
{
g_signal_emit_by_name (G_OBJECT (controller), "finished");
g_object_notify (G_OBJECT (controller), "state");
}
break;
case GDK_TOUCH_UPDATE:
touch->x = event->touch.x;
touch->y = event->touch.y;
_sugar_zoom_controller_check_emit (SUGAR_ZOOM_CONTROLLER (controller));
break;
default:
handled = FALSE;
break;
}
return handled;
}
SugarEventControllerState
sugar_zoom_controller_get_state (SugarEventController *controller)
{
SugarZoomControllerPriv *priv;
priv = SUGAR_ZOOM_CONTROLLER (controller)->_priv;
if (priv->device)
{
if (priv->touches[0].set && priv->touches[1].set)
return SUGAR_EVENT_CONTROLLER_STATE_RECOGNIZED;
else if (priv->touches[0].set || priv->touches[1].set)
return SUGAR_EVENT_CONTROLLER_STATE_COLLECTING;
}
return SUGAR_EVENT_CONTROLLER_STATE_NONE;
}
void
sugar_zoom_controller_reset (SugarEventController *controller)
{
SugarZoomControllerPriv *priv;
priv = SUGAR_ZOOM_CONTROLLER (controller)->_priv;
if (priv->touches[0].set && priv->touches[1].set)
g_signal_emit_by_name (G_OBJECT (controller), "finished");
priv->touches[0].sequence = NULL;
priv->touches[0].set = FALSE;
priv->touches[1].sequence = NULL;
priv->touches[1].set = FALSE;
if (priv->device)
{
g_object_unref (priv->device);
priv->device = NULL;
}
g_object_notify (G_OBJECT (controller), "state");
}
static void
sugar_zoom_controller_class_init (SugarZoomControllerClass *klass)
{
SugarEventControllerClass *controller_class;
GObjectClass *object_class;
object_class = G_OBJECT_CLASS (klass);
object_class->finalize = sugar_zoom_controller_finalize;
controller_class = SUGAR_EVENT_CONTROLLER_CLASS (klass);
controller_class->handle_event = sugar_zoom_controller_handle_event;
controller_class->get_state = sugar_zoom_controller_get_state;
controller_class->reset = sugar_zoom_controller_reset;
signals[ZOOM_CHANGED] =
g_signal_new ("zoom-changed",
SUGAR_TYPE_ZOOM_CONTROLLER,
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (SugarZoomControllerClass, zoom_changed),
NULL, NULL,
g_cclosure_marshal_VOID__DOUBLE,
G_TYPE_NONE, 1,
G_TYPE_DOUBLE);
g_type_class_add_private (klass, sizeof (SugarZoomControllerPriv));
}
SugarEventController *
sugar_zoom_controller_new (void)
{
return g_object_new (SUGAR_TYPE_ZOOM_CONTROLLER, NULL);
}
gboolean
sugar_zoom_controller_get_zoom_delta (SugarZoomController *controller,
gdouble *delta)
{
SugarZoomControllerPriv *priv;
gdouble distance;
g_return_val_if_fail (SUGAR_IS_ZOOM_CONTROLLER (controller), FALSE);
if (!_sugar_zoom_controller_get_distance (controller, &distance))
return FALSE;
priv = controller->_priv;
if (delta)
*delta = distance / priv->initial_distance;
return TRUE;
}

@ -0,0 +1,65 @@
/*
* 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 <carlos@lanedo.com>
*/
#if !defined (__SUGAR_CONTROLLERS_H_INSIDE__) && !defined (SUGAR_TOOLKIT_COMPILATION)
#error "Only <sugar/event-controller/sugar-event-controllers.h> can be included directly."
#endif
#ifndef __SUGAR_ZOOM_CONTROLLER_H__
#define __SUGAR_ZOOM_CONTROLLER_H__
#include "sugar-event-controller.h"
#include <gtk/gtk.h>
G_BEGIN_DECLS
#define SUGAR_TYPE_ZOOM_CONTROLLER (sugar_zoom_controller_get_type ())
#define SUGAR_ZOOM_CONTROLLER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), SUGAR_TYPE_ZOOM_CONTROLLER, SugarZoomController))
#define SUGAR_ZOOM_CONTROLLER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), SUGAR_TYPE_ZOOM_CONTROLLER, SugarZoomControllerClass))
#define SUGAR_IS_ZOOM_CONTROLLER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), SUGAR_TYPE_ZOOM_CONTROLLER))
#define SUGAR_IS_ZOOM_CONTROLLER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), SUGAR_TYPE_ZOOM_CONTROLLER))
#define SUGAR_ZOOM_CONTROLLER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), SUGAR_TYPE_ZOOM_CONTROLLER, SugarZoomControllerClass))
typedef struct _SugarZoomController SugarZoomController;
typedef struct _SugarZoomControllerClass SugarZoomControllerClass;
struct _SugarZoomController
{
SugarEventController parent_instance;
gpointer _priv;
};
struct _SugarZoomControllerClass
{
SugarEventControllerClass parent_class;
void (* zoom_changed) (SugarZoomController *controller,
gdouble zoom);
};
GType sugar_zoom_controller_get_type (void) G_GNUC_CONST;
SugarEventController * sugar_zoom_controller_new (void);
gboolean sugar_zoom_controller_get_zoom_delta (SugarZoomController *controller,
gdouble *delta);
G_END_DECLS
#endif /* __SUGAR_ZOOM_CONTROLLER_H__ */
Loading…
Cancel
Save