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