mutter/src/backends/x11/meta-input-device-x11.c
Carlos Garnacho 2c86bff0cc backends/x11: Drop clutter_event_set_state_full() helpers
Since the full decomposed modifier state is unused, and only the
effective modifier mask matters to users, the new constructors take
just this effective modifier mask. This	means this helper went
unused in the port to the new constructors, so can be now dropped.

Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/3153>
2023-08-08 04:02:54 +02:00

799 lines
22 KiB
C

/*
* Copyright © 2011 Intel Corp.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Author: Emmanuele Bassi <ebassi@linux.intel.com>
*/
#include "config.h"
#include <X11/extensions/XInput2.h>
#include "clutter/clutter-mutter.h"
#include "backends/x11/meta-backend-x11.h"
#include "backends/x11/meta-clutter-backend-x11.h"
#include "backends/x11/meta-input-device-x11.h"
#include "backends/x11/meta-seat-x11.h"
struct _MetaInputDeviceX11
{
ClutterInputDevice device;
int32_t device_id;
ClutterInputDeviceTool *current_tool;
int inhibit_pointer_query_timer;
gboolean query_status;
float current_x;
float current_y;
GArray *axes;
GArray *scroll_info;
#ifdef HAVE_LIBWACOM
GArray *group_modes;
#endif
};
typedef struct _MetaX11AxisInfo
{
ClutterInputAxis axis;
double min_axis;
double max_axis;
double min_value;
double max_value;
double resolution;
} MetaX11AxisInfo;
typedef struct _MetaX11ScrollInfo
{
guint axis_id;
ClutterScrollDirection direction;
double increment;
double last_value;
guint last_value_valid : 1;
} MetaX11ScrollInfo;
struct _MetaInputDeviceX11Class
{
ClutterInputDeviceClass device_class;
};
#define N_BUTTONS 5
enum
{
PROP_0,
PROP_ID,
N_PROPS
};
static GParamSpec *props[N_PROPS] = { 0 };
G_DEFINE_TYPE (MetaInputDeviceX11,
meta_input_device_x11,
META_TYPE_INPUT_DEVICE)
static void
meta_input_device_x11_constructed (GObject *object)
{
MetaInputDeviceX11 *device_xi2 = META_INPUT_DEVICE_X11 (object);
if (G_OBJECT_CLASS (meta_input_device_x11_parent_class)->constructed)
G_OBJECT_CLASS (meta_input_device_x11_parent_class)->constructed (object);
#ifdef HAVE_LIBWACOM
if (clutter_input_device_get_device_type (CLUTTER_INPUT_DEVICE (object)) == CLUTTER_PAD_DEVICE)
{
device_xi2->group_modes = g_array_new (FALSE, TRUE, sizeof (uint32_t));
g_array_set_size (device_xi2->group_modes,
clutter_input_device_get_n_mode_groups (CLUTTER_INPUT_DEVICE (object)));
}
#endif
}
static gboolean
meta_input_device_x11_is_grouped (ClutterInputDevice *device,
ClutterInputDevice *other_device)
{
#ifdef HAVE_LIBWACOM
WacomDevice *wacom_device, *other_wacom_device;
wacom_device =
meta_input_device_get_wacom_device (META_INPUT_DEVICE (device));
other_wacom_device =
meta_input_device_get_wacom_device (META_INPUT_DEVICE (other_device));
if (wacom_device && other_wacom_device &&
libwacom_compare (wacom_device,
other_wacom_device,
WCOMPARE_NORMAL) == 0)
return TRUE;
#endif
if (clutter_input_device_get_vendor_id (device) &&
clutter_input_device_get_product_id (device) &&
clutter_input_device_get_vendor_id (other_device) &&
clutter_input_device_get_product_id (other_device))
{
if (strcmp (clutter_input_device_get_vendor_id (device),
clutter_input_device_get_vendor_id (other_device)) == 0 &&
strcmp (clutter_input_device_get_product_id (device),
clutter_input_device_get_product_id (other_device)) == 0)
return TRUE;
}
return FALSE;
}
static void
meta_input_device_x11_finalize (GObject *object)
{
MetaInputDeviceX11 *device_xi2 = META_INPUT_DEVICE_X11 (object);
g_clear_pointer (&device_xi2->axes, g_array_unref);
g_clear_pointer (&device_xi2->scroll_info, g_array_unref);
#ifdef HAVE_LIBWACOM
if (device_xi2->group_modes)
g_array_unref (device_xi2->group_modes);
#endif
g_clear_handle_id (&device_xi2->inhibit_pointer_query_timer, g_source_remove);
G_OBJECT_CLASS (meta_input_device_x11_parent_class)->finalize (object);
}
static void
meta_input_device_x11_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
MetaInputDeviceX11 *device_x11 = META_INPUT_DEVICE_X11 (object);
switch (prop_id)
{
case PROP_ID:
device_x11->device_id = g_value_get_int (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
meta_input_device_x11_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
MetaInputDeviceX11 *device_x11 = META_INPUT_DEVICE_X11 (object);
switch (prop_id)
{
case PROP_ID:
g_value_set_int (value, device_x11->device_id);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static int
meta_input_device_x11_get_group_n_modes (ClutterInputDevice *device,
int group)
{
#ifdef HAVE_LIBWACOM
WacomDevice *wacom_device;
wacom_device = meta_input_device_get_wacom_device (META_INPUT_DEVICE (device));
if (wacom_device)
{
if (group == 0)
{
if (libwacom_has_ring (wacom_device))
return libwacom_get_ring_num_modes (wacom_device);
else if (libwacom_get_num_strips (wacom_device) >= 1)
return libwacom_get_strips_num_modes (wacom_device);
}
else if (group == 1)
{
if (libwacom_has_ring2 (wacom_device))
return libwacom_get_ring2_num_modes (wacom_device);
else if (libwacom_get_num_strips (wacom_device) >= 2)
return libwacom_get_strips_num_modes (wacom_device);
}
}
#endif
return -1;
}
#ifdef HAVE_LIBWACOM
static int
meta_input_device_x11_get_button_group (ClutterInputDevice *device,
uint32_t button)
{
WacomDevice *wacom_device;
wacom_device = meta_input_device_get_wacom_device (META_INPUT_DEVICE (device));
if (wacom_device)
{
WacomButtonFlags flags;
if (button >= libwacom_get_num_buttons (wacom_device))
return -1;
flags = libwacom_get_button_flag (wacom_device, 'A' + button);
if (flags &
(WACOM_BUTTON_RING_MODESWITCH |
WACOM_BUTTON_TOUCHSTRIP_MODESWITCH))
return 0;
if (flags &
(WACOM_BUTTON_RING2_MODESWITCH |
WACOM_BUTTON_TOUCHSTRIP2_MODESWITCH))
return 1;
}
return -1;
}
#endif
static gboolean
meta_input_device_x11_is_mode_switch_button (ClutterInputDevice *device,
uint32_t group,
uint32_t button)
{
int button_group = -1;
#ifdef HAVE_LIBWACOM
button_group = meta_input_device_x11_get_button_group (device, button);
#endif
return button_group == (int) group;
}
static gboolean
meta_input_device_x11_get_dimensions (ClutterInputDevice *device,
unsigned int *width,
unsigned int *height)
{
MetaInputDeviceX11 *device_x11 = META_INPUT_DEVICE_X11 (device);
ClutterSeat *seat = clutter_input_device_get_seat (device);
MetaSeatX11 *seat_x11 = META_SEAT_X11 (seat);
MetaBackendX11 *backend_x11 =
META_BACKEND_X11 (meta_seat_x11_get_backend (seat_x11));
Display *xdisplay =
meta_backend_x11_get_xdisplay (backend_x11);
XIDeviceInfo *info;
uint *value, w, h;
int i, n_info;
static gboolean atoms_initialized = FALSE;
static Atom abs_axis_atoms[4] = { 0, };
meta_clutter_x11_trap_x_errors ();
info = XIQueryDevice (xdisplay, device_x11->device_id, &n_info);
*width = *height = w = h = 0;
if (meta_clutter_x11_untrap_x_errors ())
return FALSE;
if (!info)
return FALSE;
if (G_UNLIKELY (!atoms_initialized))
{
const char *abs_axis_atom_names[4] = {
"Abs X",
"Abs MT Position X",
"Abs Y",
"Abs MT Position Y",
};
XInternAtoms (xdisplay,
(char **) abs_axis_atom_names,
G_N_ELEMENTS (abs_axis_atom_names),
False,
abs_axis_atoms);
atoms_initialized = TRUE;
}
for (i = 0; i < info->num_classes; i++)
{
XIValuatorClassInfo *valuator_info;
if (info->classes[i]->type != XIValuatorClass)
continue;
valuator_info = (XIValuatorClassInfo *) info->classes[i];
if (valuator_info->label == abs_axis_atoms[0] || /* Abs X */
valuator_info->label == abs_axis_atoms[1]) /* Abs MT X */
value = &w;
else if (valuator_info->label == abs_axis_atoms[2] || /* Abs Y */
valuator_info->label == abs_axis_atoms[3]) /* Abs MT Y */
value = &h;
else
continue;
*value = (valuator_info->max - valuator_info->min) * 1000 / valuator_info->resolution;
}
*width = w;
*height = h;
XIFreeDeviceInfo (info);
return (w != 0 && h != 0);
}
static void
meta_input_device_x11_class_init (MetaInputDeviceX11Class *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
ClutterInputDeviceClass *device_class = CLUTTER_INPUT_DEVICE_CLASS (klass);
gobject_class->constructed = meta_input_device_x11_constructed;
gobject_class->finalize = meta_input_device_x11_finalize;
gobject_class->set_property = meta_input_device_x11_set_property;
gobject_class->get_property = meta_input_device_x11_get_property;
device_class->is_grouped = meta_input_device_x11_is_grouped;
device_class->get_group_n_modes = meta_input_device_x11_get_group_n_modes;
device_class->is_mode_switch_button = meta_input_device_x11_is_mode_switch_button;
device_class->get_dimensions = meta_input_device_x11_get_dimensions;
props[PROP_ID] =
g_param_spec_int ("id", NULL, NULL,
-1, G_MAXINT,
0,
CLUTTER_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY);
g_object_class_install_properties (gobject_class, N_PROPS, props);
}
static void
meta_input_device_x11_init (MetaInputDeviceX11 *self)
{
}
void
meta_input_device_x11_update_tool (ClutterInputDevice *device,
ClutterInputDeviceTool *tool)
{
MetaInputDeviceX11 *device_xi2 = META_INPUT_DEVICE_X11 (device);
g_set_object (&device_xi2->current_tool, tool);
}
ClutterInputDeviceTool *
meta_input_device_x11_get_current_tool (ClutterInputDevice *device)
{
MetaInputDeviceX11 *device_xi2 = META_INPUT_DEVICE_X11 (device);
return device_xi2->current_tool;
}
static gboolean
meta_input_device_x11_query_pointer_location (MetaInputDeviceX11 *device_xi2)
{
ClutterInputDevice *device = CLUTTER_INPUT_DEVICE (device_xi2);
ClutterSeat *seat = clutter_input_device_get_seat (device);
MetaSeatX11 *seat_x11 = META_SEAT_X11 (seat);
MetaBackendX11 *backend_x11 =
META_BACKEND_X11 (meta_seat_x11_get_backend (seat_x11));
Window xroot_window, xchild_window;
double xroot_x, xroot_y, xwin_x, xwin_y;
XIButtonState button_state = { 0 };
XIModifierState mod_state;
XIGroupState group_state;
int result;
meta_clutter_x11_trap_x_errors ();
result = XIQueryPointer (meta_backend_x11_get_xdisplay (backend_x11),
device_xi2->device_id,
meta_backend_x11_get_root_xwindow (backend_x11),
&xroot_window,
&xchild_window,
&xroot_x, &xroot_y,
&xwin_x, &xwin_y,
&button_state,
&mod_state,
&group_state);
meta_clutter_x11_untrap_x_errors ();
g_free (button_state.mask);
if (!result)
return FALSE;
device_xi2->current_x = (float) xroot_x;
device_xi2->current_y = (float) xroot_y;
return TRUE;
}
static gboolean
clear_inhibit_pointer_query_cb (gpointer data)
{
MetaInputDeviceX11 *device_xi2 = META_INPUT_DEVICE_X11 (data);
device_xi2->inhibit_pointer_query_timer = 0;
return G_SOURCE_REMOVE;
}
gboolean
meta_input_device_x11_get_pointer_location (ClutterInputDevice *device,
float *x,
float *y)
{
MetaInputDeviceX11 *device_xi2 = META_INPUT_DEVICE_X11 (device);
g_return_val_if_fail (META_IS_INPUT_DEVICE_X11 (device), FALSE);
g_return_val_if_fail (clutter_input_device_get_device_type (device) ==
CLUTTER_POINTER_DEVICE, FALSE);
/* Throttle XServer queries and roundtrips using an idle timeout */
if (device_xi2->inhibit_pointer_query_timer == 0)
{
device_xi2->query_status =
meta_input_device_x11_query_pointer_location (device_xi2);
device_xi2->inhibit_pointer_query_timer =
clutter_threads_add_idle (clear_inhibit_pointer_query_cb, device_xi2);
}
*x = device_xi2->current_x;
*y = device_xi2->current_y;
return device_xi2->query_status;
}
int
meta_input_device_x11_get_device_id (ClutterInputDevice *device)
{
MetaInputDeviceX11 *device_xi2 = META_INPUT_DEVICE_X11 (device);
g_return_val_if_fail (META_IS_INPUT_DEVICE_X11 (device), 0);
return device_xi2->device_id;
}
void
meta_input_device_x11_reset_axes (ClutterInputDevice *device)
{
MetaInputDeviceX11 *device_x11 = META_INPUT_DEVICE_X11 (device);
g_clear_pointer (&device_x11->axes, g_array_unref);
}
int
meta_input_device_x11_add_axis (ClutterInputDevice *device,
ClutterInputAxis axis,
double minimum,
double maximum,
double resolution)
{
MetaInputDeviceX11 *device_x11 = META_INPUT_DEVICE_X11 (device);
MetaX11AxisInfo info;
guint pos;
if (device_x11->axes == NULL)
device_x11->axes = g_array_new (FALSE, TRUE, sizeof (MetaX11AxisInfo));
info.axis = axis;
info.min_value = minimum;
info.max_value = maximum;
info.resolution = resolution;
switch (axis)
{
case CLUTTER_INPUT_AXIS_X:
case CLUTTER_INPUT_AXIS_Y:
info.min_axis = 0;
info.max_axis = 0;
break;
case CLUTTER_INPUT_AXIS_XTILT:
case CLUTTER_INPUT_AXIS_YTILT:
info.min_axis = -1;
info.max_axis = 1;
break;
default:
info.min_axis = 0;
info.max_axis = 1;
break;
}
g_array_append_val (device_x11->axes, info);
pos = device_x11->axes->len - 1;
return pos;
}
gboolean
meta_input_device_x11_get_axis (ClutterInputDevice *device,
int idx,
ClutterInputAxis *use)
{
MetaInputDeviceX11 *device_x11 = META_INPUT_DEVICE_X11 (device);
MetaX11AxisInfo *info;
if (device_x11->axes == NULL)
return FALSE;
if (idx < 0 || idx >= device_x11->axes->len)
return FALSE;
info = &g_array_index (device_x11->axes, MetaX11AxisInfo, idx);
if (use)
*use = info->axis;
return TRUE;
}
gboolean
meta_input_device_x11_translate_axis (ClutterInputDevice *device,
int idx,
double value,
double *axis_value)
{
MetaInputDeviceX11 *device_x11 = META_INPUT_DEVICE_X11 (device);
MetaX11AxisInfo *info;
double width;
double real_value;
if (device_x11->axes == NULL || idx < 0 || idx >= device_x11->axes->len)
return FALSE;
info = &g_array_index (device_x11->axes, MetaX11AxisInfo, idx);
if (info->axis == CLUTTER_INPUT_AXIS_X ||
info->axis == CLUTTER_INPUT_AXIS_Y)
return FALSE;
if (fabs (info->max_value - info->min_value) < 0.0000001)
return FALSE;
width = info->max_value - info->min_value;
real_value = (info->max_axis * (value - info->min_value)
+ info->min_axis * (info->max_value - value))
/ width;
if (axis_value)
*axis_value = real_value;
return TRUE;
}
int
meta_input_device_x11_get_n_axes (ClutterInputDevice *device)
{
MetaInputDeviceX11 *device_x11 = META_INPUT_DEVICE_X11 (device);
return device_x11->axes->len;
}
void
meta_input_device_x11_add_scroll_info (ClutterInputDevice *device,
int idx,
ClutterScrollDirection direction,
double increment)
{
MetaInputDeviceX11 *device_x11 = META_INPUT_DEVICE_X11 (device);
MetaX11ScrollInfo info;
g_return_if_fail (CLUTTER_IS_INPUT_DEVICE (device));
info.axis_id = idx;
info.direction = direction;
info.increment = increment;
info.last_value_valid = FALSE;
if (device_x11->scroll_info == NULL)
{
device_x11->scroll_info = g_array_new (FALSE,
FALSE,
sizeof (MetaX11ScrollInfo));
}
g_array_append_val (device_x11->scroll_info, info);
}
gboolean
meta_input_device_x11_get_scroll_delta (ClutterInputDevice *device,
int idx,
double value,
ClutterScrollDirection *direction_p,
double *delta_p)
{
MetaInputDeviceX11 *device_x11 = META_INPUT_DEVICE_X11 (device);
int i;
if (device_x11->scroll_info == NULL)
return FALSE;
for (i = 0; i < device_x11->scroll_info->len; i++)
{
MetaX11ScrollInfo *info = &g_array_index (device_x11->scroll_info,
MetaX11ScrollInfo,
i);
if (info->axis_id == idx)
{
if (direction_p != NULL)
*direction_p = info->direction;
if (delta_p != NULL)
*delta_p = 0.0;
if (info->last_value_valid)
{
if (delta_p != NULL)
{
*delta_p = (value - info->last_value)
/ info->increment;
}
info->last_value = value;
}
else
{
info->last_value = value;
info->last_value_valid = TRUE;
}
return TRUE;
}
}
return FALSE;
}
void
meta_input_device_x11_reset_scroll_info (ClutterInputDevice *device)
{
MetaInputDeviceX11 *device_x11 = META_INPUT_DEVICE_X11 (device);
int i;
if (device_x11->scroll_info == NULL)
return;
for (i = 0; i < device_x11->scroll_info->len; i++)
{
MetaX11ScrollInfo *info = &g_array_index (device_x11->scroll_info,
MetaX11ScrollInfo,
i);
info->last_value_valid = FALSE;
}
}
#ifdef HAVE_LIBWACOM
uint32_t
meta_input_device_x11_get_pad_group_mode (ClutterInputDevice *device,
uint32_t group)
{
MetaInputDeviceX11 *device_xi2 = META_INPUT_DEVICE_X11 (device);
if (group >= device_xi2->group_modes->len)
return 0;
return g_array_index (device_xi2->group_modes, uint32_t, group);
}
static gboolean
pad_switch_mode (ClutterInputDevice *device,
uint32_t button,
uint32_t group,
uint32_t *mode)
{
MetaInputDeviceX11 *device_x11 = META_INPUT_DEVICE_X11 (device);
uint32_t n_buttons, n_modes, button_group, next_mode, i;
WacomDevice *wacom_device;
GList *switch_buttons = NULL;
wacom_device =
meta_input_device_get_wacom_device (META_INPUT_DEVICE (device));
n_buttons = libwacom_get_num_buttons (wacom_device);
for (i = 0; i < n_buttons; i++)
{
button_group = meta_input_device_x11_get_button_group (device, i);
if (button_group == group)
switch_buttons = g_list_prepend (switch_buttons, GINT_TO_POINTER (button));
}
switch_buttons = g_list_reverse (switch_buttons);
n_modes = clutter_input_device_get_group_n_modes (device, group);
if (g_list_length (switch_buttons) > 1)
{
/* If there's multiple switch buttons, we don't toggle but assign a mode
* to each of those buttons.
*/
next_mode = g_list_index (switch_buttons, GINT_TO_POINTER (button));
}
else if (switch_buttons)
{
uint32_t cur_mode;
/* If there is a single button, have it toggle across modes */
cur_mode = g_array_index (device_x11->group_modes, uint32_t, group);
next_mode = (cur_mode + 1) % n_modes;
}
else
{
return FALSE;
}
g_list_free (switch_buttons);
if (next_mode < 0 || next_mode > n_modes)
return FALSE;
*mode = next_mode;
return TRUE;
}
void
meta_input_device_x11_update_pad_state (ClutterInputDevice *device,
uint32_t button,
uint32_t state,
uint32_t *group,
uint32_t *mode)
{
MetaInputDeviceX11 *device_xi2 = META_INPUT_DEVICE_X11 (device);
uint32_t button_group, *group_mode;
button_group = meta_input_device_x11_get_button_group (device, button);
if (button_group < 0 || button_group >= device_xi2->group_modes->len)
{
if (group)
*group = 0;
if (mode)
*mode = 0;
return;
}
group_mode = &g_array_index (device_xi2->group_modes, uint32_t, button_group);
if (state)
{
uint32_t next_mode;
if (pad_switch_mode (device, button, button_group, &next_mode))
*group_mode = next_mode;
}
if (group)
*group = button_group;
if (mode)
*mode = *group_mode;
}
#endif