1 /*
2  * Clutter.
3  *
4  * An OpenGL based 'interactive canvas' library.
5  *
6  * Copyright © 2011  Intel Corp.
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library. If not, see <http://www.gnu.org/licenses/>.
20  *
21  * Author: Emmanuele Bassi <ebassi@linux.intel.com>
22  */
23 
24 #include "clutter-build-config.h"
25 
26 #include "clutter-input-device-xi2.h"
27 
28 #include "clutter-debug.h"
29 #include "clutter-device-manager-private.h"
30 #include "clutter-event-private.h"
31 #include "clutter-private.h"
32 #include "clutter-stage-private.h"
33 
34 #include "clutter-backend-x11.h"
35 #include "clutter-stage-x11.h"
36 
37 #include <X11/extensions/XInput2.h>
38 
39 typedef struct _ClutterInputDeviceClass         ClutterInputDeviceXI2Class;
40 
41 /* a specific XI2 input device */
42 struct _ClutterInputDeviceXI2
43 {
44   ClutterInputDevice device;
45 
46   gint device_id;
47   ClutterInputDeviceTool *current_tool;
48 
49 #ifdef HAVE_LIBWACOM
50   WacomDevice *wacom_device;
51   GArray *group_modes;
52 #endif
53 };
54 
55 #define N_BUTTONS       5
56 
57 #define clutter_input_device_xi2_get_type       _clutter_input_device_xi2_get_type
58 
59 G_DEFINE_TYPE (ClutterInputDeviceXI2,
60                clutter_input_device_xi2,
61                CLUTTER_TYPE_INPUT_DEVICE);
62 
63 static void
clutter_input_device_xi2_constructed(GObject * gobject)64 clutter_input_device_xi2_constructed (GObject *gobject)
65 {
66   ClutterInputDeviceXI2 *device_xi2 = CLUTTER_INPUT_DEVICE_XI2 (gobject);
67 
68   g_object_get (gobject, "id", &device_xi2->device_id, NULL);
69 
70   if (G_OBJECT_CLASS (clutter_input_device_xi2_parent_class)->constructed)
71     G_OBJECT_CLASS (clutter_input_device_xi2_parent_class)->constructed (gobject);
72 
73 #ifdef HAVE_LIBWACOM
74   if (clutter_input_device_get_device_type (CLUTTER_INPUT_DEVICE (gobject)) == CLUTTER_PAD_DEVICE)
75     {
76       device_xi2->group_modes = g_array_new (FALSE, TRUE, sizeof (guint));
77       g_array_set_size (device_xi2->group_modes,
78                         clutter_input_device_get_n_mode_groups (CLUTTER_INPUT_DEVICE (gobject)));
79     }
80 #endif
81 }
82 
83 static gboolean
clutter_input_device_xi2_keycode_to_evdev(ClutterInputDevice * device,guint hardware_keycode,guint * evdev_keycode)84 clutter_input_device_xi2_keycode_to_evdev (ClutterInputDevice *device,
85                                            guint hardware_keycode,
86                                            guint *evdev_keycode)
87 {
88   /* When using evdev under X11 the hardware keycodes are the evdev
89      keycodes plus 8. I haven't been able to find any documentation to
90      know what the +8 is for. FIXME: This should probably verify that
91      X server is using evdev. */
92   *evdev_keycode = hardware_keycode - 8;
93 
94   return TRUE;
95 }
96 
97 static gboolean
clutter_input_device_xi2_is_grouped(ClutterInputDevice * device,ClutterInputDevice * other_device)98 clutter_input_device_xi2_is_grouped (ClutterInputDevice *device,
99                                      ClutterInputDevice *other_device)
100 {
101   return FALSE;
102 }
103 
104 static void
clutter_input_device_xi2_finalize(GObject * object)105 clutter_input_device_xi2_finalize (GObject *object)
106 {
107 #ifdef HAVE_LIBWACOM
108   ClutterInputDeviceXI2 *device_xi2 = CLUTTER_INPUT_DEVICE_XI2 (object);
109 
110   if (device_xi2->wacom_device)
111     libwacom_destroy (device_xi2->wacom_device);
112 
113   if (device_xi2->group_modes)
114     g_array_unref (device_xi2->group_modes);
115 #endif
116 
117   G_OBJECT_CLASS (clutter_input_device_xi2_parent_class)->finalize (object);
118 }
119 
120 static gint
clutter_input_device_xi2_get_group_n_modes(ClutterInputDevice * device,gint group)121 clutter_input_device_xi2_get_group_n_modes (ClutterInputDevice *device,
122                                             gint                group)
123 {
124 #ifdef HAVE_LIBWACOM
125   ClutterInputDeviceXI2 *device_xi2 = CLUTTER_INPUT_DEVICE_XI2 (device);
126 
127   if (device_xi2->wacom_device)
128     {
129       if (group == 0)
130         {
131           if (libwacom_has_ring (device_xi2->wacom_device))
132             return libwacom_get_ring_num_modes (device_xi2->wacom_device);
133           else if (libwacom_get_num_strips (device_xi2->wacom_device) >= 1)
134             return libwacom_get_strips_num_modes (device_xi2->wacom_device);
135         }
136       else if (group == 1)
137         {
138           if (libwacom_has_ring2 (device_xi2->wacom_device))
139             return libwacom_get_ring2_num_modes (device_xi2->wacom_device);
140           else if (libwacom_get_num_strips (device_xi2->wacom_device) >= 2)
141             return libwacom_get_strips_num_modes (device_xi2->wacom_device);
142         }
143     }
144 #endif
145 
146   return -1;
147 }
148 
149 #ifdef HAVE_LIBWACOM
150 static int
clutter_input_device_xi2_get_button_group(ClutterInputDevice * device,guint button)151 clutter_input_device_xi2_get_button_group (ClutterInputDevice *device,
152                                            guint               button)
153 {
154   ClutterInputDeviceXI2 *device_xi2 = CLUTTER_INPUT_DEVICE_XI2 (device);
155 
156   if (device_xi2->wacom_device)
157     {
158       if (button >= libwacom_get_num_buttons (device_xi2->wacom_device))
159         return -1;
160 
161       return libwacom_get_button_led_group (device_xi2->wacom_device,
162                                             'A' + button);
163     }
164   else
165     return -1;
166 }
167 #endif
168 
169 static gboolean
clutter_input_device_xi2_is_mode_switch_button(ClutterInputDevice * device,guint group,guint button)170 clutter_input_device_xi2_is_mode_switch_button (ClutterInputDevice *device,
171                                                 guint               group,
172                                                 guint               button)
173 {
174   int button_group = -1;
175 
176 #ifdef HAVE_LIBWACOM
177   button_group = clutter_input_device_xi2_get_button_group (device, button);
178 #endif
179 
180   return button_group == (int) group;
181 }
182 
183 static void
clutter_input_device_xi2_class_init(ClutterInputDeviceXI2Class * klass)184 clutter_input_device_xi2_class_init (ClutterInputDeviceXI2Class *klass)
185 {
186   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
187   ClutterInputDeviceClass *device_class = CLUTTER_INPUT_DEVICE_CLASS (klass);
188 
189   gobject_class->constructed = clutter_input_device_xi2_constructed;
190   gobject_class->finalize = clutter_input_device_xi2_finalize;
191 
192   device_class->keycode_to_evdev = clutter_input_device_xi2_keycode_to_evdev;
193   device_class->is_grouped = clutter_input_device_xi2_is_grouped;
194   device_class->get_group_n_modes = clutter_input_device_xi2_get_group_n_modes;
195   device_class->is_mode_switch_button = clutter_input_device_xi2_is_mode_switch_button;
196 }
197 
198 static void
clutter_input_device_xi2_init(ClutterInputDeviceXI2 * self)199 clutter_input_device_xi2_init (ClutterInputDeviceXI2 *self)
200 {
201 }
202 
203 static ClutterModifierType
get_modifier_for_button(int i)204 get_modifier_for_button (int i)
205 {
206   switch (i)
207     {
208     case 1:
209       return CLUTTER_BUTTON1_MASK;
210     case 2:
211       return CLUTTER_BUTTON2_MASK;
212     case 3:
213       return CLUTTER_BUTTON3_MASK;
214     case 4:
215       return CLUTTER_BUTTON4_MASK;
216     case 5:
217       return CLUTTER_BUTTON5_MASK;
218     default:
219       return 0;
220     }
221 }
222 
223 void
_clutter_input_device_xi2_translate_state(ClutterEvent * event,XIModifierState * modifiers_state,XIButtonState * buttons_state,XIGroupState * group_state)224 _clutter_input_device_xi2_translate_state (ClutterEvent    *event,
225 					   XIModifierState *modifiers_state,
226                                            XIButtonState   *buttons_state,
227                                            XIGroupState    *group_state)
228 {
229   guint button = 0;
230   guint base = 0;
231   guint latched = 0;
232   guint locked = 0;
233   guint effective;
234 
235   if (modifiers_state)
236     {
237       base = (guint) modifiers_state->base;
238       latched = (guint) modifiers_state->latched;
239       locked = (guint) modifiers_state->locked;
240     }
241 
242   if (buttons_state)
243     {
244       int len, i;
245 
246       len = MIN (N_BUTTONS, buttons_state->mask_len * 8);
247 
248       for (i = 0; i < len; i++)
249         {
250           if (!XIMaskIsSet (buttons_state->mask, i))
251             continue;
252 
253           button |= get_modifier_for_button (i);
254         }
255     }
256 
257   /* The XIButtonState sent in the event specifies the
258    * state of the buttons before the event. In order to
259    * get the current state of the buttons, we need to
260    * filter out the current button.
261    */
262   switch (event->type)
263     {
264     case CLUTTER_BUTTON_PRESS:
265       button |=  (get_modifier_for_button (event->button.button));
266       break;
267     case CLUTTER_BUTTON_RELEASE:
268       button &= ~(get_modifier_for_button (event->button.button));
269       break;
270     default:
271       break;
272     }
273 
274   effective = button | base | latched | locked;
275   if (group_state)
276     effective |= (group_state->effective) << 13;
277 
278   _clutter_event_set_state_full (event, button, base, latched, locked, effective);
279 }
280 
281 void
clutter_input_device_xi2_update_tool(ClutterInputDevice * device,ClutterInputDeviceTool * tool)282 clutter_input_device_xi2_update_tool (ClutterInputDevice     *device,
283                                       ClutterInputDeviceTool *tool)
284 {
285   ClutterInputDeviceXI2 *device_xi2 = CLUTTER_INPUT_DEVICE_XI2 (device);
286   g_set_object (&device_xi2->current_tool, tool);
287 }
288 
289 ClutterInputDeviceTool *
clutter_input_device_xi2_get_current_tool(ClutterInputDevice * device)290 clutter_input_device_xi2_get_current_tool (ClutterInputDevice *device)
291 {
292   ClutterInputDeviceXI2 *device_xi2 = CLUTTER_INPUT_DEVICE_XI2 (device);
293   return device_xi2->current_tool;
294 }
295 
296 #ifdef HAVE_LIBWACOM
297 void
clutter_input_device_xi2_ensure_wacom_info(ClutterInputDevice * device,WacomDeviceDatabase * wacom_db)298 clutter_input_device_xi2_ensure_wacom_info (ClutterInputDevice  *device,
299                                             WacomDeviceDatabase *wacom_db)
300 {
301   ClutterInputDeviceXI2 *device_xi2 = CLUTTER_INPUT_DEVICE_XI2 (device);
302   const gchar *node_path;
303 
304   node_path = clutter_input_device_get_device_node (device);
305   device_xi2->wacom_device = libwacom_new_from_path (wacom_db, node_path,
306                                                      WFALLBACK_NONE, NULL);
307 }
308 
309 guint
clutter_input_device_xi2_get_pad_group_mode(ClutterInputDevice * device,guint group)310 clutter_input_device_xi2_get_pad_group_mode (ClutterInputDevice *device,
311                                              guint               group)
312 {
313   ClutterInputDeviceXI2 *device_xi2 = CLUTTER_INPUT_DEVICE_XI2 (device);
314 
315   if (group >= device_xi2->group_modes->len)
316     return 0;
317 
318   return g_array_index (device_xi2->group_modes, guint, group);
319 }
320 
321 void
clutter_input_device_xi2_update_pad_state(ClutterInputDevice * device,guint button,guint state,guint * group,guint * mode)322 clutter_input_device_xi2_update_pad_state (ClutterInputDevice *device,
323                                            guint               button,
324                                            guint               state,
325                                            guint              *group,
326                                            guint              *mode)
327 {
328   ClutterInputDeviceXI2 *device_xi2 = CLUTTER_INPUT_DEVICE_XI2 (device);
329   guint button_group, *group_mode;
330   gboolean is_mode_switch = FALSE;
331 
332   button_group = clutter_input_device_xi2_get_button_group (device, button);
333   is_mode_switch = button_group >= 0;
334 
335   /* Assign all non-mode-switch buttons to group 0 so far */
336   button_group = MAX (0, button_group);
337 
338   if (button_group >= device_xi2->group_modes->len)
339     return;
340 
341   group_mode = &g_array_index (device_xi2->group_modes, guint, button_group);
342 
343   if (is_mode_switch && state)
344     {
345       guint next, n_modes;
346 
347       n_modes = clutter_input_device_get_group_n_modes (device, button_group);
348       next = (*group_mode + 1) % n_modes;
349       *group_mode = next;
350     }
351 
352   if (group)
353     *group = button_group;
354   if (mode)
355     *mode = *group_mode;
356 }
357 #endif
358