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