/* * Copyright 2010 Canonical, Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of either or both of the following licenses: * * 1) the GNU Lesser General Public License version 3, as published by the * Free Software Foundation; and/or * 2) the GNU Lesser General Public License version 2.1, as published by * the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR * PURPOSE. See the applicable version of the GNU Lesser General Public * License for more details. * * You should have received a copy of both the GNU Lesser General Public * License version 3 and version 2.1 along with this program. If not, see * * * Authors: * Cody Russell */ #include "idogesturemanager.h" #include #include typedef struct _IdoGestureRegistration IdoGestureRegistration; typedef struct _IdoGestureBinding IdoGestureBinding; struct _IdoGestureManagerPrivate { GHashTable *hash; }; struct _IdoGestureBinding { IdoGestureType type; gint touches; IdoGestureCallback start; IdoGestureCallback update; IdoGestureCallback end; }; struct _IdoGestureRegistration { GtkWindow *window; GList *bindings; GeisInstance instance; GIOChannel *iochannel; }; static void gesture_added (void *cookie, GeisGestureType gesture_type, GeisGestureId gesture_id, GeisSize attr_count, GeisGestureAttr *attrs); static void gesture_removed (void *cookie, GeisGestureType gesture_type, GeisGestureId gesture_id, GeisSize attr_count, GeisGestureAttr *attrs); static void gesture_start (void *cookie, GeisGestureType gesture_type, GeisGestureId gesture_id, GeisSize attr_count, GeisGestureAttr *attrs); static void gesture_update (void *cookie, GeisGestureType gesture_type, GeisGestureId gesture_id, GeisSize attr_count, GeisGestureAttr *attrs); static void gesture_finish (void *cookie, GeisGestureType gesture_type, GeisGestureId gesture_id, GeisSize attr_count, GeisGestureAttr *attrs); static IdoGestureManager *manager_singleton = NULL; static GeisGestureFuncs gesture_funcs = { gesture_added, gesture_removed, gesture_start, gesture_update, gesture_finish }; G_DEFINE_TYPE (IdoGestureManager, ido_gesture_manager, G_TYPE_OBJECT) #define IDO_GESTURE_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), IDO_TYPE_GESTURE_MANAGER, IdoGestureManagerPrivate)) static void ido_gesture_manager_dispose (GObject *object) { IdoGestureManagerPrivate *priv = IDO_GESTURE_MANAGER (object)->priv; if (priv->hash != NULL) { g_hash_table_unref (priv->hash); priv->hash = NULL; } } static void ido_gesture_manager_finalize (GObject *object) { } static GObject * ido_gesture_manager_constructor (GType type, guint n_params, GObjectConstructParam *params) { GObject *object; if (manager_singleton != NULL) { object = g_object_ref (manager_singleton); } else { object = G_OBJECT_CLASS (ido_gesture_manager_parent_class)->constructor (type, n_params, params); manager_singleton = IDO_GESTURE_MANAGER (object); g_object_add_weak_pointer (object, (gpointer) &manager_singleton); } return object; } static void ido_gesture_manager_class_init (IdoGestureManagerClass *class) { GObjectClass *gobject_class; gobject_class = G_OBJECT_CLASS (class); ido_gesture_manager_parent_class = g_type_class_peek_parent (class); g_type_class_add_private (gobject_class, sizeof (IdoGestureManagerPrivate)); gobject_class->constructor = ido_gesture_manager_constructor; gobject_class->dispose = ido_gesture_manager_dispose; gobject_class->finalize = ido_gesture_manager_finalize; } /* static void print_attr (GeisGestureAttr *attr) { return; g_print ("\tattr '%s'=", attr->name); switch (attr->type) { case GEIS_ATTR_TYPE_BOOLEAN: g_print ("%s\n", attr->boolean_val ? "true" : "false"); break; case GEIS_ATTR_TYPE_FLOAT: g_print ("%f\n", attr->float_val); break; case GEIS_ATTR_TYPE_INTEGER: g_print ("%d\n", (gint)attr->integer_val); break; case GEIS_ATTR_TYPE_STRING: g_print ("\"%s\"\n", attr->string_val); break; default: g_print ("\n"); break; } } */ static gint pinch_gesture_handle_properties (IdoEventGesturePinch *event, GeisSize attr_count, GeisGestureAttr *attrs) { gint i = 0; gint touches = 0; for (i = 0; i < attr_count; ++i) { if (g_strcmp0 (attrs[i].name, GEIS_GESTURE_ATTRIBUTE_TOUCHES) == 0 && attrs[i].type == GEIS_ATTR_TYPE_INTEGER) { touches = attrs[i].integer_val; } if (g_strcmp0 (attrs[i].name, GEIS_GESTURE_ATTRIBUTE_TIMESTAMP) == 0 && attrs[i].type == GEIS_ATTR_TYPE_INTEGER) { event->timestamp = attrs[i].integer_val; } else if (g_strcmp0 (attrs[i].name, GEIS_GESTURE_ATTRIBUTE_FOCUS_X) == 0 && attrs[i].type == GEIS_ATTR_TYPE_FLOAT) { event->focus_x = attrs[i].float_val; } else if (g_strcmp0 (attrs[i].name, GEIS_GESTURE_ATTRIBUTE_FOCUS_Y) == 0 && attrs[i].type == GEIS_ATTR_TYPE_FLOAT) { event->focus_y = attrs[i].float_val; } else if (g_strcmp0 (attrs[i].name, GEIS_GESTURE_ATTRIBUTE_RADIUS_DELTA) == 0 && attrs[i].type == GEIS_ATTR_TYPE_FLOAT) { event->radius_delta = attrs[i].float_val; } else if (g_strcmp0 (attrs[i].name, GEIS_GESTURE_ATTRIBUTE_RADIAL_VELOCITY) == 0 && attrs[i].type == GEIS_ATTR_TYPE_FLOAT) { event->radial_velocity = attrs[i].float_val; } else if (g_strcmp0 (attrs[i].name, GEIS_GESTURE_ATTRIBUTE_RADIUS) == 0 && attrs[i].type == GEIS_ATTR_TYPE_FLOAT) { event->radius = attrs[i].float_val; } } return touches; } static gint drag_gesture_handle_properties (IdoEventGestureDrag *event, GeisSize attr_count, GeisGestureAttr *attrs) { gint i; gint touches = 0; for (i = 0; i < attr_count; ++i) { if (g_strcmp0 (attrs[i].name, GEIS_GESTURE_ATTRIBUTE_TOUCHES) == 0 && attrs[i].type == GEIS_ATTR_TYPE_INTEGER) { touches = attrs[i].integer_val; } if (g_strcmp0 (attrs[i].name, GEIS_GESTURE_ATTRIBUTE_TIMESTAMP) == 0 && attrs[i].type == GEIS_ATTR_TYPE_INTEGER) { event->timestamp = attrs[i].integer_val; } else if (g_strcmp0 (attrs[i].name, GEIS_GESTURE_ATTRIBUTE_FOCUS_X) == 0 && attrs[i].type == GEIS_ATTR_TYPE_FLOAT) { event->focus_x = attrs[i].float_val; } else if (g_strcmp0 (attrs[i].name, GEIS_GESTURE_ATTRIBUTE_FOCUS_Y) == 0 && attrs[i].type == GEIS_ATTR_TYPE_FLOAT) { event->focus_y = attrs[i].float_val; } else if (g_strcmp0 (attrs[i].name, GEIS_GESTURE_ATTRIBUTE_DELTA_X) == 0 && attrs[i].type == GEIS_ATTR_TYPE_FLOAT) { event->delta_x = attrs[i].float_val; } else if (g_strcmp0 (attrs[i].name, GEIS_GESTURE_ATTRIBUTE_DELTA_Y) == 0 && attrs[i].type == GEIS_ATTR_TYPE_FLOAT) { event->delta_y = attrs[i].float_val; } else if (g_strcmp0 (attrs[i].name, GEIS_GESTURE_ATTRIBUTE_VELOCITY_X) == 0 && attrs[i].type == GEIS_ATTR_TYPE_FLOAT) { event->velocity_x = attrs[i].float_val; } else if (g_strcmp0 (attrs[i].name, GEIS_GESTURE_ATTRIBUTE_VELOCITY_Y) == 0 && attrs[i].type == GEIS_ATTR_TYPE_FLOAT) { event->velocity_y = attrs[i].float_val; } else if (g_strcmp0 (attrs[i].name, GEIS_GESTURE_ATTRIBUTE_POSITION_X) == 0 && attrs[i].type == GEIS_ATTR_TYPE_FLOAT) { event->position_x = attrs[i].float_val; } else if (g_strcmp0 (attrs[i].name, GEIS_GESTURE_ATTRIBUTE_POSITION_Y) == 0 && attrs[i].type == GEIS_ATTR_TYPE_FLOAT) { event->position_y = attrs[i].float_val; } } return touches; } static gint rotate_gesture_handle_properties (IdoEventGestureRotate *event, GeisSize attr_count, GeisGestureAttr *attrs) { gint i; gint touches = 0; for (i = 0; i < attr_count; ++i) { if (g_strcmp0 (attrs[i].name, GEIS_GESTURE_ATTRIBUTE_TOUCHES) == 0 && attrs[i].type == GEIS_ATTR_TYPE_INTEGER) { touches = attrs[i].integer_val; } if (g_strcmp0 (attrs[i].name, GEIS_GESTURE_ATTRIBUTE_TIMESTAMP) == 0 && attrs[i].type == GEIS_ATTR_TYPE_INTEGER) { event->timestamp = attrs[i].integer_val; } else if (g_strcmp0 (attrs[i].name, GEIS_GESTURE_ATTRIBUTE_FOCUS_X) == 0 && attrs[i].type == GEIS_ATTR_TYPE_FLOAT) { event->focus_x = attrs[i].float_val; } else if (g_strcmp0 (attrs[i].name, GEIS_GESTURE_ATTRIBUTE_FOCUS_Y) == 0 && attrs[i].type == GEIS_ATTR_TYPE_FLOAT) { event->focus_y = attrs[i].float_val; } else if (g_strcmp0 (attrs[i].name, GEIS_GESTURE_ATTRIBUTE_ANGLE_DELTA) == 0 && attrs[i].type == GEIS_ATTR_TYPE_FLOAT) { event->angle_delta = attrs[i].float_val; } else if (g_strcmp0 (attrs[i].name, GEIS_GESTURE_ATTRIBUTE_ANGULAR_VELOCITY) == 0 && attrs[i].type == GEIS_ATTR_TYPE_FLOAT) { event->angular_velocity = attrs[i].float_val; } else if (g_strcmp0 (attrs[i].name, GEIS_GESTURE_ATTRIBUTE_ANGLE) == 0 && attrs[i].type == GEIS_ATTR_TYPE_FLOAT) { event->angle = attrs[i].float_val; } } return touches; } static void gesture_added (void *cookie, GeisGestureType gesture_type, GeisGestureId gesture_id, GeisSize attr_count, GeisGestureAttr *attrs) { } static void gesture_removed (void *cookie, GeisGestureType gesture_type, GeisGestureId gesture_id, GeisSize attr_count, GeisGestureAttr *attrs) { } static void gesture_start (void *cookie, GeisGestureType type, GeisGestureId id, GeisSize attr_count, GeisGestureAttr *attrs) { IdoGestureRegistration *reg = (IdoGestureRegistration *)cookie; GList *l = NULL; for (l = reg->bindings; l != NULL; l = l->next) { IdoGestureBinding *binding = (IdoGestureBinding *)l->data; if (binding->type == type) { if (type == IDO_GESTURE_DRAG) { IdoEventGestureDrag drag; drag.type = type; drag.id = id; drag.fingers = drag_gesture_handle_properties (&drag, attr_count, attrs); if (drag.fingers == binding->touches) { binding->start (reg->window, ((IdoGestureEvent*)&drag)); } } else if (type == IDO_GESTURE_PINCH) { IdoEventGesturePinch pinch; pinch.type = type; pinch.id = id; pinch.fingers = pinch_gesture_handle_properties (&pinch, attr_count, attrs); if (pinch.fingers == binding->touches) { binding->start (reg->window, ((IdoGestureEvent*)&pinch)); } } else if (type == IDO_GESTURE_ROTATE) { IdoEventGestureRotate rotate; rotate.type = type; rotate.id = id; rotate.fingers = rotate_gesture_handle_properties (&rotate, attr_count, attrs); if (rotate.fingers == binding->touches) { binding->start (reg->window, ((IdoGestureEvent*)&rotate)); } } return; } } } static void gesture_update (void *cookie, GeisGestureType type, GeisGestureId id, GeisSize attr_count, GeisGestureAttr *attrs) { IdoGestureRegistration *reg = (IdoGestureRegistration *)cookie; GList *l = NULL; for (l = reg->bindings; l != NULL; l = l->next) { IdoGestureBinding *binding = (IdoGestureBinding *)l->data; if (binding->type == type) { if (type == IDO_GESTURE_DRAG) { IdoEventGestureDrag drag; drag.type = type; drag.id = id; drag.fingers = drag_gesture_handle_properties (&drag, attr_count, attrs); if (drag.fingers == binding->touches) { binding->update (reg->window, ((IdoGestureEvent*)&drag)); } } else if (type == IDO_GESTURE_PINCH) { IdoEventGesturePinch pinch; pinch.type = type; pinch.id = id; pinch.fingers = pinch_gesture_handle_properties (&pinch, attr_count, attrs); if (pinch.fingers == binding->touches) { binding->update (reg->window, ((IdoGestureEvent*)&pinch)); } } else if (type == IDO_GESTURE_ROTATE) { IdoEventGestureRotate rotate; rotate.type = type; rotate.id = id; rotate.fingers = rotate_gesture_handle_properties (&rotate, attr_count, attrs); if (rotate.fingers == binding->touches) { binding->update (reg->window, ((IdoGestureEvent*)&rotate)); } } } } } static void gesture_finish (void *cookie, GeisGestureType type, GeisGestureId id, GeisSize attr_count, GeisGestureAttr *attrs) { IdoGestureRegistration *reg = (IdoGestureRegistration *)cookie; GList *l = NULL; for (l = reg->bindings; l != NULL; l = l->next) { IdoGestureBinding *binding = (IdoGestureBinding *)l->data; if (binding->type == type) { if (type == IDO_GESTURE_DRAG) { IdoEventGestureDrag drag; drag.type = type; drag.id = id; drag.fingers = drag_gesture_handle_properties (&drag, attr_count, attrs); if (drag.fingers == binding->touches) { binding->end (reg->window, ((IdoGestureEvent*)&drag)); } } else if (type == IDO_GESTURE_PINCH) { IdoEventGesturePinch pinch; pinch.type = type; pinch.id = id; pinch.fingers = pinch_gesture_handle_properties (&pinch, attr_count, attrs); if (pinch.fingers == binding->touches) { binding->end (reg->window, ((IdoGestureEvent*)&pinch)); } } else if (type == IDO_GESTURE_ROTATE) { IdoEventGestureRotate rotate; rotate.type = type; rotate.id = id; rotate.fingers = rotate_gesture_handle_properties (&rotate, attr_count, attrs); if (rotate.fingers == binding->touches) { binding->end (reg->window, ((IdoGestureEvent*)&rotate)); } } } } } static void ido_gesture_manager_init (IdoGestureManager *item) { IdoGestureManagerPrivate *priv; priv = item->priv = IDO_GESTURE_MANAGER_GET_PRIVATE (item); priv->hash = g_hash_table_new (g_direct_hash, g_direct_equal); } static gboolean io_callback (GIOChannel *source, GIOCondition condition, gpointer data) { IdoGestureRegistration *reg = (IdoGestureRegistration *)data; geis_event_dispatch (reg->instance); return TRUE; } static void window_destroyed_cb (GtkObject *object, gpointer user_data) { IdoGestureManager *manager = (IdoGestureManager *)user_data; IdoGestureManagerPrivate *priv = manager->priv; IdoGestureRegistration *reg = g_hash_table_lookup (priv->hash, object); GList *list; for (list = reg->bindings; list != NULL; list = list->next) { IdoGestureBinding *binding = (IdoGestureBinding *)list->data; g_free (binding); } g_list_free (reg->bindings); g_io_channel_shutdown (reg->iochannel, TRUE, NULL); geis_finish (reg->instance); g_hash_table_remove (priv->hash, object); g_free (reg); } /* Public API */ IdoGestureManager * ido_gesture_manager_get (void) { return g_object_new (IDO_TYPE_GESTURE_MANAGER, NULL); } /** * ido_gesture_manager_register_window: * @window: A #GtkWindow to register the gesture event for. * @gesture_type: The type of gesture event to register. * @touch_points: Number of touch points for this gesture. * @start: Called when a user initiates a gesture. * @update: Called each time the user updates the gesture. * @end: Called when the user ends the gesture. * * Registers a toplevel window to receive gesture events. * The callback parameters provided will be called by the * #IdoGestureManager whenever the user initiates a gesture * on the specified window. */ void ido_gesture_manager_register_window (IdoGestureManager *manager, GtkWindow *window, IdoGestureType gesture_type, gint touch_points, IdoGestureCallback start, IdoGestureCallback update, IdoGestureCallback end) { IdoGestureManagerPrivate *priv; IdoGestureRegistration *reg; IdoGestureBinding *binding; g_return_if_fail (IDO_IS_GESTURE_MANAGER (manager)); g_return_if_fail (GTK_IS_WINDOW (window)); g_return_if_fail (gtk_widget_get_realized (GTK_WIDGET (window))); priv = manager->priv; if (!(reg = g_hash_table_lookup (priv->hash, window))) { GeisInstance instance; GIOChannel *iochannel; gint fd = -1; GeisXcbWinInfo xcb_win_info = { .display_name = NULL, .screenp = NULL, .window_id = GDK_DRAWABLE_XID (GTK_WIDGET (window)->window) }; GeisWinInfo win_info = { GEIS_XCB_FULL_WINDOW, &xcb_win_info }; if (geis_init (&win_info, &instance) != GEIS_STATUS_SUCCESS) { g_warning ("Failed to initialize gesture manager."); return; } if (geis_configuration_supported (instance, GEIS_CONFIG_UNIX_FD) != GEIS_STATUS_SUCCESS) { g_warning ("Gesture manager does not support UNIX fd."); return; } if (geis_configuration_get_value (instance, GEIS_CONFIG_UNIX_FD, &fd) != GEIS_STATUS_SUCCESS) { g_error ("Gesture manager failed to obtain UNIX fd."); return; } reg = g_new0 (IdoGestureRegistration, 1); reg->window = window; reg->instance = instance; g_signal_connect (window, "destroy", G_CALLBACK (window_destroyed_cb), manager); geis_subscribe (reg->instance, GEIS_ALL_INPUT_DEVICES, GEIS_ALL_GESTURES, &gesture_funcs, reg); iochannel = g_io_channel_unix_new (fd); g_io_add_watch (iochannel, G_IO_IN, io_callback, reg); reg->iochannel = iochannel; } /* XXX - check for duplicates in reg->bindings first */ binding = g_new0 (IdoGestureBinding, 1); binding->type = gesture_type; binding->touches = touch_points; binding->start = start; binding->update = update; binding->end = end; reg->bindings = g_list_append (reg->bindings, binding); g_hash_table_insert (priv->hash, window, reg); }