
This is a bit scattered around, with the setter/getter in Clutter, and it only being only directly honored in Wayland (it goes straight through device properties in X11). Make this private native API, and out of public ClutterInputDevice API. https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1486
967 lines
32 KiB
C
967 lines
32 KiB
C
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
|
|
|
/*
|
|
* Copyright (C) 2014 Red Hat
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License as
|
|
* published by the Free Software Foundation; either version 2 of the
|
|
* License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
|
* 02111-1307, USA.
|
|
*
|
|
* Author: Carlos Garnacho <carlosg@gnome.org>
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include "backends/x11/meta-input-settings-x11.h"
|
|
|
|
#include <gdk/gdkx.h>
|
|
#include <string.h>
|
|
#include <X11/Xatom.h>
|
|
#include <X11/extensions/XInput2.h>
|
|
#include <X11/XKBlib.h>
|
|
|
|
#ifdef HAVE_LIBGUDEV
|
|
#include <gudev/gudev.h>
|
|
#endif
|
|
|
|
#include "backends/meta-logical-monitor.h"
|
|
#include "backends/x11/meta-backend-x11.h"
|
|
#include "core/display-private.h"
|
|
#include "meta/meta-x11-errors.h"
|
|
|
|
typedef struct _MetaInputSettingsX11Private
|
|
{
|
|
#ifdef HAVE_LIBGUDEV
|
|
GUdevClient *udev_client;
|
|
#endif
|
|
} MetaInputSettingsX11Private;
|
|
|
|
G_DEFINE_TYPE_WITH_PRIVATE (MetaInputSettingsX11, meta_input_settings_x11,
|
|
META_TYPE_INPUT_SETTINGS)
|
|
|
|
typedef enum
|
|
{
|
|
SCROLL_METHOD_FIELD_2FG,
|
|
SCROLL_METHOD_FIELD_EDGE,
|
|
SCROLL_METHOD_FIELD_BUTTON,
|
|
SCROLL_METHOD_NUM_FIELDS
|
|
} ScrollMethod;
|
|
|
|
static void
|
|
device_free_xdevice (gpointer user_data)
|
|
{
|
|
MetaDisplay *display = meta_get_display ();
|
|
MetaBackend *backend = meta_get_backend ();
|
|
Display *xdisplay = meta_backend_x11_get_xdisplay (META_BACKEND_X11 (backend));
|
|
XDevice *xdev = user_data;
|
|
|
|
meta_x11_error_trap_push (display->x11_display);
|
|
XCloseDevice (xdisplay, xdev);
|
|
meta_x11_error_trap_pop (display->x11_display);
|
|
}
|
|
|
|
static XDevice *
|
|
device_ensure_xdevice (ClutterInputDevice *device)
|
|
{
|
|
MetaDisplay *display = meta_get_display ();
|
|
MetaBackend *backend = meta_get_backend ();
|
|
Display *xdisplay = meta_backend_x11_get_xdisplay (META_BACKEND_X11 (backend));
|
|
int device_id = clutter_input_device_get_device_id (device);
|
|
XDevice *xdev = NULL;
|
|
|
|
xdev = g_object_get_data (G_OBJECT (device), "meta-input-settings-xdevice");
|
|
if (xdev)
|
|
return xdev;
|
|
|
|
meta_x11_error_trap_push (display->x11_display);
|
|
xdev = XOpenDevice (xdisplay, device_id);
|
|
meta_x11_error_trap_pop (display->x11_display);
|
|
|
|
if (xdev)
|
|
{
|
|
g_object_set_data_full (G_OBJECT (device),
|
|
"meta-input-settings-xdevice",
|
|
xdev, device_free_xdevice);
|
|
}
|
|
|
|
return xdev;
|
|
}
|
|
|
|
static void *
|
|
get_property (ClutterInputDevice *device,
|
|
const gchar *property,
|
|
Atom type,
|
|
int format,
|
|
gulong nitems)
|
|
{
|
|
MetaBackend *backend = meta_get_backend ();
|
|
Display *xdisplay = meta_backend_x11_get_xdisplay (META_BACKEND_X11 (backend));
|
|
gulong nitems_ret, bytes_after_ret;
|
|
int rc, device_id, format_ret;
|
|
Atom property_atom, type_ret;
|
|
guchar *data_ret = NULL;
|
|
|
|
property_atom = XInternAtom (xdisplay, property, True);
|
|
if (!property_atom)
|
|
return NULL;
|
|
|
|
device_id = clutter_input_device_get_device_id (device);
|
|
|
|
clutter_x11_trap_x_errors ();
|
|
rc = XIGetProperty (xdisplay, device_id, property_atom,
|
|
0, 10, False, type, &type_ret, &format_ret,
|
|
&nitems_ret, &bytes_after_ret, &data_ret);
|
|
clutter_x11_untrap_x_errors ();
|
|
|
|
if (rc == Success && type_ret == type && format_ret == format && nitems_ret >= nitems)
|
|
{
|
|
if (nitems_ret > nitems)
|
|
g_warning ("Property '%s' for device '%s' returned %lu items, expected %lu",
|
|
property, clutter_input_device_get_device_name (device), nitems_ret, nitems);
|
|
return data_ret;
|
|
}
|
|
|
|
meta_XFree (data_ret);
|
|
return NULL;
|
|
}
|
|
|
|
static void
|
|
change_property (ClutterInputDevice *device,
|
|
const gchar *property,
|
|
Atom type,
|
|
int format,
|
|
void *data,
|
|
gulong nitems)
|
|
{
|
|
MetaBackend *backend = meta_get_backend ();
|
|
Display *xdisplay = meta_backend_x11_get_xdisplay (META_BACKEND_X11 (backend));
|
|
int device_id;
|
|
Atom property_atom;
|
|
guchar *data_ret;
|
|
|
|
property_atom = XInternAtom (xdisplay, property, True);
|
|
if (!property_atom)
|
|
return;
|
|
|
|
device_id = clutter_input_device_get_device_id (device);
|
|
|
|
data_ret = get_property (device, property, type, format, nitems);
|
|
if (!data_ret)
|
|
return;
|
|
|
|
XIChangeProperty (xdisplay, device_id, property_atom, type,
|
|
format, XIPropModeReplace, data, nitems);
|
|
meta_XFree (data_ret);
|
|
}
|
|
|
|
static void
|
|
meta_input_settings_x11_set_send_events (MetaInputSettings *settings,
|
|
ClutterInputDevice *device,
|
|
GDesktopDeviceSendEvents mode)
|
|
{
|
|
guchar values[2] = { 0 }; /* disabled, disabled-on-external-mouse */
|
|
guchar *available;
|
|
|
|
available = get_property (device, "libinput Send Events Modes Available",
|
|
XA_INTEGER, 8, 2);
|
|
if (!available)
|
|
return;
|
|
|
|
switch (mode)
|
|
{
|
|
case G_DESKTOP_DEVICE_SEND_EVENTS_DISABLED:
|
|
values[0] = 1;
|
|
break;
|
|
case G_DESKTOP_DEVICE_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE:
|
|
values[1] = 1;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if ((values[0] && !available[0]) || (values[1] && !available[1]))
|
|
g_warning ("Device '%s' does not support sendevents mode %d",
|
|
clutter_input_device_get_device_name (device), mode);
|
|
else
|
|
change_property (device, "libinput Send Events Mode Enabled",
|
|
XA_INTEGER, 8, &values, 2);
|
|
|
|
meta_XFree (available);
|
|
}
|
|
|
|
static void
|
|
meta_input_settings_x11_set_matrix (MetaInputSettings *settings,
|
|
ClutterInputDevice *device,
|
|
gfloat matrix[6])
|
|
{
|
|
MetaBackend *backend = meta_get_backend ();
|
|
Display *xdisplay = meta_backend_x11_get_xdisplay (META_BACKEND_X11 (backend));
|
|
gfloat full_matrix[9] = { matrix[0], matrix[1], matrix[2],
|
|
matrix[3], matrix[4], matrix[5],
|
|
0, 0, 1 };
|
|
|
|
change_property (device, "Coordinate Transformation Matrix",
|
|
XInternAtom (xdisplay, "FLOAT", False),
|
|
32, &full_matrix, 9);
|
|
}
|
|
|
|
static void
|
|
meta_input_settings_x11_set_speed (MetaInputSettings *settings,
|
|
ClutterInputDevice *device,
|
|
gdouble speed)
|
|
{
|
|
MetaBackend *backend = meta_get_backend ();
|
|
Display *xdisplay = meta_backend_x11_get_xdisplay (META_BACKEND_X11 (backend));
|
|
gfloat value = speed;
|
|
|
|
change_property (device, "libinput Accel Speed",
|
|
XInternAtom (xdisplay, "FLOAT", False),
|
|
32, &value, 1);
|
|
}
|
|
|
|
static void
|
|
meta_input_settings_x11_set_left_handed (MetaInputSettings *settings,
|
|
ClutterInputDevice *device,
|
|
gboolean enabled)
|
|
{
|
|
ClutterInputDeviceType device_type;
|
|
guchar value;
|
|
|
|
device_type = clutter_input_device_get_device_type (device);
|
|
|
|
if (device_type == CLUTTER_TABLET_DEVICE ||
|
|
device_type == CLUTTER_PEN_DEVICE ||
|
|
device_type == CLUTTER_ERASER_DEVICE)
|
|
{
|
|
value = enabled ? 3 : 0;
|
|
change_property (device, "Wacom Rotation",
|
|
XA_INTEGER, 8, &value, 1);
|
|
}
|
|
else
|
|
{
|
|
value = enabled ? 1 : 0;
|
|
change_property (device, "libinput Left Handed Enabled",
|
|
XA_INTEGER, 8, &value, 1);
|
|
}
|
|
}
|
|
|
|
static void
|
|
meta_input_settings_x11_set_disable_while_typing (MetaInputSettings *settings,
|
|
ClutterInputDevice *device,
|
|
gboolean enabled)
|
|
{
|
|
guchar value = (enabled) ? 1 : 0;
|
|
|
|
change_property (device, "libinput Disable While Typing Enabled",
|
|
XA_INTEGER, 8, &value, 1);
|
|
}
|
|
|
|
static void
|
|
meta_input_settings_x11_set_tap_enabled (MetaInputSettings *settings,
|
|
ClutterInputDevice *device,
|
|
gboolean enabled)
|
|
{
|
|
guchar value = (enabled) ? 1 : 0;
|
|
|
|
change_property (device, "libinput Tapping Enabled",
|
|
XA_INTEGER, 8, &value, 1);
|
|
}
|
|
|
|
static void
|
|
meta_input_settings_x11_set_tap_and_drag_enabled (MetaInputSettings *settings,
|
|
ClutterInputDevice *device,
|
|
gboolean enabled)
|
|
{
|
|
guchar value = (enabled) ? 1 : 0;
|
|
|
|
change_property (device, "libinput Tapping Drag Enabled",
|
|
XA_INTEGER, 8, &value, 1);
|
|
}
|
|
|
|
static void
|
|
meta_input_settings_x11_set_tap_and_drag_lock_enabled (MetaInputSettings *settings,
|
|
ClutterInputDevice *device,
|
|
gboolean enabled)
|
|
{
|
|
guchar value = (enabled) ? 1 : 0;
|
|
|
|
change_property (device, "libinput Tapping Drag Lock Enabled",
|
|
XA_INTEGER, 8, &value, 1);
|
|
}
|
|
|
|
static void
|
|
meta_input_settings_x11_set_invert_scroll (MetaInputSettings *settings,
|
|
ClutterInputDevice *device,
|
|
gboolean inverted)
|
|
{
|
|
guchar value = (inverted) ? 1 : 0;
|
|
|
|
change_property (device, "libinput Natural Scrolling Enabled",
|
|
XA_INTEGER, 8, &value, 1);
|
|
}
|
|
|
|
static void
|
|
change_scroll_method (ClutterInputDevice *device,
|
|
ScrollMethod method,
|
|
gboolean enabled)
|
|
{
|
|
guchar values[SCROLL_METHOD_NUM_FIELDS] = { 0 }; /* 2fg, edge, button. The last value is unused */
|
|
guchar *current = NULL;
|
|
guchar *available = NULL;
|
|
|
|
available = get_property (device, "libinput Scroll Methods Available",
|
|
XA_INTEGER, 8, SCROLL_METHOD_NUM_FIELDS);
|
|
if (!available || !available[method])
|
|
goto out;
|
|
|
|
current = get_property (device, "libinput Scroll Method Enabled",
|
|
XA_INTEGER, 8, SCROLL_METHOD_NUM_FIELDS);
|
|
if (!current)
|
|
goto out;
|
|
|
|
memcpy (values, current, SCROLL_METHOD_NUM_FIELDS);
|
|
|
|
values[method] = !!enabled;
|
|
change_property (device, "libinput Scroll Method Enabled",
|
|
XA_INTEGER, 8, &values, SCROLL_METHOD_NUM_FIELDS);
|
|
out:
|
|
meta_XFree (current);
|
|
meta_XFree (available);
|
|
}
|
|
|
|
static void
|
|
meta_input_settings_x11_set_edge_scroll (MetaInputSettings *settings,
|
|
ClutterInputDevice *device,
|
|
gboolean edge_scroll_enabled)
|
|
{
|
|
change_scroll_method (device, SCROLL_METHOD_FIELD_EDGE, edge_scroll_enabled);
|
|
}
|
|
|
|
static void
|
|
meta_input_settings_x11_set_two_finger_scroll (MetaInputSettings *settings,
|
|
ClutterInputDevice *device,
|
|
gboolean two_finger_scroll_enabled)
|
|
{
|
|
change_scroll_method (device, SCROLL_METHOD_FIELD_2FG, two_finger_scroll_enabled);
|
|
}
|
|
|
|
static gboolean
|
|
meta_input_settings_x11_has_two_finger_scroll (MetaInputSettings *settings,
|
|
ClutterInputDevice *device)
|
|
{
|
|
guchar *available = NULL;
|
|
gboolean has_two_finger = TRUE;
|
|
|
|
available = get_property (device, "libinput Scroll Methods Available",
|
|
XA_INTEGER, 8, SCROLL_METHOD_NUM_FIELDS);
|
|
if (!available || !available[SCROLL_METHOD_FIELD_2FG])
|
|
has_two_finger = FALSE;
|
|
|
|
meta_XFree (available);
|
|
return has_two_finger;
|
|
}
|
|
|
|
static void
|
|
meta_input_settings_x11_set_scroll_button (MetaInputSettings *settings,
|
|
ClutterInputDevice *device,
|
|
guint button,
|
|
gboolean button_lock)
|
|
{
|
|
gchar lock = button_lock;
|
|
|
|
change_scroll_method (device, SCROLL_METHOD_FIELD_BUTTON, button != 0);
|
|
change_property (device, "libinput Button Scrolling Button",
|
|
XA_CARDINAL, 32, &button, 1);
|
|
change_property (device, "libinput Button Scrolling Button Lock Enabled",
|
|
XA_INTEGER, 8, &lock, 1);
|
|
}
|
|
|
|
static void
|
|
meta_input_settings_x11_set_click_method (MetaInputSettings *settings,
|
|
ClutterInputDevice *device,
|
|
GDesktopTouchpadClickMethod mode)
|
|
{
|
|
guchar values[2] = { 0 }; /* buttonareas, clickfinger */
|
|
guchar *defaults, *available;
|
|
|
|
available = get_property (device, "libinput Click Methods Available",
|
|
XA_INTEGER, 8, 2);
|
|
if (!available)
|
|
return;
|
|
|
|
switch (mode)
|
|
{
|
|
case G_DESKTOP_TOUCHPAD_CLICK_METHOD_DEFAULT:
|
|
defaults = get_property (device, "libinput Click Method Enabled Default",
|
|
XA_INTEGER, 8, 2);
|
|
if (!defaults)
|
|
break;
|
|
memcpy (values, defaults, 2);
|
|
meta_XFree (defaults);
|
|
break;
|
|
case G_DESKTOP_TOUCHPAD_CLICK_METHOD_NONE:
|
|
break;
|
|
case G_DESKTOP_TOUCHPAD_CLICK_METHOD_AREAS:
|
|
values[0] = 1;
|
|
break;
|
|
case G_DESKTOP_TOUCHPAD_CLICK_METHOD_FINGERS:
|
|
values[1] = 1;
|
|
break;
|
|
default:
|
|
g_assert_not_reached ();
|
|
return;
|
|
}
|
|
|
|
if ((values[0] && !available[0]) || (values[1] && !available[1]))
|
|
g_warning ("Device '%s' does not support click method %d",
|
|
clutter_input_device_get_device_name (device), mode);
|
|
else
|
|
change_property (device, "libinput Click Method Enabled",
|
|
XA_INTEGER, 8, &values, 2);
|
|
|
|
meta_XFree(available);
|
|
}
|
|
|
|
static void
|
|
meta_input_settings_x11_set_tap_button_map (MetaInputSettings *settings,
|
|
ClutterInputDevice *device,
|
|
GDesktopTouchpadTapButtonMap mode)
|
|
{
|
|
guchar values[2] = { 0 }; /* lrm, lmr */
|
|
guchar *defaults;
|
|
|
|
switch (mode)
|
|
{
|
|
case G_DESKTOP_TOUCHPAD_BUTTON_TAP_MAP_DEFAULT:
|
|
defaults = get_property (device, "libinput Tapping Button Mapping Default",
|
|
XA_INTEGER, 8, 2);
|
|
if (!defaults)
|
|
break;
|
|
memcpy (values, defaults, 2);
|
|
meta_XFree (defaults);
|
|
break;
|
|
case G_DESKTOP_TOUCHPAD_BUTTON_TAP_MAP_LRM:
|
|
values[0] = 1;
|
|
break;
|
|
case G_DESKTOP_TOUCHPAD_BUTTON_TAP_MAP_LMR:
|
|
values[1] = 1;
|
|
break;
|
|
default:
|
|
g_assert_not_reached ();
|
|
return;
|
|
}
|
|
|
|
if (values[0] || values[1])
|
|
change_property (device, "libinput Tapping Button Mapping Enabled",
|
|
XA_INTEGER, 8, &values, 2);
|
|
}
|
|
|
|
static void
|
|
meta_input_settings_x11_set_keyboard_repeat (MetaInputSettings *settings,
|
|
gboolean enabled,
|
|
guint delay,
|
|
guint interval)
|
|
{
|
|
MetaBackend *backend = meta_get_backend ();
|
|
Display *xdisplay = meta_backend_x11_get_xdisplay (META_BACKEND_X11 (backend));
|
|
|
|
if (enabled)
|
|
{
|
|
XAutoRepeatOn (xdisplay);
|
|
XkbSetAutoRepeatRate (xdisplay, XkbUseCoreKbd, delay, interval);
|
|
}
|
|
else
|
|
{
|
|
XAutoRepeatOff (xdisplay);
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
has_udev_property (MetaInputSettings *settings,
|
|
ClutterInputDevice *device,
|
|
const char *property_name)
|
|
{
|
|
#ifdef HAVE_LIBGUDEV
|
|
MetaInputSettingsX11 *settings_x11 = META_INPUT_SETTINGS_X11 (settings);
|
|
MetaInputSettingsX11Private *priv =
|
|
meta_input_settings_x11_get_instance_private (settings_x11);
|
|
const char *device_node;
|
|
GUdevDevice *udev_device = NULL;
|
|
GUdevDevice *parent_udev_device = NULL;
|
|
|
|
device_node = clutter_input_device_get_device_node (device);
|
|
if (!device_node)
|
|
return FALSE;
|
|
|
|
udev_device = g_udev_client_query_by_device_file (priv->udev_client,
|
|
device_node);
|
|
if (!udev_device)
|
|
return FALSE;
|
|
|
|
if (NULL != g_udev_device_get_property (udev_device, property_name))
|
|
{
|
|
g_object_unref (udev_device);
|
|
return TRUE;
|
|
}
|
|
|
|
parent_udev_device = g_udev_device_get_parent (udev_device);
|
|
g_object_unref (udev_device);
|
|
|
|
if (!parent_udev_device)
|
|
return FALSE;
|
|
|
|
if (NULL != g_udev_device_get_property (parent_udev_device, property_name))
|
|
{
|
|
g_object_unref (parent_udev_device);
|
|
return TRUE;
|
|
}
|
|
|
|
g_object_unref (parent_udev_device);
|
|
return FALSE;
|
|
#else
|
|
static gboolean warned_once = FALSE;
|
|
|
|
if (!warned_once)
|
|
{
|
|
g_warning ("Failed to query property: no udev support");
|
|
warned_once = TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
#endif
|
|
}
|
|
|
|
static gboolean
|
|
is_mouse (MetaInputSettings *settings,
|
|
ClutterInputDevice *device)
|
|
{
|
|
return (has_udev_property (settings, device, "ID_INPUT_MOUSE") &&
|
|
!has_udev_property (settings, device, "ID_INPUT_POINTINGSTICK"));
|
|
}
|
|
|
|
static gboolean
|
|
meta_input_settings_x11_is_touchpad_device (MetaInputSettings *settings,
|
|
ClutterInputDevice *device)
|
|
{
|
|
return has_udev_property (settings, device, "ID_INPUT_TOUCHPAD");
|
|
}
|
|
|
|
static gboolean
|
|
meta_input_settings_x11_is_trackball_device (MetaInputSettings *settings,
|
|
ClutterInputDevice *device)
|
|
{
|
|
return has_udev_property (settings, device, "ID_INPUT_TRACKBALL");
|
|
}
|
|
|
|
static void
|
|
set_device_accel_profile (ClutterInputDevice *device,
|
|
GDesktopPointerAccelProfile profile)
|
|
{
|
|
guchar *defaults, *available;
|
|
guchar values[2] = { 0 }; /* adaptive, flat */
|
|
|
|
defaults = get_property (device, "libinput Accel Profile Enabled Default",
|
|
XA_INTEGER, 8, 2);
|
|
if (!defaults)
|
|
return;
|
|
|
|
available = get_property (device, "libinput Accel Profiles Available",
|
|
XA_INTEGER, 8, 2);
|
|
if (!available)
|
|
goto err_available;
|
|
|
|
switch (profile)
|
|
{
|
|
case G_DESKTOP_POINTER_ACCEL_PROFILE_FLAT:
|
|
values[0] = 0;
|
|
values[1] = 1;
|
|
break;
|
|
case G_DESKTOP_POINTER_ACCEL_PROFILE_ADAPTIVE:
|
|
values[0] = 1;
|
|
values[1] = 0;
|
|
break;
|
|
default:
|
|
g_warn_if_reached ();
|
|
case G_DESKTOP_POINTER_ACCEL_PROFILE_DEFAULT:
|
|
values[0] = defaults[0];
|
|
values[1] = defaults[1];
|
|
break;
|
|
}
|
|
|
|
change_property (device, "libinput Accel Profile Enabled",
|
|
XA_INTEGER, 8, &values, 2);
|
|
|
|
meta_XFree (available);
|
|
|
|
err_available:
|
|
meta_XFree (defaults);
|
|
}
|
|
|
|
static void
|
|
meta_input_settings_x11_set_mouse_accel_profile (MetaInputSettings *settings,
|
|
ClutterInputDevice *device,
|
|
GDesktopPointerAccelProfile profile)
|
|
{
|
|
if (!is_mouse (settings, device))
|
|
return;
|
|
|
|
set_device_accel_profile (device, profile);
|
|
}
|
|
|
|
static void
|
|
meta_input_settings_x11_set_trackball_accel_profile (MetaInputSettings *settings,
|
|
ClutterInputDevice *device,
|
|
GDesktopPointerAccelProfile profile)
|
|
{
|
|
if (!meta_input_settings_x11_is_trackball_device (settings, device))
|
|
return;
|
|
|
|
set_device_accel_profile (device, profile);
|
|
}
|
|
|
|
static void
|
|
meta_input_settings_x11_set_tablet_mapping (MetaInputSettings *settings,
|
|
ClutterInputDevice *device,
|
|
GDesktopTabletMapping mapping)
|
|
{
|
|
MetaDisplay *display = meta_get_display ();
|
|
MetaBackend *backend = meta_get_backend ();
|
|
Display *xdisplay = meta_backend_x11_get_xdisplay (META_BACKEND_X11 (backend));
|
|
XDevice *xdev;
|
|
|
|
if (!display)
|
|
return;
|
|
|
|
/* Grab the puke bucket! */
|
|
meta_x11_error_trap_push (display->x11_display);
|
|
xdev = device_ensure_xdevice (device);
|
|
if (xdev)
|
|
{
|
|
XSetDeviceMode (xdisplay, xdev,
|
|
mapping == G_DESKTOP_TABLET_MAPPING_ABSOLUTE ?
|
|
Absolute : Relative);
|
|
}
|
|
|
|
if (meta_x11_error_trap_pop_with_return (display->x11_display))
|
|
{
|
|
g_warning ("Could not set tablet mapping for %s",
|
|
clutter_input_device_get_device_name (device));
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
device_query_area (ClutterInputDevice *device,
|
|
gint *x,
|
|
gint *y,
|
|
gint *width,
|
|
gint *height)
|
|
{
|
|
MetaBackend *backend = meta_get_backend ();
|
|
Display *xdisplay = meta_backend_x11_get_xdisplay (META_BACKEND_X11 (backend));
|
|
gint device_id, n_devices, i;
|
|
XIDeviceInfo *info;
|
|
Atom abs_x, abs_y;
|
|
|
|
*width = *height = 0;
|
|
device_id = clutter_input_device_get_device_id (device);
|
|
info = XIQueryDevice (xdisplay, device_id, &n_devices);
|
|
if (n_devices <= 0 || !info)
|
|
return FALSE;
|
|
|
|
abs_x = XInternAtom (xdisplay, "Abs X", True);
|
|
abs_y = XInternAtom (xdisplay, "Abs Y", True);
|
|
|
|
for (i = 0; i < info->num_classes; i++)
|
|
{
|
|
XIValuatorClassInfo *valuator = (XIValuatorClassInfo *) info->classes[i];
|
|
|
|
if (valuator->type != XIValuatorClass)
|
|
continue;
|
|
if (valuator->label == abs_x)
|
|
{
|
|
*x = valuator->min;
|
|
*width = valuator->max - valuator->min;
|
|
}
|
|
else if (valuator->label == abs_y)
|
|
{
|
|
*y = valuator->min;
|
|
*height = valuator->max - valuator->min;
|
|
}
|
|
}
|
|
|
|
XIFreeDeviceInfo (info);
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
update_tablet_area (MetaInputSettings *settings,
|
|
ClutterInputDevice *device,
|
|
gint32 *area)
|
|
{
|
|
change_property (device, "Wacom Tablet Area",
|
|
XA_INTEGER, 32, area, 4);
|
|
}
|
|
|
|
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)
|
|
{
|
|
gint32 x, y, width, height, area[4] = { 0 };
|
|
|
|
if (!device_query_area (device, &x, &y, &width, &height))
|
|
return;
|
|
|
|
area[0] = (width * padding_left) + x;
|
|
area[1] = (height * padding_top) + y;
|
|
area[2] = width - (width * padding_right) + x;
|
|
area[3] = height - (height * padding_bottom) + y;
|
|
update_tablet_area (settings, device, area);
|
|
}
|
|
|
|
static void
|
|
meta_input_settings_x11_set_tablet_keep_aspect (MetaInputSettings *settings,
|
|
ClutterInputDevice *device,
|
|
MetaLogicalMonitor *logical_monitor,
|
|
gboolean keep_aspect)
|
|
{
|
|
gint32 width, height, dev_x, dev_y, dev_width, dev_height, area[4] = { 0 };
|
|
|
|
if (!device_query_area (device, &dev_x, &dev_y, &dev_width, &dev_height))
|
|
return;
|
|
|
|
if (keep_aspect)
|
|
{
|
|
double aspect_ratio, dev_aspect;
|
|
|
|
if (logical_monitor)
|
|
{
|
|
width = logical_monitor->rect.width;
|
|
height = logical_monitor->rect.height;
|
|
}
|
|
else
|
|
{
|
|
MetaMonitorManager *monitor_manager;
|
|
MetaBackend *backend;
|
|
|
|
backend = meta_get_backend ();
|
|
monitor_manager = meta_backend_get_monitor_manager (backend);
|
|
meta_monitor_manager_get_screen_size (monitor_manager,
|
|
&width, &height);
|
|
}
|
|
|
|
aspect_ratio = (double) width / height;
|
|
dev_aspect = (double) dev_width / dev_height;
|
|
|
|
if (dev_aspect > aspect_ratio)
|
|
dev_width = dev_height * aspect_ratio;
|
|
else if (dev_aspect < aspect_ratio)
|
|
dev_height = dev_width / aspect_ratio;
|
|
}
|
|
|
|
area[0] = dev_x;
|
|
area[1] = dev_y;
|
|
area[2] = dev_width + dev_x;
|
|
area[3] = dev_height + dev_y;
|
|
update_tablet_area (settings, device, area);
|
|
}
|
|
|
|
static void
|
|
meta_input_settings_x11_dispose (GObject *object)
|
|
{
|
|
#ifdef HAVE_LIBGUDEV
|
|
MetaInputSettingsX11 *settings_x11 = META_INPUT_SETTINGS_X11 (object);
|
|
MetaInputSettingsX11Private *priv =
|
|
meta_input_settings_x11_get_instance_private (settings_x11);
|
|
|
|
g_clear_object (&priv->udev_client);
|
|
#endif
|
|
|
|
G_OBJECT_CLASS (meta_input_settings_x11_parent_class)->dispose (object);
|
|
}
|
|
|
|
static guint
|
|
action_to_button (GDesktopStylusButtonAction action,
|
|
guint button)
|
|
{
|
|
switch (action)
|
|
{
|
|
case G_DESKTOP_STYLUS_BUTTON_ACTION_MIDDLE:
|
|
return CLUTTER_BUTTON_MIDDLE;
|
|
case G_DESKTOP_STYLUS_BUTTON_ACTION_RIGHT:
|
|
return CLUTTER_BUTTON_SECONDARY;
|
|
case G_DESKTOP_STYLUS_BUTTON_ACTION_BACK:
|
|
return 8;
|
|
case G_DESKTOP_STYLUS_BUTTON_ACTION_FORWARD:
|
|
return 9;
|
|
case G_DESKTOP_STYLUS_BUTTON_ACTION_DEFAULT:
|
|
default:
|
|
return button;
|
|
}
|
|
}
|
|
|
|
static void
|
|
meta_input_settings_x11_set_stylus_button_map (MetaInputSettings *settings,
|
|
ClutterInputDevice *device,
|
|
ClutterInputDeviceTool *tool,
|
|
GDesktopStylusButtonAction primary,
|
|
GDesktopStylusButtonAction secondary,
|
|
GDesktopStylusButtonAction tertiary)
|
|
{
|
|
MetaDisplay *display = meta_get_display ();
|
|
MetaBackend *backend = meta_get_backend ();
|
|
Display *xdisplay = meta_backend_x11_get_xdisplay (META_BACKEND_X11 (backend));
|
|
XDevice *xdev;
|
|
|
|
if (!display)
|
|
return;
|
|
|
|
/* Grab the puke bucket! */
|
|
meta_x11_error_trap_push (display->x11_display);
|
|
xdev = device_ensure_xdevice (device);
|
|
if (xdev)
|
|
{
|
|
guchar map[8] = {
|
|
CLUTTER_BUTTON_PRIMARY,
|
|
action_to_button (primary, CLUTTER_BUTTON_MIDDLE),
|
|
action_to_button (secondary, CLUTTER_BUTTON_SECONDARY),
|
|
4,
|
|
5,
|
|
6,
|
|
7,
|
|
action_to_button (tertiary, 8), /* "Back" */
|
|
};
|
|
|
|
XSetDeviceButtonMapping (xdisplay, xdev, map, G_N_ELEMENTS (map));
|
|
}
|
|
|
|
if (meta_x11_error_trap_pop_with_return (display->x11_display))
|
|
{
|
|
g_warning ("Could not set stylus button map for %s",
|
|
clutter_input_device_get_device_name (device));
|
|
}
|
|
}
|
|
|
|
static void
|
|
meta_input_settings_x11_set_mouse_middle_click_emulation (MetaInputSettings *settings,
|
|
ClutterInputDevice *device,
|
|
gboolean enabled)
|
|
{
|
|
guchar value = enabled ? 1 : 0;
|
|
|
|
if (!is_mouse (settings, device))
|
|
return;
|
|
|
|
change_property (device, "libinput Middle Click Emulation Enabled",
|
|
XA_INTEGER, 8, &value, 1);
|
|
}
|
|
|
|
static void
|
|
meta_input_settings_x11_set_touchpad_middle_click_emulation (MetaInputSettings *settings,
|
|
ClutterInputDevice *device,
|
|
gboolean enabled)
|
|
{
|
|
guchar value = enabled ? 1 : 0;
|
|
|
|
if (!meta_input_settings_x11_is_touchpad_device (settings, device))
|
|
return;
|
|
|
|
change_property (device, "libinput Middle Click Emulation Enabled",
|
|
XA_INTEGER, 8, &value, 1);
|
|
}
|
|
|
|
static void
|
|
meta_input_settings_x11_set_trackball_middle_click_emulation (MetaInputSettings *settings,
|
|
ClutterInputDevice *device,
|
|
gboolean enabled)
|
|
{
|
|
guchar value = enabled ? 1 : 0;
|
|
|
|
if (!meta_input_settings_x11_is_trackball_device (settings, device))
|
|
return;
|
|
|
|
change_property (device, "libinput Middle Click Emulation Enabled",
|
|
XA_INTEGER, 8, &value, 1);
|
|
}
|
|
|
|
static void
|
|
meta_input_settings_x11_set_stylus_pressure (MetaInputSettings *settings,
|
|
ClutterInputDevice *device,
|
|
ClutterInputDeviceTool *tool,
|
|
const gint32 pressure[4])
|
|
{
|
|
guint32 values[4] = { pressure[0], pressure[1], pressure[2], pressure[3] };
|
|
|
|
change_property (device, "Wacom Pressurecurve", XA_INTEGER, 32,
|
|
&values, G_N_ELEMENTS (values));
|
|
}
|
|
|
|
static void
|
|
meta_input_settings_x11_class_init (MetaInputSettingsX11Class *klass)
|
|
{
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
MetaInputSettingsClass *input_settings_class = META_INPUT_SETTINGS_CLASS (klass);
|
|
|
|
object_class->dispose = meta_input_settings_x11_dispose;
|
|
|
|
input_settings_class->set_send_events = meta_input_settings_x11_set_send_events;
|
|
input_settings_class->set_matrix = meta_input_settings_x11_set_matrix;
|
|
input_settings_class->set_speed = meta_input_settings_x11_set_speed;
|
|
input_settings_class->set_left_handed = meta_input_settings_x11_set_left_handed;
|
|
input_settings_class->set_tap_enabled = meta_input_settings_x11_set_tap_enabled;
|
|
input_settings_class->set_tap_button_map = meta_input_settings_x11_set_tap_button_map;
|
|
input_settings_class->set_tap_and_drag_enabled = meta_input_settings_x11_set_tap_and_drag_enabled;
|
|
input_settings_class->set_tap_and_drag_lock_enabled =
|
|
meta_input_settings_x11_set_tap_and_drag_lock_enabled;
|
|
input_settings_class->set_disable_while_typing = meta_input_settings_x11_set_disable_while_typing;
|
|
input_settings_class->set_invert_scroll = meta_input_settings_x11_set_invert_scroll;
|
|
input_settings_class->set_edge_scroll = meta_input_settings_x11_set_edge_scroll;
|
|
input_settings_class->set_two_finger_scroll = meta_input_settings_x11_set_two_finger_scroll;
|
|
input_settings_class->set_scroll_button = meta_input_settings_x11_set_scroll_button;
|
|
input_settings_class->set_click_method = meta_input_settings_x11_set_click_method;
|
|
input_settings_class->set_keyboard_repeat = meta_input_settings_x11_set_keyboard_repeat;
|
|
|
|
input_settings_class->set_tablet_mapping = meta_input_settings_x11_set_tablet_mapping;
|
|
input_settings_class->set_tablet_keep_aspect = meta_input_settings_x11_set_tablet_keep_aspect;
|
|
input_settings_class->set_tablet_area = meta_input_settings_x11_set_tablet_area;
|
|
|
|
input_settings_class->set_mouse_accel_profile = meta_input_settings_x11_set_mouse_accel_profile;
|
|
input_settings_class->set_trackball_accel_profile = meta_input_settings_x11_set_trackball_accel_profile;
|
|
|
|
input_settings_class->set_stylus_pressure = meta_input_settings_x11_set_stylus_pressure;
|
|
input_settings_class->set_stylus_button_map = meta_input_settings_x11_set_stylus_button_map;
|
|
|
|
input_settings_class->set_mouse_middle_click_emulation = meta_input_settings_x11_set_mouse_middle_click_emulation;
|
|
input_settings_class->set_touchpad_middle_click_emulation = meta_input_settings_x11_set_touchpad_middle_click_emulation;
|
|
input_settings_class->set_trackball_middle_click_emulation = meta_input_settings_x11_set_trackball_middle_click_emulation;
|
|
|
|
input_settings_class->has_two_finger_scroll = meta_input_settings_x11_has_two_finger_scroll;
|
|
input_settings_class->is_trackball_device = meta_input_settings_x11_is_trackball_device;
|
|
}
|
|
|
|
static void
|
|
meta_input_settings_x11_init (MetaInputSettingsX11 *settings)
|
|
{
|
|
#ifdef HAVE_LIBGUDEV
|
|
MetaInputSettingsX11Private *priv =
|
|
meta_input_settings_x11_get_instance_private (settings);
|
|
const char *subsystems[] = { NULL };
|
|
|
|
priv->udev_client = g_udev_client_new (subsystems);
|
|
#endif
|
|
}
|