[gtk+/multitouch: 12/27] gtk-demo: Add multitouch demo
- From: Carlos Garnacho <carlosg src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtk+/multitouch: 12/27] gtk-demo: Add multitouch demo
- Date: Mon, 5 Dec 2011 01:17:08 +0000 (UTC)
commit b927a55df6f2d9f0f91d683b6e98b2a618cc87b1
Author: Carlos Garnacho <carlosg gnome org>
Date: Fri Mar 11 21:26:36 2011 +0100
gtk-demo: Add multitouch demo
Pretty much ignores pointer events currently, so you're out
of luck to see anything happening at all if you don't have
a multitouch device...
demos/gtk-demo/Makefile.am | 1 +
demos/gtk-demo/multitouch.c | 514 +++++++++++++++++++++++++++++++++++++++++++
2 files changed, 515 insertions(+), 0 deletions(-)
---
diff --git a/demos/gtk-demo/Makefile.am b/demos/gtk-demo/Makefile.am
index 2dac9ec..0d9680e 100644
--- a/demos/gtk-demo/Makefile.am
+++ b/demos/gtk-demo/Makefile.am
@@ -28,6 +28,7 @@ demos = \
links.c \
list_store.c \
menus.c \
+ multitouch.c \
offscreen_window.c \
offscreen_window2.c \
overlay.c \
diff --git a/demos/gtk-demo/multitouch.c b/demos/gtk-demo/multitouch.c
new file mode 100644
index 0000000..79f1f7d
--- /dev/null
+++ b/demos/gtk-demo/multitouch.c
@@ -0,0 +1,514 @@
+/* Multitouch
+ *
+ * Demonstrates some general multitouch event handling,
+ * using GdkTouchCluster in order to get grouped motion
+ * events for the touches within a cluster.
+ */
+
+#include <math.h>
+#include <gtk/gtk.h>
+#include "demo-common.h"
+
+static GtkWidget *window = NULL;
+static GList *shapes = NULL;
+
+typedef struct {
+ GdkTouchCluster *cluster;
+ GdkRGBA color;
+
+ gdouble angle;
+ gdouble zoom;
+
+ gdouble center_x;
+ gdouble center_y;
+
+ gdouble x;
+ gdouble y;
+ gdouble width;
+ gdouble height;
+
+ gdouble base_zoom;
+ gdouble base_angle;
+ gdouble initial_distance;
+ gdouble initial_angle;
+
+ GdkPoint points[4];
+} ShapeInfo;
+
+static void
+calculate_rotated_point (gdouble angle,
+ gdouble zoom,
+ gdouble center_x,
+ gdouble center_y,
+ gdouble point_x,
+ gdouble point_y,
+ gdouble *ret_x,
+ gdouble *ret_y)
+{
+ gdouble distance, xd, yd, ang;
+
+ if (angle == 0)
+ {
+ *ret_x = point_x;
+ *ret_y = point_y;
+ return;
+ }
+
+ xd = center_x - point_x;
+ yd = center_y - point_y;
+
+ if (xd == 0 && yd == 0)
+ {
+ *ret_x = center_x;
+ *ret_y = center_y;
+ return;
+ }
+
+ distance = sqrt ((xd * xd) + (yd * yd));
+ distance *= zoom;
+
+ ang = atan2 (xd, yd);
+
+ /* Invert angle */
+ ang = (2 * G_PI) - ang;
+
+ /* Shift it 270Â */
+ ang += 3 * (G_PI / 2);
+
+ /* And constraint it to 0Â-360Â */
+ ang = fmod (ang, 2 * G_PI);
+ ang += angle;
+
+ *ret_x = center_x + (distance * cos (ang));
+ *ret_y = center_y + (distance * sin (ang));
+}
+
+static void
+shape_info_allocate_input_rect (ShapeInfo *info)
+{
+ gint width, height, i;
+
+ width = info->width;
+ height = info->height;
+
+ /* Top/left */
+ info->points[0].x = info->x - info->center_x;
+ info->points[0].y = info->y - info->center_y;
+
+ /* Top/right */
+ info->points[1].x = info->x - info->center_x + width;
+ info->points[1].y = info->y - info->center_y;
+
+ /* Bottom/right */
+ info->points[2].x = info->x - info->center_x + width;
+ info->points[2].y = info->y - info->center_y + height;
+
+ /* Bottom/left */
+ info->points[3].x = info->x - info->center_x;
+ info->points[3].y = info->y - info->center_y + height;
+
+ for (i = 0; i < 4; i++)
+ {
+ gdouble ret_x, ret_y;
+
+ calculate_rotated_point (info->angle,
+ info->zoom,
+ info->x,
+ info->y,
+ (gdouble) info->points[i].x,
+ (gdouble) info->points[i].y,
+ &ret_x,
+ &ret_y);
+
+ info->points[i].x = (gint) ret_x;
+ info->points[i].y = (gint) ret_y;
+ }
+}
+
+static void
+shape_info_bounding_rect (ShapeInfo *info,
+ GdkRectangle *rect)
+{
+ gint i, left, right, top, bottom;
+
+ left = top = G_MAXINT;
+ right = bottom = 0;
+
+ for (i = 0; i < 4; i++)
+ {
+ if (info->points[i].x < left)
+ left = info->points[i].x;
+
+ if (info->points[i].x > right)
+ right = info->points[i].x;
+
+ if (info->points[i].y < top)
+ top = info->points[i].y;
+
+ if (info->points[i].y > bottom)
+ bottom = info->points[i].y;
+ }
+
+ rect->x = left - 20;
+ rect->y = top - 20;
+ rect->width = right - left + 40;
+ rect->height = bottom - top + 40;
+}
+
+static gboolean
+shape_info_point_in (ShapeInfo *info,
+ gint x,
+ gint y)
+{
+ GdkPoint *left, *right, *top, *bottom;
+ gint i;
+
+ left = right = top = bottom = NULL;
+
+ for (i = 0; i < 4; i++)
+ {
+ GdkPoint *p = &info->points[i];
+
+ if (!left ||
+ p->x < left->x ||
+ (p->x == left->x && p->y > left->y))
+ left = p;
+
+ if (!right ||
+ p->x > right->x ||
+ (p->x == right->x && p->y < right->y))
+ right = p;
+ }
+
+ for (i = 0; i < 4; i++)
+ {
+ GdkPoint *p = &info->points[i];
+
+ if (p == left || p == right)
+ continue;
+
+ if (!top ||
+ p->y < top->y)
+ top = p;
+
+ if (!bottom ||
+ p->y > bottom->y)
+ bottom = p;
+ }
+
+ g_assert (left && right && top && bottom);
+
+ if (x < left->x ||
+ x > right->x ||
+ y < top->y ||
+ y > bottom->y)
+ return FALSE;
+
+ /* Check whether point is above the sides
+ * between leftmost and topmost, and
+ * topmost and rightmost corners.
+ */
+ if (x <= top->x)
+ {
+ if (left->y - ((left->y - top->y) * (((gdouble) x - left->x) / (top->x - left->x))) > y)
+ return FALSE;
+ }
+ else
+ {
+ if (top->y + ((right->y - top->y) * (((gdouble) x - top->x) / (right->x - top->x))) > y)
+ return FALSE;
+ }
+
+ /* Check whether point is below the sides
+ * between leftmost and bottom, and
+ * bottom and rightmost corners.
+ */
+ if (x <= bottom->x)
+ {
+ if (left->y + ((bottom->y - left->y) * (((gdouble) x - left->x) / (bottom->x - left->x))) < y)
+ return FALSE;
+ }
+ else
+ {
+ if (bottom->y - ((bottom->y - right->y) * (((gdouble) x - bottom->x) / (right->x - bottom->x))) < y)
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static ShapeInfo *
+shape_info_new (gdouble x,
+ gdouble y,
+ gdouble width,
+ gdouble height,
+ GdkRGBA *color)
+{
+ ShapeInfo *info;
+
+ info = g_slice_new0 (ShapeInfo);
+ info->cluster = NULL;
+ info->color = *color;
+
+ info->x = x;
+ info->y = y;
+ info->width = width;
+ info->height = height;
+
+ info->angle = 0;
+ info->zoom = 1;
+
+ info->base_zoom = 1;
+ info->base_angle = 0;
+ info->initial_distance = 0;
+ info->initial_angle = 0;
+
+ shape_info_allocate_input_rect (info);
+
+ shapes = g_list_prepend (shapes, info);
+
+ return info;
+}
+
+static void
+shape_info_free (ShapeInfo *info)
+{
+ g_slice_free (ShapeInfo, info);
+}
+
+static void
+shape_info_draw (cairo_t *cr,
+ ShapeInfo *info)
+{
+ cairo_save (cr);
+
+ cairo_translate (cr, info->points[0].x, info->points[0].y);
+ cairo_scale (cr, info->zoom, info->zoom);
+ cairo_rotate (cr, info->angle);
+
+ cairo_rectangle (cr, 0, 0, info->width, info->height);
+ gdk_cairo_set_source_rgba (cr, &info->color);
+ cairo_fill_preserve (cr);
+
+ cairo_set_line_width (cr, 6);
+ cairo_set_source_rgb (cr, 0, 0, 0);
+ cairo_stroke (cr);
+
+ cairo_restore (cr);
+}
+
+static gboolean
+draw_cb (GtkWidget *widget,
+ cairo_t *cr,
+ gpointer user_data)
+{
+ GList *l;
+
+ for (l = shapes; l; l = l->next)
+ shape_info_draw (cr, l->data);
+
+ return FALSE;
+}
+
+static gboolean
+button_press_cb (GtkWidget *widget,
+ GdkEvent *event,
+ gpointer user_data)
+{
+ ShapeInfo *shape = NULL;
+ guint touch_id;
+
+ if (gdk_event_get_touch_id (event, &touch_id))
+ {
+ GList *l;
+
+ for (l = shapes; l; l = l->next)
+ {
+ ShapeInfo *info = l->data;
+
+ if (shape_info_point_in (info,
+ (gint) event->button.x,
+ (gint) event->button.y))
+ shape = info;
+ }
+
+ if (!shape)
+ return FALSE;
+
+ if (!shape->cluster)
+ shape->cluster = gdk_window_create_touch_cluster (gtk_widget_get_window (widget));
+
+ /* Only change cluster device if there were no touches or no device */
+ if (!gdk_touch_cluster_get_device (shape->cluster) ||
+ !gdk_touch_cluster_get_touches (shape->cluster))
+ gdk_touch_cluster_set_device (shape->cluster,
+ gdk_event_get_source_device (event));
+
+ gdk_touch_cluster_add_touch (shape->cluster, touch_id);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+multitouch_cb (GtkWidget *widget,
+ GdkEvent *event,
+ gpointer user_data)
+{
+ ShapeInfo *info = NULL;
+ gboolean new_center = FALSE;
+ gboolean new_position = FALSE;
+ gdouble event_x, event_y;
+ cairo_region_t *region;
+ GdkRectangle rect;
+ GList *l;
+
+ for (l = shapes; l; l = l->next)
+ {
+ ShapeInfo *shape = l->data;
+
+ if (event->multitouch.group == shape->cluster)
+ {
+ info = shape;
+ break;
+ }
+ }
+
+ if (!info)
+ return FALSE;
+
+ shape_info_bounding_rect (info, &rect);
+ region = cairo_region_create_rectangle ((cairo_rectangle_int_t *) &rect);
+
+ if (event->multitouch.n_events == 1)
+ {
+ /* Update center if we just got to
+ * this situation from either way */
+ if (event->type == GDK_MULTITOUCH_ADDED ||
+ event->type == GDK_MULTITOUCH_REMOVED)
+ new_center = TRUE;
+
+ event_x = event->multitouch.events[0]->x;
+ event_y = event->multitouch.events[0]->y;
+ new_position = TRUE;
+ }
+ else if (event->multitouch.n_events == 2)
+ {
+ gdouble distance, angle;
+
+ gdk_events_get_center ((GdkEvent *) event->multitouch.events[0],
+ (GdkEvent *) event->multitouch.events[1],
+ &event_x, &event_y);
+
+ gdk_events_get_distance ((GdkEvent *) event->multitouch.events[0],
+ (GdkEvent *) event->multitouch.events[1],
+ &distance);
+
+ gdk_events_get_angle ((GdkEvent *) event->multitouch.events[0],
+ (GdkEvent *) event->multitouch.events[1],
+ &angle);
+
+ if (event->type == GDK_MULTITOUCH_ADDED)
+ {
+ /* Second touch was just added, update base zoom/angle */
+ info->base_zoom = info->zoom;
+ info->base_angle = info->angle;
+ info->initial_angle = angle;
+ info->initial_distance = distance;
+ new_center = TRUE;
+ }
+
+ info->zoom = MAX (info->base_zoom * (distance / info->initial_distance), 1.0);
+ info->angle = info->base_angle + (angle - info->initial_angle);
+ new_position = TRUE;
+ }
+
+ if (new_center)
+ {
+ gdouble origin_x, origin_y;
+
+ origin_x = info->x - info->center_x;
+ origin_y = info->y - info->center_y;
+
+ calculate_rotated_point (- info->angle,
+ 1 / info->zoom,
+ info->x - origin_x,
+ info->y - origin_y,
+ event_x - origin_x,
+ event_y - origin_y,
+ &info->center_x,
+ &info->center_y);
+ }
+
+ if (new_position)
+ {
+ info->x = event_x;
+ info->y = event_y;
+ }
+
+ shape_info_allocate_input_rect (info);
+
+ shape_info_bounding_rect (info, &rect);
+ cairo_region_union_rectangle (region, (cairo_rectangle_int_t *) &rect);
+ gdk_window_invalidate_region (gtk_widget_get_window (widget), region, FALSE);
+
+ return TRUE;
+}
+
+GtkWidget *
+do_multitouch (GtkWidget *do_widget)
+{
+ if (!window)
+ {
+ GdkRGBA color;
+
+ window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_title (GTK_WINDOW (window), "Multitouch demo");
+ gtk_window_set_default_size (GTK_WINDOW (window), 600, 600);
+
+ gtk_window_set_screen (GTK_WINDOW (window),
+ gtk_widget_get_screen (do_widget));
+
+ gtk_widget_add_events (window,
+ GDK_TOUCH_MASK |
+ GDK_POINTER_MOTION_MASK |
+ GDK_BUTTON_PRESS_MASK |
+ GDK_BUTTON_RELEASE_MASK);
+
+ gtk_widget_set_app_paintable (window, TRUE);
+
+ g_signal_connect (window, "draw",
+ G_CALLBACK (draw_cb), NULL);
+ g_signal_connect (window, "button-press-event",
+ G_CALLBACK (button_press_cb), NULL);
+ g_signal_connect (window, "multitouch-event",
+ G_CALLBACK (multitouch_cb), NULL);
+
+ gdk_rgba_parse (&color, "red");
+ color.alpha = 0.5;
+ shape_info_new (100, 50, 100, 140, &color);
+
+ gdk_rgba_parse (&color, "green");
+ color.alpha = 0.5;
+ shape_info_new (200, 100, 120, 90, &color);
+
+ gdk_rgba_parse (&color, "blue");
+ color.alpha = 0.5;
+ shape_info_new (150, 190, 140, 90, &color);
+ }
+
+ if (!gtk_widget_get_visible (window))
+ gtk_widget_show (window);
+ else
+ {
+ gtk_widget_destroy (window);
+ window = NULL;
+
+ g_list_foreach (shapes, (GFunc) shape_info_free, NULL);
+ g_list_free (shapes);
+ shapes = NULL;
+ }
+
+ return window;
+}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]