gtkpadcontroller.c 16.9 KB
Newer Older
Carlos Garnacho's avatar
Carlos Garnacho committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/* GTK - The GIMP Toolkit
 * Copyright (C) 2016, Red Hat, Inc.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
 *
 * Author(s): Carlos Garnacho <carlosg@gnome.org>
 */

/**
Matthias Clasen's avatar
Matthias Clasen committed
21
 * GtkPadController:
Carlos Garnacho's avatar
Carlos Garnacho committed
22
 *
Matthias Clasen's avatar
Matthias Clasen committed
23
24
25
26
27
 * `GtkPadController` is an event controller for the pads found in drawing
 * tablets.
 *
 * Pads are the collection of buttons and tactile sensors often found around
 * the stylus-sensitive area.
Carlos Garnacho's avatar
Carlos Garnacho committed
28
29
 *
 * These buttons and sensors have no implicit meaning, and by default they
Matthias Clasen's avatar
Matthias Clasen committed
30
 * perform no action. `GtkPadController` is provided to map those to
Maximiliano's avatar
Maximiliano committed
31
 * [iface@Gio.Action] objects, thus letting the application give them a more
Matthias Clasen's avatar
Matthias Clasen committed
32
33
34
35
36
37
 * semantic meaning.
 *
 * Buttons and sensors are not constrained to triggering a single action,
 * some %GDK_SOURCE_TABLET_PAD devices feature multiple "modes". All these
 * input elements have one current mode, which may determine the final action
 * being triggered.
Carlos Garnacho's avatar
Carlos Garnacho committed
38
 *
Matthias Clasen's avatar
Matthias Clasen committed
39
40
41
42
 * Pad devices often divide buttons and sensors into groups. All elements
 * in a group share the same current mode, but different groups may have
 * different modes. See [method@Gdk.DevicePad.get_n_groups] and
 * [method@Gdk.DevicePad.get_group_n_modes].
Carlos Garnacho's avatar
Carlos Garnacho committed
43
 *
Maximiliano's avatar
Maximiliano committed
44
45
46
47
 * Each of the actions that a given button/strip/ring performs for a given mode
 * is defined by a [struct@Gtk.PadActionEntry]. It contains an action name that
 * will be looked up in the given [iface@Gio.ActionGroup] and activated whenever
 * the specified input element and mode are triggered.
Carlos Garnacho's avatar
Carlos Garnacho committed
48
 *
Matthias Clasen's avatar
Matthias Clasen committed
49
 * A simple example of `GtkPadController` usage: Assigning button 1 in all
Carlos Garnacho's avatar
Carlos Garnacho committed
50
51
 * modes and pad devices to an "invert-selection" action:
 *
Matthias Clasen's avatar
Matthias Clasen committed
52
53
54
 * ```c
 * GtkPadActionEntry *pad_actions[] = {
 *   { GTK_PAD_ACTION_BUTTON, 1, -1, "Invert selection", "pad-actions.invert-selection" },
Carlos Garnacho's avatar
Carlos Garnacho committed
55
 *   …
Matthias Clasen's avatar
Matthias Clasen committed
56
57
58
59
60
61
62
63
64
65
 * };
 *
 * …
 * action_group = g_simple_action_group_new ();
 * action = g_simple_action_new ("pad-actions.invert-selection", NULL);
 * g_signal_connect (action, "activate", on_invert_selection_activated, NULL);
 * g_action_map_add_action (G_ACTION_MAP (action_group), action);
 * …
 * pad_controller = gtk_pad_controller_new (action_group, NULL);
 * ```
66
67
68
 *
 * The actions belonging to rings/strips will be activated with a parameter
 * of type %G_VARIANT_TYPE_DOUBLE bearing the value of the given axis, it
Matthias Clasen's avatar
Matthias Clasen committed
69
 * is required that those are made stateful and accepting this `GVariantType`.
Carlos Garnacho's avatar
Carlos Garnacho committed
70
71
72
73
74
75
76
77
78
79
80
 */

#include "config.h"

#include "gtkeventcontrollerprivate.h"
#include "gtkpadcontroller.h"
#include "gtkwindow.h"
#include "gtkprivate.h"

#ifdef GDK_WINDOWING_WAYLAND
#include <gdk/wayland/gdkwayland.h>
81
#include "gdk/wayland/gdkdevice-wayland-private.h"
Carlos Garnacho's avatar
Carlos Garnacho committed
82
83
84
85
86
87
88
#endif

struct _GtkPadController {
  GtkEventController parent_instance;
  GActionGroup *action_group;
  GdkDevice *pad;

89
  GArray *action_entries;
Carlos Garnacho's avatar
Carlos Garnacho committed
90
91
92
93
94
95
96
97
98
99
100
101
102
};

struct _GtkPadControllerClass {
  GtkEventControllerClass parent_class;
};

enum {
  PROP_0,
  PROP_ACTION_GROUP,
  PROP_PAD,
  N_PROPS
};

103
typedef struct
Carlos Garnacho's avatar
Carlos Garnacho committed
104
{
105
106
107
108
109
110
  GtkPadActionType type;
  int index;
  int mode;
  char *label;
  char *action_name;
} ActionEntryData;
Carlos Garnacho's avatar
Carlos Garnacho committed
111

112
static GParamSpec *pspecs[N_PROPS] = { NULL };
Carlos Garnacho's avatar
Carlos Garnacho committed
113

114
G_DEFINE_TYPE (GtkPadController, gtk_pad_controller, GTK_TYPE_EVENT_CONTROLLER)
Carlos Garnacho's avatar
Carlos Garnacho committed
115

116
static const ActionEntryData *
Carlos Garnacho's avatar
Carlos Garnacho committed
117
118
gtk_pad_action_find_match (GtkPadController *controller,
                           GtkPadActionType  type,
Benjamin Otte's avatar
Benjamin Otte committed
119
120
                           int               index,
                           int               mode)
Carlos Garnacho's avatar
Carlos Garnacho committed
121
{
122
  guint i;
Carlos Garnacho's avatar
Carlos Garnacho committed
123

124
  for (i = 0; i < controller->action_entries->len; i++)
Carlos Garnacho's avatar
Carlos Garnacho committed
125
    {
126
127
      const ActionEntryData *entry = &g_array_index (controller->action_entries,
                                                     ActionEntryData, i);
Carlos Garnacho's avatar
Carlos Garnacho committed
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
      gboolean match_index = FALSE, match_mode = FALSE;

      if (entry->type != type)
        continue;

      match_index = entry->index < 0 || entry->index == index;
      match_mode = entry->mode < 0 || entry->mode == mode;

      if (match_index && match_mode)
        return entry;
    }

  return NULL;
}

static void
gtk_pad_controller_activate_action (GtkPadController        *controller,
145
                                    const ActionEntryData   *entry)
Carlos Garnacho's avatar
Carlos Garnacho committed
146
147
148
149
150
151
{
  g_action_group_activate_action (controller->action_group,
                                  entry->action_name,
                                  NULL);
}

152
153
static void
gtk_pad_controller_activate_action_with_axis (GtkPadController        *controller,
154
                                              const ActionEntryData   *entry,
155
                                              double                   value)
156
157
158
159
160
161
{
  g_action_group_activate_action (controller->action_group,
                                  entry->action_name,
                                  g_variant_new_double (value));
}

Carlos Garnacho's avatar
Carlos Garnacho committed
162
163
164
165
166
167
static void
gtk_pad_controller_handle_mode_switch (GtkPadController *controller,
                                       GdkDevice        *pad,
                                       guint             group,
                                       guint             mode)
{
168
169
170
#ifdef GDK_WINDOWING_WAYLAND
  if (GDK_IS_WAYLAND_DISPLAY (gdk_device_get_display (pad)))
    {
171
      const ActionEntryData *entry;
Benjamin Otte's avatar
Benjamin Otte committed
172
      int elem, idx, n_features;
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197

      for (elem = GTK_PAD_ACTION_BUTTON; elem <= GTK_PAD_ACTION_STRIP; elem++)
        {
          n_features = gdk_device_pad_get_n_features (GDK_DEVICE_PAD (pad),
                                                      elem);

          for (idx = 0; idx < n_features; idx++)
            {
              if (gdk_device_pad_get_feature_group (GDK_DEVICE_PAD (pad),
                                                    elem, idx) != group)
                continue;

              entry = gtk_pad_action_find_match (controller, elem, idx, mode);
              if (!entry)
                continue;
              if (!g_action_group_has_action (controller->action_group,
                                              entry->action_name))
                continue;

              gdk_wayland_device_pad_set_feedback (pad, elem, idx,
                                                   g_dgettext (NULL, entry->label));
            }
        }
    }
#endif
Carlos Garnacho's avatar
Carlos Garnacho committed
198
199
200
201
}

static gboolean
gtk_pad_controller_filter_event (GtkEventController *controller,
Matthias Clasen's avatar
Matthias Clasen committed
202
                                 GdkEvent           *event)
Carlos Garnacho's avatar
Carlos Garnacho committed
203
204
{
  GtkPadController *pad_controller = GTK_PAD_CONTROLLER (controller);
205
  GdkEventType event_type = gdk_event_get_event_type (event);
Carlos Garnacho's avatar
Carlos Garnacho committed
206

207
208
209
210
211
  if (event_type != GDK_PAD_BUTTON_PRESS &&
      event_type != GDK_PAD_BUTTON_RELEASE &&
      event_type != GDK_PAD_RING &&
      event_type != GDK_PAD_STRIP &&
      event_type != GDK_PAD_GROUP_MODE)
Carlos Garnacho's avatar
Carlos Garnacho committed
212
213
214
    return TRUE;

  if (pad_controller->pad &&
Carlos Garnacho's avatar
Carlos Garnacho committed
215
      gdk_event_get_device (event) != pad_controller->pad)
Carlos Garnacho's avatar
Carlos Garnacho committed
216
217
218
219
220
221
222
    return TRUE;

  return FALSE;
}

static gboolean
gtk_pad_controller_handle_event (GtkEventController *controller,
Matthias Clasen's avatar
Matthias Clasen committed
223
                                 GdkEvent           *event,
224
225
                                 double              x,
                                 double              y)
Carlos Garnacho's avatar
Carlos Garnacho committed
226
227
{
  GtkPadController *pad_controller = GTK_PAD_CONTROLLER (controller);
228
  GdkEventType event_type = gdk_event_get_event_type (event);
229
  const ActionEntryData *entry;
Carlos Garnacho's avatar
Carlos Garnacho committed
230
  GtkPadActionType type;
231
  guint index, mode, group;
232
  double value = 0;
Carlos Garnacho's avatar
Carlos Garnacho committed
233

Matthias Clasen's avatar
Matthias Clasen committed
234
  gdk_pad_event_get_group_mode (event, &group, &mode);
235
  if (event_type == GDK_PAD_GROUP_MODE)
Carlos Garnacho's avatar
Carlos Garnacho committed
236
237
    {
      gtk_pad_controller_handle_mode_switch (pad_controller,
Carlos Garnacho's avatar
Carlos Garnacho committed
238
                                             gdk_event_get_device (event),
239
240
                                             group,
                                             mode);
Carlos Garnacho's avatar
Carlos Garnacho committed
241
242
243
      return GDK_EVENT_PROPAGATE;
    }

244
  switch ((guint) event_type)
Carlos Garnacho's avatar
Carlos Garnacho committed
245
246
247
    {
    case GDK_PAD_BUTTON_PRESS:
      type = GTK_PAD_ACTION_BUTTON;
248
      index = gdk_pad_event_get_button (event);
Carlos Garnacho's avatar
Carlos Garnacho committed
249
250
251
      break;
    case GDK_PAD_RING:
    case GDK_PAD_STRIP:
252
      type = event_type == GDK_PAD_RING ?
Carlos Garnacho's avatar
Carlos Garnacho committed
253
        GTK_PAD_ACTION_RING : GTK_PAD_ACTION_STRIP;
254
      gdk_pad_event_get_axis_value (event, &index, &value);
Carlos Garnacho's avatar
Carlos Garnacho committed
255
256
257
258
259
260
261
262
263
264
      break;
    default:
      return GDK_EVENT_PROPAGATE;
    }

  entry = gtk_pad_action_find_match (pad_controller,
                                     type, index, mode);
  if (!entry)
    return GDK_EVENT_PROPAGATE;

265
266
  if (event_type == GDK_PAD_RING || event_type == GDK_PAD_STRIP)
    gtk_pad_controller_activate_action_with_axis (pad_controller, entry, value);
267
  else
268
    gtk_pad_controller_activate_action (pad_controller, entry);
Carlos Garnacho's avatar
Carlos Garnacho committed
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339

  return GDK_EVENT_STOP;
}

static void
gtk_pad_controller_set_pad (GtkPadController *controller,
                            GdkDevice        *pad)
{
  g_return_if_fail (!pad || GDK_IS_DEVICE (pad));
  g_return_if_fail (!pad || gdk_device_get_source (pad) == GDK_SOURCE_TABLET_PAD);

  g_set_object (&controller->pad, pad);
}

static void
gtk_pad_controller_set_property (GObject      *object,
                                 guint         prop_id,
                                 const GValue *value,
                                 GParamSpec   *pspec)
{
  GtkPadController *controller = GTK_PAD_CONTROLLER (object);

  switch (prop_id)
    {
    case PROP_ACTION_GROUP:
      controller->action_group = g_value_dup_object (value);
      break;
    case PROP_PAD:
      gtk_pad_controller_set_pad (controller, g_value_get_object (value));
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
    }
}

static void
gtk_pad_controller_get_property (GObject    *object,
                                 guint       prop_id,
                                 GValue     *value,
                                 GParamSpec *pspec)
{
  GtkPadController *controller = GTK_PAD_CONTROLLER (object);

  switch (prop_id)
    {
    case PROP_ACTION_GROUP:
      g_value_set_object (value, controller->action_group);
      break;
    case PROP_PAD:
      g_value_set_object (value, controller->pad);
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
    }
}

static void
gtk_pad_controller_dispose (GObject *object)
{
  GtkPadController *controller = GTK_PAD_CONTROLLER (object);

  g_clear_object (&controller->action_group);
  g_clear_object (&controller->pad);

  G_OBJECT_CLASS (gtk_pad_controller_parent_class)->dispose (object);
}

static void
gtk_pad_controller_finalize (GObject *object)
{
  GtkPadController *controller = GTK_PAD_CONTROLLER (object);
340
341
342
343
344
345
  guint i;

  for (i = 0; i < controller->action_entries->len; i++)
    {
      const ActionEntryData *entry = &g_array_index (controller->action_entries,
                                                     ActionEntryData, i);
Carlos Garnacho's avatar
Carlos Garnacho committed
346

347
348
349
350
      g_free (entry->label);
      g_free (entry->action_name);
    }
  g_array_free (controller->action_entries, TRUE);
Carlos Garnacho's avatar
Carlos Garnacho committed
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369

  G_OBJECT_CLASS (gtk_pad_controller_parent_class)->finalize (object);
}

static void
gtk_pad_controller_class_init (GtkPadControllerClass *klass)
{
  GObjectClass *object_class = G_OBJECT_CLASS (klass);
  GtkEventControllerClass *controller_class = GTK_EVENT_CONTROLLER_CLASS (klass);

  controller_class->filter_event = gtk_pad_controller_filter_event;
  controller_class->handle_event = gtk_pad_controller_handle_event;

  object_class->set_property = gtk_pad_controller_set_property;
  object_class->get_property = gtk_pad_controller_get_property;
  object_class->dispose = gtk_pad_controller_dispose;
  object_class->finalize = gtk_pad_controller_finalize;

  pspecs[PROP_ACTION_GROUP] =
370
    g_param_spec_object ("action-group", NULL, NULL,
Carlos Garnacho's avatar
Carlos Garnacho committed
371
372
373
                         G_TYPE_ACTION_GROUP,
                         GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
  pspecs[PROP_PAD] =
374
    g_param_spec_object ("pad", NULL, NULL,
Carlos Garnacho's avatar
Carlos Garnacho committed
375
376
377
378
379
380
381
382
383
                         GDK_TYPE_DEVICE,
                         GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);

  g_object_class_install_properties (object_class, N_PROPS, pspecs);
}

static void
gtk_pad_controller_init (GtkPadController *controller)
{
384
  controller->action_entries = g_array_new (FALSE, TRUE, sizeof (ActionEntryData));
Carlos Garnacho's avatar
Carlos Garnacho committed
385
386
387
388
}

/**
 * gtk_pad_controller_new:
Matthias Clasen's avatar
Matthias Clasen committed
389
 * @group: `GActionGroup` to trigger actions from
Carlos Garnacho's avatar
Carlos Garnacho committed
390
391
 * @pad: (nullable): A %GDK_SOURCE_TABLET_PAD device, or %NULL to handle all pads
 *
Matthias Clasen's avatar
Matthias Clasen committed
392
393
394
395
396
397
398
 * Creates a new `GtkPadController` that will associate events from @pad to
 * actions.
 *
 * A %NULL pad may be provided so the controller manages all pad devices
 * generically, it is discouraged to mix `GtkPadController` objects with
 * %NULL and non-%NULL @pad argument on the same toplevel window, as execution
 * order is not guaranteed.
Carlos Garnacho's avatar
Carlos Garnacho committed
399
 *
Matthias Clasen's avatar
Matthias Clasen committed
400
401
402
 * The `GtkPadController` is created with no mapped actions. In order to
 * map pad events to actions, use [method@Gtk.PadController.set_action_entries]
 * or [method@Gtk.PadController.set_action].
Carlos Garnacho's avatar
Carlos Garnacho committed
403
 *
Matthias Clasen's avatar
Matthias Clasen committed
404
405
 * Be aware that pad events will only be delivered to `GtkWindow`s, so adding
 * a pad controller to any other type of widget will not have an effect.
406
 *
Matthias Clasen's avatar
Matthias Clasen committed
407
408
 * Returns: A newly created `GtkPadController`
 */
Carlos Garnacho's avatar
Carlos Garnacho committed
409
GtkPadController *
410
gtk_pad_controller_new (GActionGroup *group,
Carlos Garnacho's avatar
Carlos Garnacho committed
411
412
413
414
415
416
417
418
419
420
421
422
423
                        GdkDevice    *pad)
{
  g_return_val_if_fail (G_IS_ACTION_GROUP (group), NULL);
  g_return_val_if_fail (!pad || GDK_IS_DEVICE (pad), NULL);
  g_return_val_if_fail (!pad || gdk_device_get_source (pad) == GDK_SOURCE_TABLET_PAD, NULL);

  return g_object_new (GTK_TYPE_PAD_CONTROLLER,
                       "propagation-phase", GTK_PHASE_CAPTURE,
                       "action-group", group,
                       "pad", pad,
                       NULL);
}

Benjamin Otte's avatar
Benjamin Otte committed
424
static int
Carlos Garnacho's avatar
Carlos Garnacho committed
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
entry_compare_func (gconstpointer a,
                    gconstpointer b)
{
  const GtkPadActionEntry *entry1 = a, *entry2 = b;

  if (entry1->mode > entry2->mode)
    return -1;
  else if (entry1->mode < entry2->mode)
    return 1;
  else if (entry1->index > entry2->index)
    return -1;
  else if (entry1->index < entry2->index)
    return 1;

  return 0;
}

static void
gtk_pad_controller_add_entry (GtkPadController        *controller,
                              const GtkPadActionEntry *entry)
{
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
  guint i;
  const ActionEntryData new_entry = {
   .type = entry->type,
   .index = entry->index,
   .mode = entry->mode,
   .label= g_strdup (entry->label),
   .action_name = g_strdup (entry->action_name)
  };

  for (i = 0; i < controller->action_entries->len; i++)
    {
      if (entry_compare_func (&new_entry,
                              &g_array_index (controller->action_entries, ActionEntryData, i)) == 0)
        break;
    }
Carlos Garnacho's avatar
Carlos Garnacho committed
461

462
  g_array_insert_val (controller->action_entries, i, new_entry);
Carlos Garnacho's avatar
Carlos Garnacho committed
463
464
465
466
}

/**
 * gtk_pad_controller_set_action_entries:
Matthias Clasen's avatar
Matthias Clasen committed
467
 * @controller: a `GtkPadController`
Carlos Garnacho's avatar
Carlos Garnacho committed
468
469
470
 * @entries: (array length=n_entries): the action entries to set on @controller
 * @n_entries: the number of elements in @entries
 *
Matthias Clasen's avatar
Matthias Clasen committed
471
472
473
474
475
 * A convenience function to add a group of action entries on
 * @controller.
 *
 * See [struct@Gtk.PadActionEntry] and [method@Gtk.PadController.set_action].
 */
Carlos Garnacho's avatar
Carlos Garnacho committed
476
477
478
void
gtk_pad_controller_set_action_entries (GtkPadController        *controller,
                                       const GtkPadActionEntry *entries,
Benjamin Otte's avatar
Benjamin Otte committed
479
                                       int                      n_entries)
Carlos Garnacho's avatar
Carlos Garnacho committed
480
{
Benjamin Otte's avatar
Benjamin Otte committed
481
  int i;
Carlos Garnacho's avatar
Carlos Garnacho committed
482
483
484
485
486
487
488
489
490
491

  g_return_if_fail (GTK_IS_PAD_CONTROLLER (controller));
  g_return_if_fail (entries != NULL);

  for (i = 0; i < n_entries; i++)
    gtk_pad_controller_add_entry (controller, &entries[i]);
}

/**
 * gtk_pad_controller_set_action:
Matthias Clasen's avatar
Matthias Clasen committed
492
 * @controller: a `GtkPadController`
Carlos Garnacho's avatar
Carlos Garnacho committed
493
494
495
496
497
 * @type: the type of pad feature that will trigger this action
 * @index: the 0-indexed button/ring/strip number that will trigger this action
 * @mode: the mode that will trigger this action, or -1 for all modes.
 * @label: Human readable description of this action, this string should
 *   be deemed user-visible.
Matthias Clasen's avatar
Matthias Clasen committed
498
 * @action_name: action name that will be activated in the `GActionGroup`
Carlos Garnacho's avatar
Carlos Garnacho committed
499
 *
Matthias Clasen's avatar
Matthias Clasen committed
500
501
502
503
504
 * Adds an individual action to @controller.
 *
 * This action will only be activated if the given button/ring/strip number
 * in @index is interacted while the current mode is @mode. -1 may be used
 * for simple cases, so the action is triggered on all modes.
Carlos Garnacho's avatar
Carlos Garnacho committed
505
506
507
508
 *
 * The given @label should be considered user-visible, so internationalization
 * rules apply. Some windowing systems may be able to use those for user
 * feedback.
Matthias Clasen's avatar
Matthias Clasen committed
509
 */
Carlos Garnacho's avatar
Carlos Garnacho committed
510
511
512
void
gtk_pad_controller_set_action (GtkPadController *controller,
                               GtkPadActionType  type,
513
514
515
516
                               int               index,
                               int               mode,
                               const char       *label,
                               const char       *action_name)
Carlos Garnacho's avatar
Carlos Garnacho committed
517
{
518
  const GtkPadActionEntry entry = { type, index, mode, label, action_name };
Carlos Garnacho's avatar
Carlos Garnacho committed
519
520

  g_return_if_fail (GTK_IS_PAD_CONTROLLER (controller));
521
  g_return_if_fail (type <= GTK_PAD_ACTION_STRIP);
Carlos Garnacho's avatar
Carlos Garnacho committed
522
523
524

  gtk_pad_controller_add_entry (controller, &entry);
}