663 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			663 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /* Touch and Drawing Tablets
 | |
|  *
 | |
|  * Demonstrates advanced handling of event information from exotic
 | |
|  * input devices.
 | |
|  *
 | |
|  * On one hand, this snippet demonstrates management of drawing tablets,
 | |
|  * those contain additional information for the pointer other than
 | |
|  * X/Y coordinates. Tablet pads events are mapped to actions, which
 | |
|  * are both defined and interpreted by the application.
 | |
|  *
 | |
|  * 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 <glib/gi18n.h>
 | |
| #include <gtk/gtk.h>
 | |
| 
 | |
| typedef struct {
 | |
|   GdkDevice *last_source;
 | |
|   GdkDeviceTool *last_tool;
 | |
|   gdouble *axes;
 | |
|   GdkRGBA color;
 | |
|   gdouble x;
 | |
|   gdouble y;
 | |
| } AxesInfo;
 | |
| 
 | |
| typedef struct {
 | |
|   GHashTable *pointer_info; /* GdkDevice -> AxesInfo */
 | |
|   GHashTable *touch_info; /* GdkEventSequence -> AxesInfo */
 | |
| } EventData;
 | |
| 
 | |
| const gchar *colors[] = {
 | |
|   "black",
 | |
|   "orchid",
 | |
|   "fuchsia",
 | |
|   "indigo",
 | |
|   "thistle",
 | |
|   "sienna",
 | |
|   "azure",
 | |
|   "plum",
 | |
|   "lime",
 | |
|   "navy",
 | |
|   "maroon",
 | |
|   "burlywood"
 | |
| };
 | |
| 
 | |
| static GtkPadActionEntry pad_actions[] = {
 | |
|   { GTK_PAD_ACTION_BUTTON, 1, -1, N_("Nuclear strike"), "pad.nuke" },
 | |
|   { GTK_PAD_ACTION_BUTTON, 2, -1, N_("Release siberian methane reserves"), "pad.heat" },
 | |
|   { GTK_PAD_ACTION_BUTTON, 3, -1, N_("Release solar flare"), "pad.fry" },
 | |
|   { GTK_PAD_ACTION_BUTTON, 4, -1, N_("De-stabilize Oort cloud"), "pad.fall" },
 | |
|   { GTK_PAD_ACTION_BUTTON, 5, -1, N_("Ignite WR-104"), "pad.burst" },
 | |
|   { GTK_PAD_ACTION_BUTTON, 6, -1, N_("Lart whoever asks about this button"), "pad.lart" },
 | |
|   { GTK_PAD_ACTION_RING, -1, -1, N_("Earth axial tilt"), "pad.tilt" },
 | |
|   { GTK_PAD_ACTION_STRIP, -1, -1, N_("Extent of weak nuclear force"), "pad.dissolve" },
 | |
| };
 | |
| 
 | |
| static const gchar *pad_action_results[] = {
 | |
|   "☢",
 | |
|   "♨",
 | |
|   "☼",
 | |
|   "☄",
 | |
|   "⚡",
 | |
|   "💫",
 | |
|   "◑",
 | |
|   "⚛"
 | |
| };
 | |
| 
 | |
| static guint cur_color = 0;
 | |
| static guint pad_action_timeout_id = 0;
 | |
| 
 | |
| static AxesInfo *
 | |
| axes_info_new (void)
 | |
| {
 | |
|   AxesInfo *info;
 | |
| 
 | |
|   info = g_new0 (AxesInfo, 1);
 | |
|   gdk_rgba_parse (&info->color, colors[cur_color]);
 | |
| 
 | |
|   cur_color = (cur_color + 1) % G_N_ELEMENTS (colors);
 | |
| 
 | |
|   return info;
 | |
| }
 | |
| 
 | |
| static EventData *
 | |
| event_data_new (void)
 | |
| {
 | |
|   EventData *data;
 | |
| 
 | |
|   data = g_new0 (EventData, 1);
 | |
|   data->pointer_info = g_hash_table_new_full (NULL, NULL, NULL,
 | |
|                                               (GDestroyNotify) g_free);
 | |
|   data->touch_info = g_hash_table_new_full (NULL, NULL, NULL,
 | |
|                                             (GDestroyNotify) g_free);
 | |
| 
 | |
|   return data;
 | |
| }
 | |
| 
 | |
| static void
 | |
| event_data_free (EventData *data)
 | |
| {
 | |
|   g_hash_table_destroy (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;
 | |
|   GdkDeviceTool *tool;
 | |
|   gdouble x, y;
 | |
|   AxesInfo *info;
 | |
| 
 | |
|   device = gdk_event_get_device (event);
 | |
|   source_device = gdk_event_get_source_device (event);
 | |
|   sequence = gdk_event_get_event_sequence (event);
 | |
|   tool = gdk_event_get_device_tool (event);
 | |
| 
 | |
|   if (event->type == GDK_TOUCH_END ||
 | |
|       event->type == GDK_TOUCH_CANCEL)
 | |
|     {
 | |
|       g_hash_table_remove (data->touch_info, sequence);
 | |
|       return;
 | |
|     }
 | |
|   else if (event->type == GDK_LEAVE_NOTIFY)
 | |
|     {
 | |
|       g_hash_table_remove (data->pointer_info, device);
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|   if (!sequence)
 | |
|     {
 | |
|       info = g_hash_table_lookup (data->pointer_info, device);
 | |
| 
 | |
|       if (!info)
 | |
|         {
 | |
|           info = axes_info_new ();
 | |
|           g_hash_table_insert (data->pointer_info, device, 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)
 | |
|     info->last_source = source_device;
 | |
| 
 | |
|   if (info->last_tool != tool)
 | |
|     info->last_tool = tool;
 | |
| 
 | |
|   g_clear_pointer (&info->axes, g_free);
 | |
| 
 | |
|   if (event->type == GDK_TOUCH_BEGIN ||
 | |
|       event->type == GDK_TOUCH_UPDATE)
 | |
|     {
 | |
|       if (sequence && event->touch.emulating_pointer)
 | |
|         g_hash_table_remove (data->pointer_info, device);
 | |
|     }
 | |
|   if (event->type == GDK_MOTION_NOTIFY)
 | |
|     {
 | |
|       info->axes =
 | |
|       g_memdup (event->motion.axes,
 | |
|                 sizeof (gdouble) * gdk_device_get_n_axes (source_device));
 | |
|     }
 | |
|   else if (event->type == GDK_BUTTON_PRESS ||
 | |
|            event->type == GDK_BUTTON_RELEASE)
 | |
|     {
 | |
|       info->axes =
 | |
|       g_memdup (event->button.axes,
 | |
|                 sizeof (gdouble) * gdk_device_get_n_axes (source_device));
 | |
|     }
 | |
| 
 | |
|   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, distance, wheel, rotation, slider;
 | |
|   GdkAxisFlags axes = gdk_device_get_axes (info->last_source);
 | |
| 
 | |
|   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 (!info->axes)
 | |
|     {
 | |
|       cairo_restore (cr);
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|   if (axes & GDK_AXIS_FLAG_PRESSURE)
 | |
|     {
 | |
|       cairo_pattern_t *pattern;
 | |
| 
 | |
|       gdk_device_get_axis (info->last_source, info->axes, GDK_AXIS_PRESSURE,
 | |
|                            &pressure);
 | |
| 
 | |
|       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 & GDK_AXIS_FLAG_XTILT &&
 | |
|       axes & GDK_AXIS_FLAG_YTILT)
 | |
|     {
 | |
|       gdk_device_get_axis (info->last_source, info->axes, GDK_AXIS_XTILT,
 | |
|                            &tilt_x);
 | |
|       gdk_device_get_axis (info->last_source, info->axes, GDK_AXIS_YTILT,
 | |
|                            &tilt_y);
 | |
| 
 | |
|       render_arrow (cr, tilt_x * 100, tilt_y * 100, "Tilt");
 | |
|     }
 | |
| 
 | |
|   if (axes & GDK_AXIS_FLAG_DISTANCE)
 | |
|     {
 | |
|       double dashes[] = { 5.0, 5.0 };
 | |
|       cairo_text_extents_t extents;
 | |
| 
 | |
|       gdk_device_get_axis (info->last_source, info->axes, GDK_AXIS_DISTANCE,
 | |
|                            &distance);
 | |
| 
 | |
|       cairo_save (cr);
 | |
| 
 | |
|       cairo_move_to (cr, distance * 100, 0);
 | |
| 
 | |
|       cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
 | |
|       cairo_set_dash (cr, dashes, 2, 0.0);
 | |
|       cairo_arc (cr, 0, 0, distance * 100, 0, 2 * G_PI);
 | |
|       cairo_stroke (cr);
 | |
| 
 | |
|       cairo_move_to (cr, 0, -distance * 100);
 | |
|       cairo_text_extents (cr, "Distance", &extents);
 | |
|       cairo_rel_move_to (cr, -extents.width / 2, 0);
 | |
|       cairo_show_text (cr, "Distance");
 | |
| 
 | |
|       cairo_move_to (cr, 0, 0);
 | |
| 
 | |
|       cairo_restore (cr);
 | |
|     }
 | |
| 
 | |
|   if (axes & GDK_AXIS_FLAG_WHEEL)
 | |
|     {
 | |
|       gdk_device_get_axis (info->last_source, info->axes, GDK_AXIS_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);
 | |
|     }
 | |
| 
 | |
|   if (axes & GDK_AXIS_FLAG_ROTATION)
 | |
|     {
 | |
|       gdk_device_get_axis (info->last_source, info->axes, GDK_AXIS_ROTATION,
 | |
|                            &rotation);
 | |
|       rotation *= 2 * G_PI;
 | |
| 
 | |
|       cairo_save (cr);
 | |
|       cairo_rotate (cr, - G_PI / 2);
 | |
|       cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
 | |
|       cairo_set_line_width (cr, 5);
 | |
| 
 | |
|       cairo_new_sub_path (cr);
 | |
|       cairo_arc (cr, 0, 0, 100, 0, rotation);
 | |
|       cairo_stroke (cr);
 | |
|       cairo_restore (cr);
 | |
|     }
 | |
| 
 | |
|   if (axes & GDK_AXIS_FLAG_SLIDER)
 | |
|     {
 | |
|       cairo_pattern_t *pattern, *mask;
 | |
| 
 | |
|       gdk_device_get_axis (info->last_source, info->axes, GDK_AXIS_SLIDER,
 | |
|                            &slider);
 | |
| 
 | |
|       cairo_save (cr);
 | |
| 
 | |
|       cairo_move_to (cr, 0, -10);
 | |
|       cairo_rel_line_to (cr, 0, -50);
 | |
|       cairo_rel_line_to (cr, 10, 0);
 | |
|       cairo_rel_line_to (cr, -5, 50);
 | |
|       cairo_close_path (cr);
 | |
| 
 | |
|       cairo_clip_preserve (cr);
 | |
| 
 | |
|       pattern = cairo_pattern_create_linear (0, -10, 0, -60);
 | |
|       cairo_pattern_add_color_stop_rgb (pattern, 0, 0, 1, 0);
 | |
|       cairo_pattern_add_color_stop_rgb (pattern, 1, 1, 0, 0);
 | |
|       cairo_set_source (cr, pattern);
 | |
|       cairo_pattern_destroy (pattern);
 | |
| 
 | |
|       mask = cairo_pattern_create_linear (0, -10, 0, -60);
 | |
|       cairo_pattern_add_color_stop_rgba (mask, 0, 0, 0, 0, 1);
 | |
|       cairo_pattern_add_color_stop_rgba (mask, slider, 0, 0, 0, 1);
 | |
|       cairo_pattern_add_color_stop_rgba (mask, slider, 0, 0, 0, 0);
 | |
|       cairo_pattern_add_color_stop_rgba (mask, 1, 0, 0, 0, 0);
 | |
|       cairo_mask (cr, mask);
 | |
|       cairo_pattern_destroy (mask);
 | |
| 
 | |
|       cairo_set_source_rgb (cr, 0, 0, 0);
 | |
|       cairo_stroke (cr);
 | |
| 
 | |
|       cairo_restore (cr);
 | |
|     }
 | |
| 
 | |
|   cairo_restore (cr);
 | |
| }
 | |
| 
 | |
| static const gchar *
 | |
| tool_type_to_string (GdkDeviceToolType tool_type)
 | |
| {
 | |
|   switch (tool_type)
 | |
|     {
 | |
|     case GDK_DEVICE_TOOL_TYPE_PEN:
 | |
|       return "Pen";
 | |
|     case GDK_DEVICE_TOOL_TYPE_ERASER:
 | |
|       return "Eraser";
 | |
|     case GDK_DEVICE_TOOL_TYPE_BRUSH:
 | |
|       return "Brush";
 | |
|     case GDK_DEVICE_TOOL_TYPE_PENCIL:
 | |
|       return "Pencil";
 | |
|     case GDK_DEVICE_TOOL_TYPE_AIRBRUSH:
 | |
|       return "Airbrush";
 | |
|     case GDK_DEVICE_TOOL_TYPE_MOUSE:
 | |
|       return "Mouse";
 | |
|     case GDK_DEVICE_TOOL_TYPE_LENS:
 | |
|       return "Lens cursor";
 | |
|     case GDK_DEVICE_TOOL_TYPE_UNKNOWN:
 | |
|     default:
 | |
|       return "Unknown";
 | |
|     }
 | |
| }
 | |
| 
 | |
| 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));
 | |
| 
 | |
|   if (info->last_tool)
 | |
|     {
 | |
|       const gchar *tool_type;
 | |
|       guint64 serial;
 | |
| 
 | |
|       tool_type = tool_type_to_string (gdk_device_tool_get_tool_type (info->last_tool));
 | |
|       serial = gdk_device_tool_get_serial (info->last_tool);
 | |
|       g_string_append_printf (string, "\nTool: %s", tool_type);
 | |
| 
 | |
|       if (serial != 0)
 | |
|         g_string_append_printf (string, ", Serial: %lx", serial);
 | |
|     }
 | |
| 
 | |
|   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 *info;
 | |
|   GHashTableIter iter;
 | |
|   gpointer key, value;
 | |
|   gint y = 0;
 | |
| 
 | |
|   gtk_widget_get_allocation (widget, &allocation);
 | |
| 
 | |
|   /* Draw Abs info */
 | |
|   g_hash_table_iter_init (&iter, data->pointer_info);
 | |
| 
 | |
|   while (g_hash_table_iter_next (&iter, NULL, &value))
 | |
|     {
 | |
|       info = value;
 | |
|       draw_axes_info (cr, info, &allocation);
 | |
|     }
 | |
| 
 | |
|   g_hash_table_iter_init (&iter, data->touch_info);
 | |
| 
 | |
|   while (g_hash_table_iter_next (&iter, NULL, &value))
 | |
|     {
 | |
|       info = value;
 | |
|       draw_axes_info (cr, info, &allocation);
 | |
|     }
 | |
| 
 | |
|   /* Draw name, color legend and misc data */
 | |
|   g_hash_table_iter_init (&iter, data->pointer_info);
 | |
| 
 | |
|   while (g_hash_table_iter_next (&iter, NULL, &value))
 | |
|     {
 | |
|       info = value;
 | |
|       draw_device_info (widget, cr, NULL, &y, info);
 | |
|     }
 | |
| 
 | |
|   g_hash_table_iter_init (&iter, data->touch_info);
 | |
| 
 | |
|   while (g_hash_table_iter_next (&iter, &key, &value))
 | |
|     {
 | |
|       info = value;
 | |
|       draw_device_info (widget, cr, key, &y, info);
 | |
|     }
 | |
| 
 | |
|   return FALSE;
 | |
| }
 | |
| 
 | |
| static void
 | |
| update_label_text (GtkWidget   *label,
 | |
|                    const gchar *text)
 | |
| {
 | |
|   gchar *markup = NULL;
 | |
| 
 | |
|   if (text)
 | |
|     markup = g_strdup_printf ("<span font='48.0'>%s</span>", text);
 | |
|   gtk_label_set_markup (GTK_LABEL (label), markup);
 | |
|   g_free (markup);
 | |
| }
 | |
| 
 | |
| static gboolean
 | |
| reset_label_text_timeout_cb (gpointer user_data)
 | |
| {
 | |
|   GtkWidget *label = user_data;
 | |
| 
 | |
|   update_label_text (label, NULL);
 | |
|   pad_action_timeout_id = 0;
 | |
|   return G_SOURCE_REMOVE;
 | |
| }
 | |
| 
 | |
| static void
 | |
| update_label_and_timeout (GtkWidget   *label,
 | |
|                           const gchar *text)
 | |
| {
 | |
|   if (pad_action_timeout_id)
 | |
|     g_source_remove (pad_action_timeout_id);
 | |
| 
 | |
|   update_label_text (label, text);
 | |
|   pad_action_timeout_id = g_timeout_add (200, reset_label_text_timeout_cb, label);
 | |
| }
 | |
| 
 | |
| static void
 | |
| on_action_activate (GSimpleAction *action,
 | |
|                     GVariant      *parameter,
 | |
|                     gpointer       user_data)
 | |
| {
 | |
|   GtkWidget *label = user_data;
 | |
|   const gchar *result;
 | |
|   gchar *str;
 | |
| 
 | |
|   result = g_object_get_data (G_OBJECT (action), "action-result");
 | |
| 
 | |
|   if (!parameter)
 | |
|     update_label_and_timeout (label, result);
 | |
|   else
 | |
|     {
 | |
|       str = g_strdup_printf ("%s %.2f", result, g_variant_get_double (parameter));
 | |
|       update_label_and_timeout (label, str);
 | |
|       g_free (str);
 | |
|     }
 | |
| }
 | |
| 
 | |
| static void
 | |
| init_pad_controller (GtkWidget *window,
 | |
|                      GtkWidget *label)
 | |
| {
 | |
|   GtkPadController *pad_controller;
 | |
|   GSimpleActionGroup *action_group;
 | |
|   GSimpleAction *action;
 | |
|   gint i;
 | |
| 
 | |
|   action_group = g_simple_action_group_new ();
 | |
|   pad_controller = gtk_pad_controller_new (GTK_WINDOW (window),
 | |
|                                            G_ACTION_GROUP (action_group),
 | |
|                                            NULL);
 | |
| 
 | |
|   for (i = 0; i < G_N_ELEMENTS (pad_actions); i++)
 | |
|     {
 | |
|       if (pad_actions[i].type == GTK_PAD_ACTION_BUTTON)
 | |
|         {
 | |
|           action = g_simple_action_new (pad_actions[i].action_name, NULL);
 | |
|         }
 | |
|       else
 | |
|         {
 | |
|           action = g_simple_action_new_stateful (pad_actions[i].action_name,
 | |
|                                                  G_VARIANT_TYPE_DOUBLE, NULL);
 | |
|         }
 | |
| 
 | |
|       g_signal_connect (action, "activate",
 | |
|                         G_CALLBACK (on_action_activate), label);
 | |
|       g_object_set_data (G_OBJECT (action), "action-result",
 | |
|                          (gpointer) pad_action_results[i]);
 | |
|       g_action_map_add_action (G_ACTION_MAP (action_group), G_ACTION (action));
 | |
|       g_object_unref (action);
 | |
|     }
 | |
| 
 | |
|   gtk_pad_controller_set_action_entries (pad_controller, pad_actions,
 | |
|                                          G_N_ELEMENTS (pad_actions));
 | |
|   g_object_set_data_full (G_OBJECT (window), "pad-controller",
 | |
|                           pad_controller, g_object_unref);
 | |
| 
 | |
|   g_object_unref (action_group);
 | |
| }
 | |
| 
 | |
| GtkWidget *
 | |
| do_event_axes (GtkWidget *toplevel)
 | |
| {
 | |
|   static GtkWidget *window = NULL;
 | |
|   EventData *event_data;
 | |
|   GtkWidget *box, *label;
 | |
| 
 | |
|   if (!window)
 | |
|     {
 | |
|       window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
 | |
|       gtk_window_set_title (GTK_WINDOW (window), "Event Axes");
 | |
|       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_set_support_multidevice (box, TRUE);
 | |
|       gtk_widget_add_events (box,
 | |
| 			     GDK_POINTER_MOTION_MASK |
 | |
| 			     GDK_BUTTON_PRESS_MASK |
 | |
| 			     GDK_BUTTON_RELEASE_MASK |
 | |
| 			     GDK_SMOOTH_SCROLL_MASK |
 | |
| 			     GDK_ENTER_NOTIFY_MASK |
 | |
| 			     GDK_LEAVE_NOTIFY_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);
 | |
| 
 | |
|       label = gtk_label_new ("");
 | |
|       gtk_label_set_use_markup (GTK_LABEL (label), TRUE);
 | |
|       gtk_container_add (GTK_CONTAINER (box), label);
 | |
| 
 | |
|       init_pad_controller (window, label);
 | |
|     }
 | |
| 
 | |
|   if (!gtk_widget_get_visible (window))
 | |
|     gtk_widget_show_all (window);
 | |
|   else
 | |
|     gtk_widget_destroy (window);
 | |
| 
 | |
|   return window;
 | |
| }
 | 
