1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
2
3 /*
4 * Copyright (C) 2014 Red Hat
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation; either version 2 of the
9 * License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
19 * 02111-1307, USA.
20 *
21 * Author: Carlos Garnacho <carlosg@gnome.org>
22 */
23
24 #include "config.h"
25
26 #include "backends/x11/meta-input-settings-x11.h"
27
28 #include <gdk/gdkx.h>
29 #include <string.h>
30 #include <X11/Xatom.h>
31 #include <X11/extensions/XInput2.h>
32 #include <X11/XKBlib.h>
33
34 #ifdef HAVE_LIBGUDEV
35 #include <gudev/gudev.h>
36 #endif
37
38 #include "backends/x11/meta-backend-x11.h"
39 #include "backends/x11/meta-input-device-x11.h"
40 #include "core/display-private.h"
41 #include "meta/meta-x11-errors.h"
42
43 typedef struct _MetaInputSettingsX11Private
44 {
45 #ifdef HAVE_LIBGUDEV
46 GUdevClient *udev_client;
47 #endif
48 bool dummy_field; // struct needs at least one element to compile
49 } MetaInputSettingsX11Private;
50
51 G_DEFINE_TYPE_WITH_PRIVATE (MetaInputSettingsX11, meta_input_settings_x11,
52 META_TYPE_INPUT_SETTINGS)
53
54 typedef enum
55 {
56 SCROLL_METHOD_FIELD_2FG,
57 SCROLL_METHOD_FIELD_EDGE,
58 SCROLL_METHOD_FIELD_BUTTON,
59 SCROLL_METHOD_NUM_FIELDS
60 } ScrollMethod;
61
62 static void
device_free_xdevice(gpointer user_data)63 device_free_xdevice (gpointer user_data)
64 {
65 MetaDisplay *display = meta_get_display ();
66 MetaBackend *backend = meta_get_backend ();
67 Display *xdisplay = meta_backend_x11_get_xdisplay (META_BACKEND_X11 (backend));
68 XDevice *xdev = user_data;
69
70 meta_x11_error_trap_push (display->x11_display);
71 XCloseDevice (xdisplay, xdev);
72 meta_x11_error_trap_pop (display->x11_display);
73 }
74
75 static XDevice *
device_ensure_xdevice(ClutterInputDevice * device)76 device_ensure_xdevice (ClutterInputDevice *device)
77 {
78 MetaDisplay *display = meta_get_display ();
79 MetaBackend *backend = meta_get_backend ();
80 Display *xdisplay = meta_backend_x11_get_xdisplay (META_BACKEND_X11 (backend));
81 int device_id = meta_input_device_x11_get_device_id (device);
82 XDevice *xdev = NULL;
83
84 xdev = g_object_get_data (G_OBJECT (device), "meta-input-settings-xdevice");
85 if (xdev)
86 return xdev;
87
88 meta_x11_error_trap_push (display->x11_display);
89 xdev = XOpenDevice (xdisplay, device_id);
90 meta_x11_error_trap_pop (display->x11_display);
91
92 if (xdev)
93 {
94 g_object_set_data_full (G_OBJECT (device),
95 "meta-input-settings-xdevice",
96 xdev, device_free_xdevice);
97 }
98
99 return xdev;
100 }
101
102 static void *
get_property(ClutterInputDevice * device,const gchar * property,Atom type,int format,gulong nitems)103 get_property (ClutterInputDevice *device,
104 const gchar *property,
105 Atom type,
106 int format,
107 gulong nitems)
108 {
109 MetaBackend *backend = meta_get_backend ();
110 Display *xdisplay = meta_backend_x11_get_xdisplay (META_BACKEND_X11 (backend));
111 gulong nitems_ret, bytes_after_ret;
112 int rc, device_id, format_ret;
113 Atom property_atom, type_ret;
114 guchar *data_ret = NULL;
115
116 property_atom = XInternAtom (xdisplay, property, True);
117 if (!property_atom)
118 return NULL;
119
120 device_id = meta_input_device_x11_get_device_id (device);
121
122 meta_clutter_x11_trap_x_errors ();
123 rc = XIGetProperty (xdisplay, device_id, property_atom,
124 0, 10, False, type, &type_ret, &format_ret,
125 &nitems_ret, &bytes_after_ret, &data_ret);
126 meta_clutter_x11_untrap_x_errors ();
127
128 if (rc == Success && type_ret == type && format_ret == format && nitems_ret >= nitems)
129 {
130 if (nitems_ret > nitems)
131 g_warning ("Property '%s' for device '%s' returned %lu items, expected %lu",
132 property, clutter_input_device_get_device_name (device), nitems_ret, nitems);
133 return data_ret;
134 }
135
136 meta_XFree (data_ret);
137 return NULL;
138 }
139
140 static void
change_property(ClutterInputDevice * device,const gchar * property,Atom type,int format,void * data,gulong nitems)141 change_property (ClutterInputDevice *device,
142 const gchar *property,
143 Atom type,
144 int format,
145 void *data,
146 gulong nitems)
147 {
148 MetaBackend *backend = meta_get_backend ();
149 Display *xdisplay = meta_backend_x11_get_xdisplay (META_BACKEND_X11 (backend));
150 int device_id;
151 Atom property_atom;
152 guchar *data_ret;
153
154 property_atom = XInternAtom (xdisplay, property, True);
155 if (!property_atom)
156 return;
157
158 device_id = meta_input_device_x11_get_device_id (device);
159
160 data_ret = get_property (device, property, type, format, nitems);
161 if (!data_ret)
162 return;
163
164 XIChangeProperty (xdisplay, device_id, property_atom, type,
165 format, XIPropModeReplace, data, nitems);
166 meta_XFree (data_ret);
167 }
168
169 static void
meta_input_settings_x11_set_send_events(MetaInputSettings * settings,ClutterInputDevice * device,GDesktopDeviceSendEvents mode)170 meta_input_settings_x11_set_send_events (MetaInputSettings *settings,
171 ClutterInputDevice *device,
172 GDesktopDeviceSendEvents mode)
173 {
174 guchar values[2] = { 0 }; /* disabled, disabled-on-external-mouse */
175 guchar *available;
176
177 available = get_property (device, "libinput Send Events Modes Available",
178 XA_INTEGER, 8, 2);
179 if (!available)
180 return;
181
182 switch (mode)
183 {
184 case G_DESKTOP_DEVICE_SEND_EVENTS_DISABLED:
185 values[0] = 1;
186 break;
187 case G_DESKTOP_DEVICE_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE:
188 values[1] = 1;
189 break;
190 default:
191 break;
192 }
193
194 if ((values[0] && !available[0]) || (values[1] && !available[1]))
195 g_warning ("Device '%s' does not support sendevents mode %d",
196 clutter_input_device_get_device_name (device), mode);
197 else
198 change_property (device, "libinput Send Events Mode Enabled",
199 XA_INTEGER, 8, &values, 2);
200
201 meta_XFree (available);
202 }
203
204 static void
meta_input_settings_x11_set_matrix(MetaInputSettings * settings,ClutterInputDevice * device,const float matrix[6])205 meta_input_settings_x11_set_matrix (MetaInputSettings *settings,
206 ClutterInputDevice *device,
207 const float matrix[6])
208 {
209 MetaBackend *backend = meta_get_backend ();
210 Display *xdisplay = meta_backend_x11_get_xdisplay (META_BACKEND_X11 (backend));
211 gfloat full_matrix[9] = { matrix[0], matrix[1], matrix[2],
212 matrix[3], matrix[4], matrix[5],
213 0, 0, 1 };
214
215 change_property (device, "Coordinate Transformation Matrix",
216 XInternAtom (xdisplay, "FLOAT", False),
217 32, &full_matrix, 9);
218 }
219
220 static void
meta_input_settings_x11_set_speed(MetaInputSettings * settings,ClutterInputDevice * device,gdouble speed)221 meta_input_settings_x11_set_speed (MetaInputSettings *settings,
222 ClutterInputDevice *device,
223 gdouble speed)
224 {
225 MetaBackend *backend = meta_get_backend ();
226 Display *xdisplay = meta_backend_x11_get_xdisplay (META_BACKEND_X11 (backend));
227 gfloat value = speed;
228
229 change_property (device, "libinput Accel Speed",
230 XInternAtom (xdisplay, "FLOAT", False),
231 32, &value, 1);
232 }
233
234 static void
meta_input_settings_x11_set_left_handed(MetaInputSettings * settings,ClutterInputDevice * device,gboolean enabled)235 meta_input_settings_x11_set_left_handed (MetaInputSettings *settings,
236 ClutterInputDevice *device,
237 gboolean enabled)
238 {
239 ClutterInputDeviceType device_type;
240 guchar value;
241
242 device_type = clutter_input_device_get_device_type (device);
243
244 if (device_type == CLUTTER_TABLET_DEVICE ||
245 device_type == CLUTTER_PEN_DEVICE ||
246 device_type == CLUTTER_ERASER_DEVICE)
247 {
248 value = enabled ? 3 : 0;
249 change_property (device, "Wacom Rotation",
250 XA_INTEGER, 8, &value, 1);
251 }
252 else
253 {
254 value = enabled ? 1 : 0;
255 change_property (device, "libinput Left Handed Enabled",
256 XA_INTEGER, 8, &value, 1);
257 }
258 }
259
260 static void
meta_input_settings_x11_set_disable_while_typing(MetaInputSettings * settings,ClutterInputDevice * device,gboolean enabled)261 meta_input_settings_x11_set_disable_while_typing (MetaInputSettings *settings,
262 ClutterInputDevice *device,
263 gboolean enabled)
264 {
265 guchar value = (enabled) ? 1 : 0;
266
267 change_property (device, "libinput Disable While Typing Enabled",
268 XA_INTEGER, 8, &value, 1);
269 }
270
271 static void
meta_input_settings_x11_set_tap_enabled(MetaInputSettings * settings,ClutterInputDevice * device,gboolean enabled)272 meta_input_settings_x11_set_tap_enabled (MetaInputSettings *settings,
273 ClutterInputDevice *device,
274 gboolean enabled)
275 {
276 guchar value = (enabled) ? 1 : 0;
277
278 change_property (device, "libinput Tapping Enabled",
279 XA_INTEGER, 8, &value, 1);
280 }
281
282 static void
meta_input_settings_x11_set_tap_and_drag_enabled(MetaInputSettings * settings,ClutterInputDevice * device,gboolean enabled)283 meta_input_settings_x11_set_tap_and_drag_enabled (MetaInputSettings *settings,
284 ClutterInputDevice *device,
285 gboolean enabled)
286 {
287 guchar value = (enabled) ? 1 : 0;
288
289 change_property (device, "libinput Tapping Drag Enabled",
290 XA_INTEGER, 8, &value, 1);
291 }
292
293 static void
meta_input_settings_x11_set_tap_and_drag_lock_enabled(MetaInputSettings * settings,ClutterInputDevice * device,gboolean enabled)294 meta_input_settings_x11_set_tap_and_drag_lock_enabled (MetaInputSettings *settings,
295 ClutterInputDevice *device,
296 gboolean enabled)
297 {
298 guchar value = (enabled) ? 1 : 0;
299
300 change_property (device, "libinput Tapping Drag Lock Enabled",
301 XA_INTEGER, 8, &value, 1);
302 }
303
304 static void
meta_input_settings_x11_set_invert_scroll(MetaInputSettings * settings,ClutterInputDevice * device,gboolean inverted)305 meta_input_settings_x11_set_invert_scroll (MetaInputSettings *settings,
306 ClutterInputDevice *device,
307 gboolean inverted)
308 {
309 guchar value = (inverted) ? 1 : 0;
310
311 change_property (device, "libinput Natural Scrolling Enabled",
312 XA_INTEGER, 8, &value, 1);
313 }
314
315 static void
change_scroll_method(ClutterInputDevice * device,ScrollMethod method,gboolean enabled)316 change_scroll_method (ClutterInputDevice *device,
317 ScrollMethod method,
318 gboolean enabled)
319 {
320 guchar values[SCROLL_METHOD_NUM_FIELDS] = { 0 }; /* 2fg, edge, button. The last value is unused */
321 guchar *current = NULL;
322 guchar *available = NULL;
323
324 available = get_property (device, "libinput Scroll Methods Available",
325 XA_INTEGER, 8, SCROLL_METHOD_NUM_FIELDS);
326 if (!available || !available[method])
327 goto out;
328
329 current = get_property (device, "libinput Scroll Method Enabled",
330 XA_INTEGER, 8, SCROLL_METHOD_NUM_FIELDS);
331 if (!current)
332 goto out;
333
334 memcpy (values, current, SCROLL_METHOD_NUM_FIELDS);
335
336 values[method] = !!enabled;
337 change_property (device, "libinput Scroll Method Enabled",
338 XA_INTEGER, 8, &values, SCROLL_METHOD_NUM_FIELDS);
339 out:
340 meta_XFree (current);
341 meta_XFree (available);
342 }
343
344 static void
meta_input_settings_x11_set_edge_scroll(MetaInputSettings * settings,ClutterInputDevice * device,gboolean edge_scroll_enabled)345 meta_input_settings_x11_set_edge_scroll (MetaInputSettings *settings,
346 ClutterInputDevice *device,
347 gboolean edge_scroll_enabled)
348 {
349 change_scroll_method (device, SCROLL_METHOD_FIELD_EDGE, edge_scroll_enabled);
350 }
351
352 static void
meta_input_settings_x11_set_two_finger_scroll(MetaInputSettings * settings,ClutterInputDevice * device,gboolean two_finger_scroll_enabled)353 meta_input_settings_x11_set_two_finger_scroll (MetaInputSettings *settings,
354 ClutterInputDevice *device,
355 gboolean two_finger_scroll_enabled)
356 {
357 change_scroll_method (device, SCROLL_METHOD_FIELD_2FG, two_finger_scroll_enabled);
358 }
359
360 static gboolean
meta_input_settings_x11_has_two_finger_scroll(MetaInputSettings * settings,ClutterInputDevice * device)361 meta_input_settings_x11_has_two_finger_scroll (MetaInputSettings *settings,
362 ClutterInputDevice *device)
363 {
364 guchar *available = NULL;
365 gboolean has_two_finger = TRUE;
366
367 available = get_property (device, "libinput Scroll Methods Available",
368 XA_INTEGER, 8, SCROLL_METHOD_NUM_FIELDS);
369 if (!available || !available[SCROLL_METHOD_FIELD_2FG])
370 has_two_finger = FALSE;
371
372 meta_XFree (available);
373 return has_two_finger;
374 }
375
376 static void
meta_input_settings_x11_set_scroll_button(MetaInputSettings * settings,ClutterInputDevice * device,guint button,gboolean button_lock)377 meta_input_settings_x11_set_scroll_button (MetaInputSettings *settings,
378 ClutterInputDevice *device,
379 guint button,
380 gboolean button_lock)
381 {
382 gchar lock = button_lock;
383
384 change_scroll_method (device, SCROLL_METHOD_FIELD_BUTTON, button != 0);
385 change_property (device, "libinput Button Scrolling Button",
386 XA_CARDINAL, 32, &button, 1);
387 change_property (device, "libinput Button Scrolling Button Lock Enabled",
388 XA_INTEGER, 8, &lock, 1);
389 }
390
391 static void
meta_input_settings_x11_set_click_method(MetaInputSettings * settings,ClutterInputDevice * device,GDesktopTouchpadClickMethod mode)392 meta_input_settings_x11_set_click_method (MetaInputSettings *settings,
393 ClutterInputDevice *device,
394 GDesktopTouchpadClickMethod mode)
395 {
396 guchar values[2] = { 0 }; /* buttonareas, clickfinger */
397 guchar *defaults, *available;
398
399 available = get_property (device, "libinput Click Methods Available",
400 XA_INTEGER, 8, 2);
401 if (!available)
402 return;
403
404 switch (mode)
405 {
406 case G_DESKTOP_TOUCHPAD_CLICK_METHOD_DEFAULT:
407 defaults = get_property (device, "libinput Click Method Enabled Default",
408 XA_INTEGER, 8, 2);
409 if (!defaults)
410 break;
411 memcpy (values, defaults, 2);
412 meta_XFree (defaults);
413 break;
414 case G_DESKTOP_TOUCHPAD_CLICK_METHOD_NONE:
415 break;
416 case G_DESKTOP_TOUCHPAD_CLICK_METHOD_AREAS:
417 values[0] = 1;
418 break;
419 case G_DESKTOP_TOUCHPAD_CLICK_METHOD_FINGERS:
420 values[1] = 1;
421 break;
422 default:
423 g_assert_not_reached ();
424 return;
425 }
426
427 if ((values[0] && !available[0]) || (values[1] && !available[1]))
428 g_warning ("Device '%s' does not support click method %d",
429 clutter_input_device_get_device_name (device), mode);
430 else
431 change_property (device, "libinput Click Method Enabled",
432 XA_INTEGER, 8, &values, 2);
433
434 meta_XFree(available);
435 }
436
437 static void
meta_input_settings_x11_set_tap_button_map(MetaInputSettings * settings,ClutterInputDevice * device,GDesktopTouchpadTapButtonMap mode)438 meta_input_settings_x11_set_tap_button_map (MetaInputSettings *settings,
439 ClutterInputDevice *device,
440 GDesktopTouchpadTapButtonMap mode)
441 {
442 guchar values[2] = { 0 }; /* lrm, lmr */
443 guchar *defaults;
444
445 switch (mode)
446 {
447 case G_DESKTOP_TOUCHPAD_BUTTON_TAP_MAP_DEFAULT:
448 defaults = get_property (device, "libinput Tapping Button Mapping Default",
449 XA_INTEGER, 8, 2);
450 if (!defaults)
451 break;
452 memcpy (values, defaults, 2);
453 meta_XFree (defaults);
454 break;
455 case G_DESKTOP_TOUCHPAD_BUTTON_TAP_MAP_LRM:
456 values[0] = 1;
457 break;
458 case G_DESKTOP_TOUCHPAD_BUTTON_TAP_MAP_LMR:
459 values[1] = 1;
460 break;
461 default:
462 g_assert_not_reached ();
463 return;
464 }
465
466 if (values[0] || values[1])
467 change_property (device, "libinput Tapping Button Mapping Enabled",
468 XA_INTEGER, 8, &values, 2);
469 }
470
471 static void
meta_input_settings_x11_set_keyboard_repeat(MetaInputSettings * settings,gboolean enabled,guint delay,guint interval)472 meta_input_settings_x11_set_keyboard_repeat (MetaInputSettings *settings,
473 gboolean enabled,
474 guint delay,
475 guint interval)
476 {
477 MetaBackend *backend = meta_get_backend ();
478 Display *xdisplay = meta_backend_x11_get_xdisplay (META_BACKEND_X11 (backend));
479
480 if (enabled)
481 {
482 XAutoRepeatOn (xdisplay);
483 XkbSetAutoRepeatRate (xdisplay, XkbUseCoreKbd, delay, interval);
484 }
485 else
486 {
487 XAutoRepeatOff (xdisplay);
488 }
489 }
490
491 static gboolean
has_udev_property(MetaInputSettings * settings,ClutterInputDevice * device,const char * property_name)492 has_udev_property (MetaInputSettings *settings,
493 ClutterInputDevice *device,
494 const char *property_name)
495 {
496 #ifdef HAVE_LIBGUDEV
497 MetaInputSettingsX11 *settings_x11 = META_INPUT_SETTINGS_X11 (settings);
498 MetaInputSettingsX11Private *priv =
499 meta_input_settings_x11_get_instance_private (settings_x11);
500 const char *device_node;
501 GUdevDevice *udev_device = NULL;
502 GUdevDevice *parent_udev_device = NULL;
503
504 device_node = clutter_input_device_get_device_node (device);
505 if (!device_node)
506 return FALSE;
507
508 udev_device = g_udev_client_query_by_device_file (priv->udev_client,
509 device_node);
510 if (!udev_device)
511 return FALSE;
512
513 if (NULL != g_udev_device_get_property (udev_device, property_name))
514 {
515 g_object_unref (udev_device);
516 return TRUE;
517 }
518
519 parent_udev_device = g_udev_device_get_parent (udev_device);
520 g_object_unref (udev_device);
521
522 if (!parent_udev_device)
523 return FALSE;
524
525 if (NULL != g_udev_device_get_property (parent_udev_device, property_name))
526 {
527 g_object_unref (parent_udev_device);
528 return TRUE;
529 }
530
531 g_object_unref (parent_udev_device);
532 return FALSE;
533 #else
534 static gboolean warned_once = FALSE;
535
536 if (!warned_once)
537 {
538 g_warning ("Failed to query property: no udev support");
539 warned_once = TRUE;
540 }
541
542 return FALSE;
543 #endif
544 }
545
546 static gboolean
is_mouse(MetaInputSettings * settings,ClutterInputDevice * device)547 is_mouse (MetaInputSettings *settings,
548 ClutterInputDevice *device)
549 {
550 return (has_udev_property (settings, device, "ID_INPUT_MOUSE") &&
551 !has_udev_property (settings, device, "ID_INPUT_POINTINGSTICK"));
552 }
553
554 static gboolean
meta_input_settings_x11_is_touchpad_device(MetaInputSettings * settings,ClutterInputDevice * device)555 meta_input_settings_x11_is_touchpad_device (MetaInputSettings *settings,
556 ClutterInputDevice *device)
557 {
558 return has_udev_property (settings, device, "ID_INPUT_TOUCHPAD");
559 }
560
561 static gboolean
meta_input_settings_x11_is_trackball_device(MetaInputSettings * settings,ClutterInputDevice * device)562 meta_input_settings_x11_is_trackball_device (MetaInputSettings *settings,
563 ClutterInputDevice *device)
564 {
565 return has_udev_property (settings, device, "ID_INPUT_TRACKBALL");
566 }
567
568 static void
set_device_accel_profile(ClutterInputDevice * device,GDesktopPointerAccelProfile profile)569 set_device_accel_profile (ClutterInputDevice *device,
570 GDesktopPointerAccelProfile profile)
571 {
572 guchar *defaults, *available;
573 guchar values[2] = { 0 }; /* adaptive, flat */
574
575 defaults = get_property (device, "libinput Accel Profile Enabled Default",
576 XA_INTEGER, 8, 2);
577 if (!defaults)
578 return;
579
580 available = get_property (device, "libinput Accel Profiles Available",
581 XA_INTEGER, 8, 2);
582 if (!available)
583 goto err_available;
584
585 switch (profile)
586 {
587 case G_DESKTOP_POINTER_ACCEL_PROFILE_FLAT:
588 values[0] = 0;
589 values[1] = 1;
590 break;
591 case G_DESKTOP_POINTER_ACCEL_PROFILE_ADAPTIVE:
592 values[0] = 1;
593 values[1] = 0;
594 break;
595 default:
596 g_warn_if_reached ();
597 case G_DESKTOP_POINTER_ACCEL_PROFILE_DEFAULT:
598 values[0] = defaults[0];
599 values[1] = defaults[1];
600 break;
601 }
602
603 change_property (device, "libinput Accel Profile Enabled",
604 XA_INTEGER, 8, &values, 2);
605
606 meta_XFree (available);
607
608 err_available:
609 meta_XFree (defaults);
610 }
611
612 static void
meta_input_settings_x11_set_mouse_accel_profile(MetaInputSettings * settings,ClutterInputDevice * device,GDesktopPointerAccelProfile profile)613 meta_input_settings_x11_set_mouse_accel_profile (MetaInputSettings *settings,
614 ClutterInputDevice *device,
615 GDesktopPointerAccelProfile profile)
616 {
617 if (!is_mouse (settings, device))
618 return;
619
620 set_device_accel_profile (device, profile);
621 }
622
623 static void
meta_input_settings_x11_set_trackball_accel_profile(MetaInputSettings * settings,ClutterInputDevice * device,GDesktopPointerAccelProfile profile)624 meta_input_settings_x11_set_trackball_accel_profile (MetaInputSettings *settings,
625 ClutterInputDevice *device,
626 GDesktopPointerAccelProfile profile)
627 {
628 if (!meta_input_settings_x11_is_trackball_device (settings, device))
629 return;
630
631 set_device_accel_profile (device, profile);
632 }
633
634 static void
meta_input_settings_x11_set_tablet_mapping(MetaInputSettings * settings,ClutterInputDevice * device,GDesktopTabletMapping mapping)635 meta_input_settings_x11_set_tablet_mapping (MetaInputSettings *settings,
636 ClutterInputDevice *device,
637 GDesktopTabletMapping mapping)
638 {
639 MetaDisplay *display = meta_get_display ();
640 MetaBackend *backend = meta_get_backend ();
641 Display *xdisplay = meta_backend_x11_get_xdisplay (META_BACKEND_X11 (backend));
642 XDevice *xdev;
643
644 if (!display)
645 return;
646
647 /* Grab the puke bucket! */
648 meta_x11_error_trap_push (display->x11_display);
649 xdev = device_ensure_xdevice (device);
650 if (xdev)
651 {
652 XSetDeviceMode (xdisplay, xdev,
653 mapping == G_DESKTOP_TABLET_MAPPING_ABSOLUTE ?
654 Absolute : Relative);
655 }
656
657 if (meta_x11_error_trap_pop_with_return (display->x11_display))
658 {
659 g_warning ("Could not set tablet mapping for %s",
660 clutter_input_device_get_device_name (device));
661 }
662 }
663
664 static gboolean
device_query_area(ClutterInputDevice * device,gint * x,gint * y,gint * width,gint * height)665 device_query_area (ClutterInputDevice *device,
666 gint *x,
667 gint *y,
668 gint *width,
669 gint *height)
670 {
671 MetaBackend *backend = meta_get_backend ();
672 Display *xdisplay = meta_backend_x11_get_xdisplay (META_BACKEND_X11 (backend));
673 gint device_id, n_devices, i;
674 XIDeviceInfo *info;
675 Atom abs_x, abs_y;
676
677 *width = *height = 0;
678 device_id = meta_input_device_x11_get_device_id (device);
679 info = XIQueryDevice (xdisplay, device_id, &n_devices);
680 if (n_devices <= 0 || !info)
681 return FALSE;
682
683 abs_x = XInternAtom (xdisplay, "Abs X", True);
684 abs_y = XInternAtom (xdisplay, "Abs Y", True);
685
686 for (i = 0; i < info->num_classes; i++)
687 {
688 XIValuatorClassInfo *valuator = (XIValuatorClassInfo *) info->classes[i];
689
690 if (valuator->type != XIValuatorClass)
691 continue;
692 if (valuator->label == abs_x)
693 {
694 *x = valuator->min;
695 *width = valuator->max - valuator->min;
696 }
697 else if (valuator->label == abs_y)
698 {
699 *y = valuator->min;
700 *height = valuator->max - valuator->min;
701 }
702 }
703
704 XIFreeDeviceInfo (info);
705 return TRUE;
706 }
707
708 static void
update_tablet_area(MetaInputSettings * settings,ClutterInputDevice * device,gint32 * area)709 update_tablet_area (MetaInputSettings *settings,
710 ClutterInputDevice *device,
711 gint32 *area)
712 {
713 change_property (device, "Wacom Tablet Area",
714 XA_INTEGER, 32, area, 4);
715 }
716
717 static void
meta_input_settings_x11_set_tablet_area(MetaInputSettings * settings,ClutterInputDevice * device,gdouble padding_left,gdouble padding_right,gdouble padding_top,gdouble padding_bottom)718 meta_input_settings_x11_set_tablet_area (MetaInputSettings *settings,
719 ClutterInputDevice *device,
720 gdouble padding_left,
721 gdouble padding_right,
722 gdouble padding_top,
723 gdouble padding_bottom)
724 {
725 gint32 x, y, width, height, area[4] = { 0 };
726
727 if (!device_query_area (device, &x, &y, &width, &height))
728 return;
729
730 area[0] = (width * padding_left) + x;
731 area[1] = (height * padding_top) + y;
732 area[2] = width - (width * padding_right) + x;
733 area[3] = height - (height * padding_bottom) + y;
734 update_tablet_area (settings, device, area);
735 }
736
737 static void
meta_input_settings_x11_set_tablet_aspect_ratio(MetaInputSettings * settings,ClutterInputDevice * device,gdouble aspect_ratio)738 meta_input_settings_x11_set_tablet_aspect_ratio (MetaInputSettings *settings,
739 ClutterInputDevice *device,
740 gdouble aspect_ratio)
741 {
742 int32_t dev_x, dev_y, dev_width, dev_height, area[4] = { 0 };
743
744 if (!device_query_area (device, &dev_x, &dev_y, &dev_width, &dev_height))
745 return;
746
747 if (aspect_ratio > 0)
748 {
749 double dev_aspect;
750
751 dev_aspect = (double) dev_width / dev_height;
752
753 if (dev_aspect > aspect_ratio)
754 dev_width = dev_height * aspect_ratio;
755 else if (dev_aspect < aspect_ratio)
756 dev_height = dev_width / aspect_ratio;
757 }
758
759 area[0] = dev_x;
760 area[1] = dev_y;
761 area[2] = dev_width + dev_x;
762 area[3] = dev_height + dev_y;
763 update_tablet_area (settings, device, area);
764 }
765
766 static void
meta_input_settings_x11_dispose(GObject * object)767 meta_input_settings_x11_dispose (GObject *object)
768 {
769 MetaInputSettingsX11 *settings_x11 = META_INPUT_SETTINGS_X11 (object);
770 MetaInputSettingsX11Private *priv =
771 meta_input_settings_x11_get_instance_private (settings_x11);
772 #ifdef HAVE_LIBGUDEV
773 g_clear_object (&priv->udev_client);
774 #endif
775
776 G_OBJECT_CLASS (meta_input_settings_x11_parent_class)->dispose (object);
777 }
778
779 static guint
action_to_button(GDesktopStylusButtonAction action,guint button)780 action_to_button (GDesktopStylusButtonAction action,
781 guint button)
782 {
783 switch (action)
784 {
785 case G_DESKTOP_STYLUS_BUTTON_ACTION_MIDDLE:
786 return CLUTTER_BUTTON_MIDDLE;
787 case G_DESKTOP_STYLUS_BUTTON_ACTION_RIGHT:
788 return CLUTTER_BUTTON_SECONDARY;
789 case G_DESKTOP_STYLUS_BUTTON_ACTION_BACK:
790 return 8;
791 case G_DESKTOP_STYLUS_BUTTON_ACTION_FORWARD:
792 return 9;
793 case G_DESKTOP_STYLUS_BUTTON_ACTION_DEFAULT:
794 default:
795 return button;
796 }
797 }
798
799 static void
meta_input_settings_x11_set_stylus_button_map(MetaInputSettings * settings,ClutterInputDevice * device,ClutterInputDeviceTool * tool,GDesktopStylusButtonAction primary,GDesktopStylusButtonAction secondary,GDesktopStylusButtonAction tertiary)800 meta_input_settings_x11_set_stylus_button_map (MetaInputSettings *settings,
801 ClutterInputDevice *device,
802 ClutterInputDeviceTool *tool,
803 GDesktopStylusButtonAction primary,
804 GDesktopStylusButtonAction secondary,
805 GDesktopStylusButtonAction tertiary)
806 {
807 MetaDisplay *display = meta_get_display ();
808 MetaBackend *backend = meta_get_backend ();
809 Display *xdisplay = meta_backend_x11_get_xdisplay (META_BACKEND_X11 (backend));
810 XDevice *xdev;
811
812 if (!display)
813 return;
814
815 /* Grab the puke bucket! */
816 meta_x11_error_trap_push (display->x11_display);
817 xdev = device_ensure_xdevice (device);
818 if (xdev)
819 {
820 guchar map[8] = {
821 CLUTTER_BUTTON_PRIMARY,
822 action_to_button (primary, CLUTTER_BUTTON_MIDDLE),
823 action_to_button (secondary, CLUTTER_BUTTON_SECONDARY),
824 4,
825 5,
826 6,
827 7,
828 action_to_button (tertiary, 8), /* "Back" */
829 };
830
831 XSetDeviceButtonMapping (xdisplay, xdev, map, G_N_ELEMENTS (map));
832 }
833
834 if (meta_x11_error_trap_pop_with_return (display->x11_display))
835 {
836 g_warning ("Could not set stylus button map for %s",
837 clutter_input_device_get_device_name (device));
838 }
839 }
840
841 static void
meta_input_settings_x11_set_mouse_middle_click_emulation(MetaInputSettings * settings,ClutterInputDevice * device,gboolean enabled)842 meta_input_settings_x11_set_mouse_middle_click_emulation (MetaInputSettings *settings,
843 ClutterInputDevice *device,
844 gboolean enabled)
845 {
846 guchar value = enabled ? 1 : 0;
847
848 if (!is_mouse (settings, device))
849 return;
850
851 change_property (device, "libinput Middle Emulation Enabled",
852 XA_INTEGER, 8, &value, 1);
853 }
854
855 static void
meta_input_settings_x11_set_touchpad_middle_click_emulation(MetaInputSettings * settings,ClutterInputDevice * device,gboolean enabled)856 meta_input_settings_x11_set_touchpad_middle_click_emulation (MetaInputSettings *settings,
857 ClutterInputDevice *device,
858 gboolean enabled)
859 {
860 guchar value = enabled ? 1 : 0;
861
862 if (!meta_input_settings_x11_is_touchpad_device (settings, device))
863 return;
864
865 change_property (device, "libinput Middle Emulation Enabled",
866 XA_INTEGER, 8, &value, 1);
867 }
868
869 static void
meta_input_settings_x11_set_trackball_middle_click_emulation(MetaInputSettings * settings,ClutterInputDevice * device,gboolean enabled)870 meta_input_settings_x11_set_trackball_middle_click_emulation (MetaInputSettings *settings,
871 ClutterInputDevice *device,
872 gboolean enabled)
873 {
874 guchar value = enabled ? 1 : 0;
875
876 if (!meta_input_settings_x11_is_trackball_device (settings, device))
877 return;
878
879 change_property (device, "libinput Middle Emulation Enabled",
880 XA_INTEGER, 8, &value, 1);
881 }
882
883 static void
meta_input_settings_x11_set_stylus_pressure(MetaInputSettings * settings,ClutterInputDevice * device,ClutterInputDeviceTool * tool,const gint32 pressure[4])884 meta_input_settings_x11_set_stylus_pressure (MetaInputSettings *settings,
885 ClutterInputDevice *device,
886 ClutterInputDeviceTool *tool,
887 const gint32 pressure[4])
888 {
889 guint32 values[4] = { pressure[0], pressure[1], pressure[2], pressure[3] };
890
891 change_property (device, "Wacom Pressurecurve", XA_INTEGER, 32,
892 &values, G_N_ELEMENTS (values));
893 }
894
895 static void
meta_input_settings_x11_class_init(MetaInputSettingsX11Class * klass)896 meta_input_settings_x11_class_init (MetaInputSettingsX11Class *klass)
897 {
898 GObjectClass *object_class = G_OBJECT_CLASS (klass);
899 MetaInputSettingsClass *input_settings_class = META_INPUT_SETTINGS_CLASS (klass);
900
901 object_class->dispose = meta_input_settings_x11_dispose;
902
903 input_settings_class->set_send_events = meta_input_settings_x11_set_send_events;
904 input_settings_class->set_matrix = meta_input_settings_x11_set_matrix;
905 input_settings_class->set_speed = meta_input_settings_x11_set_speed;
906 input_settings_class->set_left_handed = meta_input_settings_x11_set_left_handed;
907 input_settings_class->set_tap_enabled = meta_input_settings_x11_set_tap_enabled;
908 input_settings_class->set_tap_button_map = meta_input_settings_x11_set_tap_button_map;
909 input_settings_class->set_tap_and_drag_enabled = meta_input_settings_x11_set_tap_and_drag_enabled;
910 input_settings_class->set_tap_and_drag_lock_enabled =
911 meta_input_settings_x11_set_tap_and_drag_lock_enabled;
912 input_settings_class->set_disable_while_typing = meta_input_settings_x11_set_disable_while_typing;
913 input_settings_class->set_invert_scroll = meta_input_settings_x11_set_invert_scroll;
914 input_settings_class->set_edge_scroll = meta_input_settings_x11_set_edge_scroll;
915 input_settings_class->set_two_finger_scroll = meta_input_settings_x11_set_two_finger_scroll;
916 input_settings_class->set_scroll_button = meta_input_settings_x11_set_scroll_button;
917 input_settings_class->set_click_method = meta_input_settings_x11_set_click_method;
918 input_settings_class->set_keyboard_repeat = meta_input_settings_x11_set_keyboard_repeat;
919
920 input_settings_class->set_tablet_mapping = meta_input_settings_x11_set_tablet_mapping;
921 input_settings_class->set_tablet_aspect_ratio = meta_input_settings_x11_set_tablet_aspect_ratio;
922 input_settings_class->set_tablet_area = meta_input_settings_x11_set_tablet_area;
923
924 input_settings_class->set_mouse_accel_profile = meta_input_settings_x11_set_mouse_accel_profile;
925 input_settings_class->set_trackball_accel_profile = meta_input_settings_x11_set_trackball_accel_profile;
926
927 input_settings_class->set_stylus_pressure = meta_input_settings_x11_set_stylus_pressure;
928 input_settings_class->set_stylus_button_map = meta_input_settings_x11_set_stylus_button_map;
929
930 input_settings_class->set_mouse_middle_click_emulation = meta_input_settings_x11_set_mouse_middle_click_emulation;
931 input_settings_class->set_touchpad_middle_click_emulation = meta_input_settings_x11_set_touchpad_middle_click_emulation;
932 input_settings_class->set_trackball_middle_click_emulation = meta_input_settings_x11_set_trackball_middle_click_emulation;
933
934 input_settings_class->has_two_finger_scroll = meta_input_settings_x11_has_two_finger_scroll;
935 input_settings_class->is_trackball_device = meta_input_settings_x11_is_trackball_device;
936 }
937
938 static void
meta_input_settings_x11_init(MetaInputSettingsX11 * settings)939 meta_input_settings_x11_init (MetaInputSettingsX11 *settings)
940 {
941 MetaInputSettingsX11Private *priv =
942 meta_input_settings_x11_get_instance_private (settings);
943 const char *subsystems[] = { NULL };
944 #ifdef HAVE_LIBGUDEV
945 priv->udev_client = g_udev_client_new (subsystems);
946 #endif
947 }
948