diff options
author | Carlos Garnacho <carlosg@gnome.org> | 2013-11-27 17:53:33 +0100 |
---|---|---|
committer | Carlos Garnacho <carlosg@gnome.org> | 2013-12-06 20:08:37 +0100 |
commit | bd2f87514198500115d8e9c7700c8ab7cbb769f1 (patch) | |
tree | 2ddf4fdca8ed74f953f444a83d4ad8ab64536731 /demos | |
parent | 8d3a91d6ba101d7d78e7a340a50c2cee4d524f6c (diff) | |
download | gtk+-bd2f87514198500115d8e9c7700c8ab7cbb769f1.tar.gz |
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
Diffstat (limited to 'demos')
-rw-r--r-- | demos/gtk-demo/Makefile.am | 1 | ||||
-rw-r--r-- | demos/gtk-demo/demo.gresource.xml | 1 | ||||
-rw-r--r-- | 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 b8fb5b6b86..49a9a70319 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 a7b1336c10..19c582bb81 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 0000000000..3a761f14a9 --- /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; +} |