[gtk+] demo: Add demo for advanced event information management



commit bd2f87514198500115d8e9c7700c8ab7cbb769f1
Author: Carlos Garnacho <carlosg gnome org>
Date:   Wed Nov 27 17:53:33 2013 +0100

    demo: Add demo for advanced event information management
    
    This demo condenses the essentials of advanced management of
    input events. Depending on the information available in input events,
    this demo will try to represent as much information as possible for
    those.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=719987

 demos/gtk-demo/Makefile.am        |    1 +
 demos/gtk-demo/demo.gresource.xml |    1 +
 demos/gtk-demo/event_axes.c       |  419 +++++++++++++++++++++++++++++++++++++
 3 files changed, 421 insertions(+), 0 deletions(-)
---
diff --git a/demos/gtk-demo/Makefile.am b/demos/gtk-demo/Makefile.am
index b8fb5b6..49a9a70 100644
--- a/demos/gtk-demo/Makefile.am
+++ b/demos/gtk-demo/Makefile.am
@@ -22,6 +22,7 @@ demos =                                               \
        editable_cells.c                        \
        entry_buffer.c                          \
        entry_completion.c                      \
+       event_axes.c                            \
        expander.c                              \
        hypertext.c                             \
        iconview.c                              \
diff --git a/demos/gtk-demo/demo.gresource.xml b/demos/gtk-demo/demo.gresource.xml
index a7b1336..19c582b 100644
--- a/demos/gtk-demo/demo.gresource.xml
+++ b/demos/gtk-demo/demo.gresource.xml
@@ -91,6 +91,7 @@
     <file>editable_cells.c</file>
     <file>entry_buffer.c</file>
     <file>entry_completion.c</file>
+    <file>event_axes.c</file>
     <file>expander.c</file>
     <file>flowbox.c</file>
     <file>hypertext.c</file>
diff --git a/demos/gtk-demo/event_axes.c b/demos/gtk-demo/event_axes.c
new file mode 100644
index 0000000..3a761f1
--- /dev/null
+++ b/demos/gtk-demo/event_axes.c
@@ -0,0 +1,419 @@
+/* Event axes
+ *
+ * Demonstrates advanced handling of event information from exotic
+ * input devices.
+ *
+ * On one hand, this snippet demonstrates management of input axes,
+ * those contain additional information for the pointer other than
+ * X/Y coordinates.
+ *
+ * Input axes are dependent on hardware devices, on linux/unix you
+ * can see the device axes through xinput list <device>. Each time
+ * a different hardware device is used to move the pointer, the
+ * master device will be updated to match the axes it provides,
+ * these changes can be tracked through GdkDevice::changed, or
+ * checking gdk_event_get_source_device().
+ *
+ * On the other hand, this demo handles basic multitouch events,
+ * each event coming from an specific touchpoint will contain a
+ * GdkEventSequence that's unique for its lifetime, so multiple
+ * touchpoints can be tracked.
+ */
+
+#include <gtk/gtk.h>
+
+typedef struct {
+  GdkDevice *last_source;
+  GHashTable *axes; /* axis label atom -> value */
+  GdkRGBA color;
+  gdouble x;
+  gdouble y;
+} AxesInfo;
+
+typedef struct {
+  AxesInfo *pointer_info;
+  GHashTable *touch_info; /* GdkEventSequence -> AxesInfo */
+} EventData;
+
+const gchar *colors[] = {
+  "black",
+  "orchid",
+  "fuchsia",
+  "indigo",
+  "thistle",
+  "sienna",
+  "azure",
+  "plum",
+  "lime",
+  "navy",
+  "maroon",
+  "burlywood"
+};
+
+static guint cur_color = 0;
+
+static AxesInfo *
+axes_info_new (void)
+{
+  AxesInfo *info;
+
+  info = g_new0 (AxesInfo, 1);
+  gdk_rgba_parse (&info->color, colors[cur_color]);
+  info->axes = g_hash_table_new_full (NULL, NULL, NULL,
+                                      (GDestroyNotify) g_free);
+
+  cur_color = (cur_color + 1) % G_N_ELEMENTS (colors);
+
+  return info;
+}
+
+static void
+axes_info_free (AxesInfo *info)
+{
+  g_hash_table_destroy (info->axes);
+  g_free (info);
+}
+
+static gboolean
+axes_info_lookup (AxesInfo    *info,
+                  const gchar *axis_label,
+                  gdouble     *value)
+{
+  gdouble *val;
+  GdkAtom atom;
+
+  atom = gdk_atom_intern (axis_label, FALSE);
+
+  if (atom == GDK_NONE)
+    return FALSE;
+
+  val = g_hash_table_lookup (info->axes, GDK_ATOM_TO_POINTER (atom));
+
+  if (!val)
+    return FALSE;
+
+  *value = *val;
+  return TRUE;
+}
+
+static EventData *
+event_data_new (void)
+{
+  EventData *data;
+
+  data = g_new0 (EventData, 1);
+  data->touch_info = g_hash_table_new_full (NULL, NULL, NULL,
+                                            (GDestroyNotify) axes_info_free);
+
+  return data;
+}
+
+static void
+event_data_free (EventData *data)
+{
+  axes_info_free (data->pointer_info);
+  g_hash_table_destroy (data->touch_info);
+  g_free (data);
+}
+
+static void
+update_axes_from_event (GdkEvent  *event,
+                        EventData *data)
+{
+  GdkDevice *device, *source_device;
+  GdkEventSequence *sequence;
+  gdouble x, y, value;
+  GList *l, *axes;
+  AxesInfo *info;
+
+  device = gdk_event_get_device (event);
+  source_device = gdk_event_get_source_device (event);
+  sequence = gdk_event_get_event_sequence (event);
+
+  if (event->type == GDK_TOUCH_END)
+    {
+      g_hash_table_remove (data->touch_info, sequence);
+      return;
+    }
+  else if (event->type == GDK_LEAVE_NOTIFY)
+    {
+      if (data->pointer_info)
+        axes_info_free (data->pointer_info);
+      data->pointer_info = NULL;
+      return;
+    }
+
+  if (!sequence)
+    {
+      if (!data->pointer_info)
+        data->pointer_info = axes_info_new ();
+      info = data->pointer_info;
+    }
+  else
+    {
+      info = g_hash_table_lookup (data->touch_info, sequence);
+
+      if (!info)
+        {
+          info = axes_info_new ();
+          g_hash_table_insert (data->touch_info, sequence, info);
+        }
+    }
+
+  if (info->last_source != source_device)
+    {
+      g_hash_table_remove_all (info->axes);
+      info->last_source = source_device;
+    }
+
+  if (event->type == GDK_TOUCH_BEGIN ||
+      event->type == GDK_TOUCH_UPDATE ||
+      event->type == GDK_MOTION_NOTIFY ||
+      event->type == GDK_BUTTON_PRESS ||
+      event->type == GDK_BUTTON_RELEASE)
+    {
+      axes = gdk_device_list_axes (device);
+
+      if (sequence && event->touch.emulating_pointer)
+        {
+          if (data->pointer_info)
+            axes_info_free (data->pointer_info);
+          data->pointer_info = NULL;
+        }
+
+      for (l = axes; l; l = l->next)
+        {
+          gdouble *ptr;
+
+          /* All those event types are compatible wrt axes position in the struct */
+          if (!gdk_device_get_axis_value (device, event->motion.axes,
+                                          l->data, &value))
+            continue;
+
+          ptr = g_new0 (gdouble, 1);
+          *ptr = value;
+          g_hash_table_insert (info->axes, GDK_ATOM_TO_POINTER (l->data), ptr);
+        }
+
+      g_list_free (axes);
+    }
+
+  if (gdk_event_get_coords (event, &x, &y))
+    {
+      info->x = x;
+      info->y = y;
+    }
+}
+
+static gboolean
+event_cb (GtkWidget *widget,
+          GdkEvent  *event,
+          gpointer   user_data)
+{
+  update_axes_from_event (event, user_data);
+  gtk_widget_queue_draw (widget);
+  return FALSE;
+}
+
+static void
+render_arrow (cairo_t     *cr,
+              gdouble      x_diff,
+              gdouble      y_diff,
+              const gchar *label)
+{
+  cairo_save (cr);
+
+  cairo_set_source_rgb (cr, 0, 0, 0);
+  cairo_new_path (cr);
+  cairo_move_to (cr, 0, 0);
+  cairo_line_to (cr, x_diff, y_diff);
+  cairo_stroke (cr);
+
+  cairo_move_to (cr, x_diff, y_diff);
+  cairo_show_text (cr, label);
+
+  cairo_restore (cr);
+}
+
+static void
+draw_axes_info (cairo_t       *cr,
+                AxesInfo      *info,
+                GtkAllocation *allocation)
+{
+  gdouble pressure, tilt_x, tilt_y, wheel;
+
+  cairo_save (cr);
+
+  cairo_set_line_width (cr, 1);
+  gdk_cairo_set_source_rgba (cr, &info->color);
+
+  cairo_move_to (cr, 0, info->y);
+  cairo_line_to (cr, allocation->width, info->y);
+  cairo_move_to (cr, info->x, 0);
+  cairo_line_to (cr, info->x, allocation->height);
+  cairo_stroke (cr);
+
+  cairo_translate (cr, info->x, info->y);
+
+  if (axes_info_lookup (info, "Abs Pressure", &pressure))
+    {
+      cairo_pattern_t *pattern;
+
+      pattern = cairo_pattern_create_radial (0, 0, 0, 0, 0, 100);
+      cairo_pattern_add_color_stop_rgba (pattern, pressure, 1, 0, 0, pressure);
+      cairo_pattern_add_color_stop_rgba (pattern, 1, 0, 0, 1, 0);
+
+      cairo_set_source (cr, pattern);
+
+      cairo_arc (cr, 0, 0, 100, 0, 2 * G_PI);
+      cairo_fill (cr);
+
+      cairo_pattern_destroy (pattern);
+    }
+
+  if (axes_info_lookup (info, "Abs Tilt X", &tilt_x) &&
+      axes_info_lookup (info, "Abs Tilt Y", &tilt_y))
+    render_arrow (cr, tilt_x * 100, tilt_y * 100, "Tilt");
+
+  if (axes_info_lookup (info, "Abs Wheel", &wheel))
+    {
+      cairo_save (cr);
+      cairo_set_line_width (cr, 10);
+      cairo_set_source_rgba (cr, 0, 0, 0, 0.5);
+
+      cairo_new_sub_path (cr);
+      cairo_arc (cr, 0, 0, 100, 0, wheel * 2 * G_PI);
+      cairo_stroke (cr);
+      cairo_restore (cr);
+    }
+
+  cairo_restore (cr);
+}
+
+static void
+draw_device_info (GtkWidget        *widget,
+                  cairo_t          *cr,
+                  GdkEventSequence *sequence,
+                  gint             *y,
+                  AxesInfo         *info)
+{
+  PangoLayout *layout;
+  GString *string;
+  gint height;
+
+  cairo_save (cr);
+
+  string = g_string_new (NULL);
+  g_string_append_printf (string, "Source: %s",
+                          gdk_device_get_name (info->last_source));
+
+  if (sequence)
+    g_string_append_printf (string, "\nSequence: %d",
+                            GPOINTER_TO_UINT (sequence));
+
+  cairo_move_to (cr, 10, *y);
+  layout = gtk_widget_create_pango_layout (widget, string->str);
+  pango_cairo_show_layout (cr, layout);
+  cairo_stroke (cr);
+
+  pango_layout_get_pixel_size (layout, NULL, &height);
+
+  gdk_cairo_set_source_rgba (cr, &info->color);
+  cairo_set_line_width (cr, 10);
+  cairo_move_to (cr, 0, *y);
+
+  *y = *y + height;
+  cairo_line_to (cr, 0, *y);
+  cairo_stroke (cr);
+
+  cairo_restore (cr);
+
+  g_object_unref (layout);
+  g_string_free (string, TRUE);
+}
+
+static gboolean
+draw_cb (GtkWidget *widget,
+         cairo_t   *cr,
+         gpointer   user_data)
+{
+  EventData *data = user_data;
+  GtkAllocation allocation;
+  AxesInfo *touch_info;
+  GHashTableIter iter;
+  gpointer key, value;
+  gint y = 0;
+
+  gtk_widget_get_allocation (widget, &allocation);
+
+  /* Draw Abs info */
+  if (data->pointer_info)
+    draw_axes_info (cr, data->pointer_info, &allocation);
+
+  g_hash_table_iter_init (&iter, data->touch_info);
+
+  while (g_hash_table_iter_next (&iter, NULL, &value))
+    {
+      touch_info = value;
+      draw_axes_info (cr, touch_info, &allocation);
+    }
+
+  /* Draw name, color legend and misc data */
+  if (data->pointer_info)
+    draw_device_info (widget, cr, NULL, &y, data->pointer_info);
+
+  g_hash_table_iter_init (&iter, data->touch_info);
+
+  while (g_hash_table_iter_next (&iter, &key, &value))
+    {
+      touch_info = value;
+      draw_device_info (widget, cr, key, &y, touch_info);
+    }
+
+  return FALSE;
+}
+
+GtkWidget *
+do_event_axes (GtkWidget *toplevel)
+{
+  static GtkWidget *window = NULL;
+  EventData *event_data;
+  GtkWidget *box;
+
+  if (!window)
+    {
+      window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+      gtk_window_set_default_size (GTK_WINDOW (window), 400, 400);
+
+      g_signal_connect (window, "destroy",
+                        G_CALLBACK (gtk_widget_destroyed), &window);
+
+      box = gtk_event_box_new ();
+      gtk_container_add (GTK_CONTAINER (window), box);
+      gtk_widget_add_events (box,
+                            GDK_POINTER_MOTION_MASK |
+                            GDK_BUTTON_PRESS_MASK |
+                            GDK_BUTTON_RELEASE_MASK |
+                            GDK_SMOOTH_SCROLL_MASK |
+                            GDK_TOUCH_MASK);
+
+      event_data = event_data_new ();
+      g_object_set_data_full (G_OBJECT (box), "gtk-demo-event-data",
+                              event_data, (GDestroyNotify) event_data_free);
+
+      g_signal_connect (box, "event",
+                        G_CALLBACK (event_cb), event_data);
+      g_signal_connect (box, "draw",
+                        G_CALLBACK (draw_cb), event_data);
+    }
+
+  if (!gtk_widget_get_visible (window))
+    gtk_widget_show_all (window);
+  else
+    {
+      gtk_widget_destroy (window);
+      window = NULL;
+    }
+
+  return window;
+}


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]