1 /*
2  * Copyright © 2011  Intel Corp.
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library. If not, see <http://www.gnu.org/licenses/>.
16  *
17  * Author: Emmanuele Bassi <ebassi@linux.intel.com>
18  */
19 
20 #include "config.h"
21 
22 #include <X11/extensions/XInput2.h>
23 
24 #include "clutter/clutter-mutter.h"
25 #include "backends/x11/meta-clutter-backend-x11.h"
26 #include "backends/x11/meta-input-device-x11.h"
27 
28 struct _MetaInputDeviceX11
29 {
30   ClutterInputDevice device;
31 
32   int32_t device_id;
33   ClutterInputDeviceTool *current_tool;
34 
35   int inhibit_pointer_query_timer;
36   gboolean query_status;
37   float current_x;
38   float current_y;
39 
40   GArray *axes;
41   GArray *scroll_info;
42 
43 #ifdef HAVE_LIBWACOM
44   GArray *group_modes;
45 #endif
46 };
47 
48 typedef struct _MetaX11AxisInfo
49 {
50   ClutterInputAxis axis;
51 
52   double min_axis;
53   double max_axis;
54 
55   double min_value;
56   double max_value;
57 
58   double resolution;
59 } MetaX11AxisInfo;
60 
61 typedef struct _MetaX11ScrollInfo
62 {
63   guint axis_id;
64   ClutterScrollDirection direction;
65   double increment;
66 
67   double last_value;
68   guint last_value_valid : 1;
69 } MetaX11ScrollInfo;
70 
71 struct _MetaInputDeviceX11Class
72 {
73   ClutterInputDeviceClass device_class;
74 };
75 
76 #define N_BUTTONS       5
77 
78 enum
79 {
80   PROP_0,
81   PROP_ID,
82   N_PROPS
83 };
84 
85 static GParamSpec *props[N_PROPS] = { 0 };
86 
G_DEFINE_TYPE(MetaInputDeviceX11,meta_input_device_x11,META_TYPE_INPUT_DEVICE)87 G_DEFINE_TYPE (MetaInputDeviceX11,
88                meta_input_device_x11,
89                META_TYPE_INPUT_DEVICE)
90 
91 static void
92 meta_input_device_x11_constructed (GObject *object)
93 {
94   MetaInputDeviceX11 *device_xi2 = META_INPUT_DEVICE_X11 (object);
95 
96   if (G_OBJECT_CLASS (meta_input_device_x11_parent_class)->constructed)
97     G_OBJECT_CLASS (meta_input_device_x11_parent_class)->constructed (object);
98 
99 #ifdef HAVE_LIBWACOM
100   if (clutter_input_device_get_device_type (CLUTTER_INPUT_DEVICE (object)) == CLUTTER_PAD_DEVICE)
101     {
102       device_xi2->group_modes = g_array_new (FALSE, TRUE, sizeof (uint32_t));
103       g_array_set_size (device_xi2->group_modes,
104                         clutter_input_device_get_n_mode_groups (CLUTTER_INPUT_DEVICE (object)));
105     }
106 #endif
107 }
108 
109 static gboolean
meta_input_device_x11_is_grouped(ClutterInputDevice * device,ClutterInputDevice * other_device)110 meta_input_device_x11_is_grouped (ClutterInputDevice *device,
111                                   ClutterInputDevice *other_device)
112 {
113 #ifdef HAVE_LIBWACOM
114   WacomDevice *wacom_device, *other_wacom_device;
115 
116   wacom_device =
117     meta_input_device_get_wacom_device (META_INPUT_DEVICE (device));
118   other_wacom_device =
119     meta_input_device_get_wacom_device (META_INPUT_DEVICE (other_device));
120 
121   if (wacom_device && other_wacom_device &&
122       libwacom_compare (wacom_device,
123                         other_wacom_device,
124                         WCOMPARE_NORMAL) == 0)
125     return TRUE;
126 #endif
127 
128   if (clutter_input_device_get_vendor_id (device) &&
129       clutter_input_device_get_product_id (device) &&
130       clutter_input_device_get_vendor_id (other_device) &&
131       clutter_input_device_get_product_id (other_device))
132     {
133       if (strcmp (clutter_input_device_get_vendor_id (device),
134                   clutter_input_device_get_vendor_id (other_device)) == 0 &&
135           strcmp (clutter_input_device_get_product_id (device),
136                   clutter_input_device_get_product_id (other_device)) == 0)
137         return TRUE;
138     }
139 
140   return FALSE;
141 }
142 
143 static void
meta_input_device_x11_finalize(GObject * object)144 meta_input_device_x11_finalize (GObject *object)
145 {
146   MetaInputDeviceX11 *device_xi2 = META_INPUT_DEVICE_X11 (object);
147 
148   g_clear_pointer (&device_xi2->axes, g_array_unref);
149   g_clear_pointer (&device_xi2->scroll_info, g_array_unref);
150 
151 #ifdef HAVE_LIBWACOM
152   if (device_xi2->group_modes)
153     g_array_unref (device_xi2->group_modes);
154 #endif
155 
156   g_clear_handle_id (&device_xi2->inhibit_pointer_query_timer, g_source_remove);
157 
158   G_OBJECT_CLASS (meta_input_device_x11_parent_class)->finalize (object);
159 }
160 
161 static void
meta_input_device_x11_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)162 meta_input_device_x11_set_property (GObject      *object,
163                                     guint         prop_id,
164                                     const GValue *value,
165                                     GParamSpec   *pspec)
166 {
167   MetaInputDeviceX11 *device_x11 = META_INPUT_DEVICE_X11 (object);
168 
169   switch (prop_id)
170     {
171     case PROP_ID:
172       device_x11->device_id = g_value_get_int (value);
173       break;
174     default:
175       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
176     }
177 }
178 
179 static void
meta_input_device_x11_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)180 meta_input_device_x11_get_property (GObject    *object,
181                                     guint       prop_id,
182                                     GValue     *value,
183                                     GParamSpec *pspec)
184 {
185   MetaInputDeviceX11 *device_x11 = META_INPUT_DEVICE_X11 (object);
186 
187   switch (prop_id)
188     {
189     case PROP_ID:
190       g_value_set_int (value, device_x11->device_id);
191       break;
192     default:
193       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
194     }
195 }
196 
197 static int
meta_input_device_x11_get_group_n_modes(ClutterInputDevice * device,int group)198 meta_input_device_x11_get_group_n_modes (ClutterInputDevice *device,
199                                          int                 group)
200 {
201 #ifdef HAVE_LIBWACOM
202   WacomDevice *wacom_device;
203 
204   wacom_device = meta_input_device_get_wacom_device (META_INPUT_DEVICE (device));
205 
206   if (wacom_device)
207     {
208       if (group == 0)
209         {
210           if (libwacom_has_ring (wacom_device))
211             return libwacom_get_ring_num_modes (wacom_device);
212           else if (libwacom_get_num_strips (wacom_device) >= 1)
213             return libwacom_get_strips_num_modes (wacom_device);
214         }
215       else if (group == 1)
216         {
217           if (libwacom_has_ring2 (wacom_device))
218             return libwacom_get_ring2_num_modes (wacom_device);
219           else if (libwacom_get_num_strips (wacom_device) >= 2)
220             return libwacom_get_strips_num_modes (wacom_device);
221         }
222     }
223 #endif
224 
225   return -1;
226 }
227 
228 #ifdef HAVE_LIBWACOM
229 static int
meta_input_device_x11_get_button_group(ClutterInputDevice * device,uint32_t button)230 meta_input_device_x11_get_button_group (ClutterInputDevice *device,
231                                         uint32_t            button)
232 {
233   WacomDevice *wacom_device;
234 
235   wacom_device = meta_input_device_get_wacom_device (META_INPUT_DEVICE (device));
236 
237   if (wacom_device)
238     {
239       WacomButtonFlags flags;
240 
241       if (button >= libwacom_get_num_buttons (wacom_device))
242         return -1;
243 
244       flags = libwacom_get_button_flag (wacom_device, 'A' + button);
245 
246       if (flags &
247           (WACOM_BUTTON_RING_MODESWITCH |
248            WACOM_BUTTON_TOUCHSTRIP_MODESWITCH))
249         return 0;
250       if (flags &
251           (WACOM_BUTTON_RING2_MODESWITCH |
252            WACOM_BUTTON_TOUCHSTRIP2_MODESWITCH))
253         return 1;
254     }
255 
256   return -1;
257 }
258 #endif
259 
260 static gboolean
meta_input_device_x11_is_mode_switch_button(ClutterInputDevice * device,uint32_t group,uint32_t button)261 meta_input_device_x11_is_mode_switch_button (ClutterInputDevice *device,
262                                              uint32_t            group,
263                                              uint32_t            button)
264 {
265   int button_group = -1;
266 
267 #ifdef HAVE_LIBWACOM
268   button_group = meta_input_device_x11_get_button_group (device, button);
269 #endif
270 
271   return button_group == (int) group;
272 }
273 
274 static void
meta_input_device_x11_class_init(MetaInputDeviceX11Class * klass)275 meta_input_device_x11_class_init (MetaInputDeviceX11Class *klass)
276 {
277   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
278   ClutterInputDeviceClass *device_class = CLUTTER_INPUT_DEVICE_CLASS (klass);
279 
280   gobject_class->constructed = meta_input_device_x11_constructed;
281   gobject_class->finalize = meta_input_device_x11_finalize;
282   gobject_class->set_property = meta_input_device_x11_set_property;
283   gobject_class->get_property = meta_input_device_x11_get_property;
284 
285   device_class->is_grouped = meta_input_device_x11_is_grouped;
286   device_class->get_group_n_modes = meta_input_device_x11_get_group_n_modes;
287   device_class->is_mode_switch_button = meta_input_device_x11_is_mode_switch_button;
288 
289   props[PROP_ID] =
290     g_param_spec_int ("id",
291                       "Id",
292                       "Unique identifier of the device",
293                       -1, G_MAXINT,
294                       0,
295                       CLUTTER_PARAM_READWRITE |
296                       G_PARAM_CONSTRUCT_ONLY);
297 
298   g_object_class_install_properties (gobject_class, N_PROPS, props);
299 }
300 
301 static void
meta_input_device_x11_init(MetaInputDeviceX11 * self)302 meta_input_device_x11_init (MetaInputDeviceX11 *self)
303 {
304 }
305 
306 static ClutterModifierType
get_modifier_for_button(int i)307 get_modifier_for_button (int i)
308 {
309   switch (i)
310     {
311     case 1:
312       return CLUTTER_BUTTON1_MASK;
313     case 2:
314       return CLUTTER_BUTTON2_MASK;
315     case 3:
316       return CLUTTER_BUTTON3_MASK;
317     case 4:
318       return CLUTTER_BUTTON4_MASK;
319     case 5:
320       return CLUTTER_BUTTON5_MASK;
321     default:
322       return 0;
323     }
324 }
325 
326 void
meta_input_device_x11_translate_state(ClutterEvent * event,XIModifierState * modifiers_state,XIButtonState * buttons_state,XIGroupState * group_state)327 meta_input_device_x11_translate_state (ClutterEvent    *event,
328                                        XIModifierState *modifiers_state,
329                                        XIButtonState   *buttons_state,
330                                        XIGroupState    *group_state)
331 {
332   uint32_t button = 0;
333   uint32_t base = 0;
334   uint32_t latched = 0;
335   uint32_t locked = 0;
336   uint32_t effective;
337 
338   if (modifiers_state)
339     {
340       base = (uint32_t) modifiers_state->base;
341       latched = (uint32_t) modifiers_state->latched;
342       locked = (uint32_t) modifiers_state->locked;
343     }
344 
345   if (buttons_state)
346     {
347       int len, i;
348 
349       len = MIN (N_BUTTONS, buttons_state->mask_len * 8);
350 
351       for (i = 0; i < len; i++)
352         {
353           if (!XIMaskIsSet (buttons_state->mask, i))
354             continue;
355 
356           button |= get_modifier_for_button (i);
357         }
358     }
359 
360   /* The XIButtonState sent in the event specifies the
361    * state of the buttons before the event. In order to
362    * get the current state of the buttons, we need to
363    * filter out the current button.
364    */
365   switch (event->type)
366     {
367     case CLUTTER_BUTTON_PRESS:
368       button |=  (get_modifier_for_button (event->button.button));
369       break;
370     case CLUTTER_BUTTON_RELEASE:
371       button &= ~(get_modifier_for_button (event->button.button));
372       break;
373     default:
374       break;
375     }
376 
377   effective = button | base | latched | locked;
378   if (group_state)
379     effective |= (group_state->effective) << 13;
380 
381   _clutter_event_set_state_full (event, button, base, latched, locked, effective);
382 }
383 
384 void
meta_input_device_x11_update_tool(ClutterInputDevice * device,ClutterInputDeviceTool * tool)385 meta_input_device_x11_update_tool (ClutterInputDevice     *device,
386                                    ClutterInputDeviceTool *tool)
387 {
388   MetaInputDeviceX11 *device_xi2 = META_INPUT_DEVICE_X11 (device);
389   g_set_object (&device_xi2->current_tool, tool);
390 }
391 
392 ClutterInputDeviceTool *
meta_input_device_x11_get_current_tool(ClutterInputDevice * device)393 meta_input_device_x11_get_current_tool (ClutterInputDevice *device)
394 {
395   MetaInputDeviceX11 *device_xi2 = META_INPUT_DEVICE_X11 (device);
396   return device_xi2->current_tool;
397 }
398 
399 static gboolean
meta_input_device_x11_query_pointer_location(MetaInputDeviceX11 * device_xi2)400 meta_input_device_x11_query_pointer_location (MetaInputDeviceX11 *device_xi2)
401 {
402   Window xroot_window, xchild_window;
403   double xroot_x, xroot_y, xwin_x, xwin_y;
404   XIButtonState button_state = { 0 };
405   XIModifierState mod_state;
406   XIGroupState group_state;
407   int result;
408 
409   meta_clutter_x11_trap_x_errors ();
410   result = XIQueryPointer (meta_clutter_x11_get_default_display (),
411                            device_xi2->device_id,
412                            meta_clutter_x11_get_root_window (),
413                            &xroot_window,
414                            &xchild_window,
415                            &xroot_x, &xroot_y,
416                            &xwin_x, &xwin_y,
417                            &button_state,
418                            &mod_state,
419                            &group_state);
420   meta_clutter_x11_untrap_x_errors ();
421 
422   g_free (button_state.mask);
423 
424   if (!result)
425     return FALSE;
426 
427   device_xi2->current_x = (float) xroot_x;
428   device_xi2->current_y = (float) xroot_y;
429 
430   return TRUE;
431 }
432 
433 static gboolean
clear_inhibit_pointer_query_cb(gpointer data)434 clear_inhibit_pointer_query_cb (gpointer data)
435 {
436   MetaInputDeviceX11 *device_xi2 = META_INPUT_DEVICE_X11 (data);
437 
438   device_xi2->inhibit_pointer_query_timer = 0;
439 
440   return G_SOURCE_REMOVE;
441 }
442 
443 gboolean
meta_input_device_x11_get_pointer_location(ClutterInputDevice * device,float * x,float * y)444 meta_input_device_x11_get_pointer_location (ClutterInputDevice *device,
445                                             float              *x,
446                                             float              *y)
447 
448 {
449   MetaInputDeviceX11 *device_xi2 = META_INPUT_DEVICE_X11 (device);
450 
451   g_return_val_if_fail (META_IS_INPUT_DEVICE_X11 (device), FALSE);
452   g_return_val_if_fail (clutter_input_device_get_device_type (device) ==
453                         CLUTTER_POINTER_DEVICE, FALSE);
454 
455   /* Throttle XServer queries and roundtrips using an idle timeout */
456   if (device_xi2->inhibit_pointer_query_timer == 0)
457     {
458       device_xi2->query_status =
459         meta_input_device_x11_query_pointer_location (device_xi2);
460       device_xi2->inhibit_pointer_query_timer =
461         clutter_threads_add_idle (clear_inhibit_pointer_query_cb, device_xi2);
462     }
463 
464   *x = device_xi2->current_x;
465   *y = device_xi2->current_y;
466 
467   return device_xi2->query_status;
468 }
469 
470 int
meta_input_device_x11_get_device_id(ClutterInputDevice * device)471 meta_input_device_x11_get_device_id (ClutterInputDevice *device)
472 {
473   MetaInputDeviceX11 *device_xi2 = META_INPUT_DEVICE_X11 (device);
474 
475   g_return_val_if_fail (META_IS_INPUT_DEVICE_X11 (device), 0);
476 
477   return device_xi2->device_id;
478 }
479 
480 void
meta_input_device_x11_reset_axes(ClutterInputDevice * device)481 meta_input_device_x11_reset_axes (ClutterInputDevice *device)
482 {
483   MetaInputDeviceX11 *device_x11 = META_INPUT_DEVICE_X11 (device);
484 
485   g_clear_pointer (&device_x11->axes, g_array_unref);
486 }
487 
488 int
meta_input_device_x11_add_axis(ClutterInputDevice * device,ClutterInputAxis axis,double minimum,double maximum,double resolution)489 meta_input_device_x11_add_axis (ClutterInputDevice *device,
490                                 ClutterInputAxis    axis,
491                                 double              minimum,
492                                 double              maximum,
493                                 double              resolution)
494 {
495   MetaInputDeviceX11 *device_x11 = META_INPUT_DEVICE_X11 (device);
496   MetaX11AxisInfo info;
497   guint pos;
498 
499   if (device_x11->axes == NULL)
500     device_x11->axes = g_array_new (FALSE, TRUE, sizeof (MetaX11AxisInfo));
501 
502   info.axis = axis;
503   info.min_value = minimum;
504   info.max_value = maximum;
505   info.resolution = resolution;
506 
507   switch (axis)
508     {
509     case CLUTTER_INPUT_AXIS_X:
510     case CLUTTER_INPUT_AXIS_Y:
511       info.min_axis = 0;
512       info.max_axis = 0;
513       break;
514 
515     case CLUTTER_INPUT_AXIS_XTILT:
516     case CLUTTER_INPUT_AXIS_YTILT:
517       info.min_axis = -1;
518       info.max_axis = 1;
519       break;
520 
521     default:
522       info.min_axis = 0;
523       info.max_axis = 1;
524       break;
525     }
526 
527   g_array_append_val (device_x11->axes, info);
528   pos = device_x11->axes->len - 1;
529 
530   return pos;
531 }
532 
533 gboolean
meta_input_device_x11_get_axis(ClutterInputDevice * device,int idx,ClutterInputAxis * use)534 meta_input_device_x11_get_axis (ClutterInputDevice *device,
535                                 int                 idx,
536                                 ClutterInputAxis   *use)
537 {
538   MetaInputDeviceX11 *device_x11 = META_INPUT_DEVICE_X11 (device);
539   MetaX11AxisInfo *info;
540 
541   if (device_x11->axes == NULL)
542     return FALSE;
543 
544   if (idx < 0 || idx >= device_x11->axes->len)
545     return FALSE;
546 
547   info = &g_array_index (device_x11->axes, MetaX11AxisInfo, idx);
548 
549   if (use)
550     *use = info->axis;
551 
552   return TRUE;
553 }
554 
555 gboolean
meta_input_device_x11_translate_axis(ClutterInputDevice * device,int idx,double value,double * axis_value)556 meta_input_device_x11_translate_axis (ClutterInputDevice *device,
557                                       int                 idx,
558                                       double              value,
559                                       double             *axis_value)
560 {
561   MetaInputDeviceX11 *device_x11 = META_INPUT_DEVICE_X11 (device);
562   MetaX11AxisInfo *info;
563   double width;
564   double real_value;
565 
566   if (device_x11->axes == NULL || idx < 0 || idx >= device_x11->axes->len)
567     return FALSE;
568 
569   info = &g_array_index (device_x11->axes, MetaX11AxisInfo, idx);
570 
571   if (info->axis == CLUTTER_INPUT_AXIS_X ||
572       info->axis == CLUTTER_INPUT_AXIS_Y)
573     return FALSE;
574 
575   if (fabs (info->max_value - info->min_value) < 0.0000001)
576     return FALSE;
577 
578   width = info->max_value - info->min_value;
579   real_value = (info->max_axis * (value - info->min_value)
580              + info->min_axis * (info->max_value - value))
581              / width;
582 
583   if (axis_value)
584     *axis_value = real_value;
585 
586   return TRUE;
587 }
588 
589 int
meta_input_device_x11_get_n_axes(ClutterInputDevice * device)590 meta_input_device_x11_get_n_axes (ClutterInputDevice *device)
591 {
592   MetaInputDeviceX11 *device_x11 = META_INPUT_DEVICE_X11 (device);
593 
594   return device_x11->axes->len;
595 }
596 
597 void
meta_input_device_x11_add_scroll_info(ClutterInputDevice * device,int idx,ClutterScrollDirection direction,double increment)598 meta_input_device_x11_add_scroll_info (ClutterInputDevice     *device,
599                                        int                     idx,
600                                        ClutterScrollDirection  direction,
601                                        double                  increment)
602 {
603   MetaInputDeviceX11 *device_x11 = META_INPUT_DEVICE_X11 (device);
604   MetaX11ScrollInfo info;
605 
606   g_return_if_fail (CLUTTER_IS_INPUT_DEVICE (device));
607 
608   info.axis_id = idx;
609   info.direction = direction;
610   info.increment = increment;
611   info.last_value_valid = FALSE;
612 
613   if (device_x11->scroll_info == NULL)
614     {
615       device_x11->scroll_info = g_array_new (FALSE,
616                                              FALSE,
617                                              sizeof (MetaX11ScrollInfo));
618     }
619 
620   g_array_append_val (device_x11->scroll_info, info);
621 }
622 
623 gboolean
meta_input_device_x11_get_scroll_delta(ClutterInputDevice * device,int idx,double value,ClutterScrollDirection * direction_p,double * delta_p)624 meta_input_device_x11_get_scroll_delta (ClutterInputDevice     *device,
625                                         int                     idx,
626                                         double                  value,
627                                         ClutterScrollDirection *direction_p,
628                                         double                 *delta_p)
629 {
630   MetaInputDeviceX11 *device_x11 = META_INPUT_DEVICE_X11 (device);
631   int i;
632 
633   if (device_x11->scroll_info == NULL)
634     return FALSE;
635 
636   for (i = 0; i < device_x11->scroll_info->len; i++)
637     {
638       MetaX11ScrollInfo *info = &g_array_index (device_x11->scroll_info,
639                                                 MetaX11ScrollInfo,
640                                                 i);
641 
642       if (info->axis_id == idx)
643         {
644           if (direction_p != NULL)
645             *direction_p = info->direction;
646 
647           if (delta_p != NULL)
648             *delta_p = 0.0;
649 
650           if (info->last_value_valid)
651             {
652               if (delta_p != NULL)
653                 {
654                   *delta_p = (value - info->last_value)
655                            / info->increment;
656                 }
657 
658               info->last_value = value;
659             }
660           else
661             {
662               info->last_value = value;
663               info->last_value_valid = TRUE;
664             }
665 
666           return TRUE;
667         }
668     }
669 
670   return FALSE;
671 }
672 
673 void
meta_input_device_x11_reset_scroll_info(ClutterInputDevice * device)674 meta_input_device_x11_reset_scroll_info (ClutterInputDevice *device)
675 {
676   MetaInputDeviceX11 *device_x11 = META_INPUT_DEVICE_X11 (device);
677   int i;
678 
679   if (device_x11->scroll_info == NULL)
680     return;
681 
682   for (i = 0; i < device_x11->scroll_info->len; i++)
683     {
684       MetaX11ScrollInfo *info = &g_array_index (device_x11->scroll_info,
685                                                 MetaX11ScrollInfo,
686                                                 i);
687 
688       info->last_value_valid = FALSE;
689     }
690 }
691 
692 #ifdef HAVE_LIBWACOM
693 uint32_t
meta_input_device_x11_get_pad_group_mode(ClutterInputDevice * device,uint32_t group)694 meta_input_device_x11_get_pad_group_mode (ClutterInputDevice *device,
695                                           uint32_t            group)
696 {
697   MetaInputDeviceX11 *device_xi2 = META_INPUT_DEVICE_X11 (device);
698 
699   if (group >= device_xi2->group_modes->len)
700     return 0;
701 
702   return g_array_index (device_xi2->group_modes, uint32_t, group);
703 }
704 
705 static gboolean
pad_switch_mode(ClutterInputDevice * device,uint32_t button,uint32_t group,uint32_t * mode)706 pad_switch_mode (ClutterInputDevice *device,
707                  uint32_t            button,
708                  uint32_t            group,
709                  uint32_t           *mode)
710 {
711   MetaInputDeviceX11 *device_x11 = META_INPUT_DEVICE_X11 (device);
712   uint32_t n_buttons, n_modes, button_group, next_mode, i;
713   WacomDevice *wacom_device;
714   GList *switch_buttons = NULL;
715 
716   wacom_device =
717     meta_input_device_get_wacom_device (META_INPUT_DEVICE (device));
718   n_buttons = libwacom_get_num_buttons (wacom_device);
719 
720   for (i = 0; i < n_buttons; i++)
721     {
722       button_group = meta_input_device_x11_get_button_group (device, i);
723       if (button_group == group)
724         switch_buttons = g_list_prepend (switch_buttons, GINT_TO_POINTER (button));
725     }
726 
727   switch_buttons = g_list_reverse (switch_buttons);
728   n_modes = clutter_input_device_get_group_n_modes (device, group);
729 
730   if (g_list_length (switch_buttons) > 1)
731     {
732       /* If there's multiple switch buttons, we don't toggle but assign a mode
733        * to each of those buttons.
734        */
735       next_mode = g_list_index (switch_buttons, GINT_TO_POINTER (button));
736     }
737   else if (switch_buttons)
738     {
739       uint32_t cur_mode;
740 
741       /* If there is a single button, have it toggle across modes */
742       cur_mode = g_array_index (device_x11->group_modes, uint32_t, group);
743       next_mode = (cur_mode + 1) % n_modes;
744     }
745   else
746     {
747       return FALSE;
748     }
749 
750   g_list_free (switch_buttons);
751 
752   if (next_mode < 0 || next_mode > n_modes)
753     return FALSE;
754 
755   *mode = next_mode;
756   return TRUE;
757 }
758 
759 void
meta_input_device_x11_update_pad_state(ClutterInputDevice * device,uint32_t button,uint32_t state,uint32_t * group,uint32_t * mode)760 meta_input_device_x11_update_pad_state (ClutterInputDevice *device,
761                                         uint32_t            button,
762                                         uint32_t            state,
763                                         uint32_t           *group,
764                                         uint32_t           *mode)
765 {
766   MetaInputDeviceX11 *device_xi2 = META_INPUT_DEVICE_X11 (device);
767   uint32_t button_group, *group_mode;
768 
769   button_group = meta_input_device_x11_get_button_group (device, button);
770 
771   if (button_group < 0 || button_group >= device_xi2->group_modes->len)
772     {
773       if (group)
774         *group = 0;
775       if (mode)
776         *mode = 0;
777       return;
778     }
779 
780   group_mode = &g_array_index (device_xi2->group_modes, uint32_t, button_group);
781 
782   if (state)
783     {
784       uint32_t next_mode;
785 
786       if (pad_switch_mode (device, button, button_group, &next_mode))
787         *group_mode = next_mode;
788     }
789 
790   if (group)
791     *group = button_group;
792   if (mode)
793     *mode = *group_mode;
794 }
795 #endif
796