mutter/clutter/clutter/x11/clutter-input-device-xi2.c
Carlos Garnacho df48b94889 clutter: Add API to retrieve the physical size of absolute devices
This will be used in upper layers to match abs input devices (touchscreens,
tablets) to the corresponding output.
2018-12-06 14:44:46 +00:00

409 lines
12 KiB
C

/*
* Clutter.
*
* An OpenGL based 'interactive canvas' library.
*
* 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 "clutter-build-config.h"
#include "clutter-input-device-xi2.h"
#include "clutter-debug.h"
#include "clutter-device-manager-private.h"
#include "clutter-event-private.h"
#include "clutter-private.h"
#include "clutter-stage-private.h"
#include "clutter-backend-x11.h"
#include "clutter-stage-x11.h"
#include <X11/extensions/XInput2.h>
typedef struct _ClutterInputDeviceClass ClutterInputDeviceXI2Class;
/* a specific XI2 input device */
struct _ClutterInputDeviceXI2
{
ClutterInputDevice device;
gint device_id;
ClutterInputDeviceTool *current_tool;
#ifdef HAVE_LIBWACOM
WacomDevice *wacom_device;
GArray *group_modes;
#endif
};
#define N_BUTTONS 5
#define clutter_input_device_xi2_get_type _clutter_input_device_xi2_get_type
G_DEFINE_TYPE (ClutterInputDeviceXI2,
clutter_input_device_xi2,
CLUTTER_TYPE_INPUT_DEVICE);
static void
clutter_input_device_xi2_constructed (GObject *gobject)
{
ClutterInputDeviceXI2 *device_xi2 = CLUTTER_INPUT_DEVICE_XI2 (gobject);
g_object_get (gobject, "id", &device_xi2->device_id, NULL);
if (G_OBJECT_CLASS (clutter_input_device_xi2_parent_class)->constructed)
G_OBJECT_CLASS (clutter_input_device_xi2_parent_class)->constructed (gobject);
#ifdef HAVE_LIBWACOM
if (clutter_input_device_get_device_type (CLUTTER_INPUT_DEVICE (gobject)) == CLUTTER_PAD_DEVICE)
{
device_xi2->group_modes = g_array_new (FALSE, TRUE, sizeof (guint));
g_array_set_size (device_xi2->group_modes,
clutter_input_device_get_n_mode_groups (CLUTTER_INPUT_DEVICE (gobject)));
}
#endif
}
static gboolean
clutter_input_device_xi2_keycode_to_evdev (ClutterInputDevice *device,
guint hardware_keycode,
guint *evdev_keycode)
{
/* When using evdev under X11 the hardware keycodes are the evdev
keycodes plus 8. I haven't been able to find any documentation to
know what the +8 is for. FIXME: This should probably verify that
X server is using evdev. */
*evdev_keycode = hardware_keycode - 8;
return TRUE;
}
static gboolean
clutter_input_device_xi2_is_grouped (ClutterInputDevice *device,
ClutterInputDevice *other_device)
{
return FALSE;
}
static void
clutter_input_device_xi2_finalize (GObject *object)
{
#ifdef HAVE_LIBWACOM
ClutterInputDeviceXI2 *device_xi2 = CLUTTER_INPUT_DEVICE_XI2 (object);
if (device_xi2->wacom_device)
libwacom_destroy (device_xi2->wacom_device);
if (device_xi2->group_modes)
g_array_unref (device_xi2->group_modes);
#endif
G_OBJECT_CLASS (clutter_input_device_xi2_parent_class)->finalize (object);
}
static gint
clutter_input_device_xi2_get_group_n_modes (ClutterInputDevice *device,
gint group)
{
#ifdef HAVE_LIBWACOM
ClutterInputDeviceXI2 *device_xi2 = CLUTTER_INPUT_DEVICE_XI2 (device);
if (device_xi2->wacom_device)
{
if (group == 0)
{
if (libwacom_has_ring (device_xi2->wacom_device))
return libwacom_get_ring_num_modes (device_xi2->wacom_device);
else if (libwacom_get_num_strips (device_xi2->wacom_device) >= 1)
return libwacom_get_strips_num_modes (device_xi2->wacom_device);
}
else if (group == 1)
{
if (libwacom_has_ring2 (device_xi2->wacom_device))
return libwacom_get_ring2_num_modes (device_xi2->wacom_device);
else if (libwacom_get_num_strips (device_xi2->wacom_device) >= 2)
return libwacom_get_strips_num_modes (device_xi2->wacom_device);
}
}
#endif
return -1;
}
#ifdef HAVE_LIBWACOM
static int
clutter_input_device_xi2_get_button_group (ClutterInputDevice *device,
guint button)
{
ClutterInputDeviceXI2 *device_xi2 = CLUTTER_INPUT_DEVICE_XI2 (device);
if (device_xi2->wacom_device)
{
if (button >= libwacom_get_num_buttons (device_xi2->wacom_device))
return -1;
return libwacom_get_button_led_group (device_xi2->wacom_device,
'A' + button);
}
else
return -1;
}
#endif
static gboolean
clutter_input_device_xi2_is_mode_switch_button (ClutterInputDevice *device,
guint group,
guint button)
{
int button_group = -1;
#ifdef HAVE_LIBWACOM
button_group = clutter_input_device_xi2_get_button_group (device, button);
#endif
return button_group == (int) group;
}
static gboolean
clutter_input_device_xi2_get_physical_size (ClutterInputDevice *device,
gdouble *width,
gdouble *height)
{
Display *xdisplay;
XIDeviceInfo *dev_info;
gdouble w = 0, h = 0;
int i, n_info, device_id;
xdisplay = clutter_x11_get_default_display ();
device_id = clutter_input_device_get_device_id (device);
clutter_x11_trap_x_errors ();
dev_info = XIQueryDevice (xdisplay, device_id, &n_info);
if (clutter_x11_untrap_x_errors ())
return FALSE;
if (!dev_info)
return FALSE;
for (i = 0; i < dev_info->num_classes; i++)
{
XIValuatorClassInfo *valuator;
gdouble *value;
if (dev_info->classes[i]->type != XIValuatorClass)
continue;
valuator = (XIValuatorClassInfo *) dev_info->classes[i];
if (valuator->label == XInternAtom (xdisplay, "Abs X", True) ||
valuator->label == XInternAtom (xdisplay, "Abs MT Position X", True))
value = &w;
else if (valuator->label == XInternAtom (xdisplay, "Abs Y", True) ||
valuator->label == XInternAtom (xdisplay, "Abs MT Position Y", True))
value = &h;
else
continue;
*value = (valuator->max - valuator->min) * 1000 / valuator->resolution;
}
XIFreeDeviceInfo (dev_info);
*width = w;
*height = h;
return (w > 0 && h > 0);
}
static void
clutter_input_device_xi2_class_init (ClutterInputDeviceXI2Class *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
ClutterInputDeviceClass *device_class = CLUTTER_INPUT_DEVICE_CLASS (klass);
gobject_class->constructed = clutter_input_device_xi2_constructed;
gobject_class->finalize = clutter_input_device_xi2_finalize;
device_class->keycode_to_evdev = clutter_input_device_xi2_keycode_to_evdev;
device_class->is_grouped = clutter_input_device_xi2_is_grouped;
device_class->get_group_n_modes = clutter_input_device_xi2_get_group_n_modes;
device_class->is_mode_switch_button = clutter_input_device_xi2_is_mode_switch_button;
device_class->get_physical_size = clutter_input_device_xi2_get_physical_size;
}
static void
clutter_input_device_xi2_init (ClutterInputDeviceXI2 *self)
{
}
static ClutterModifierType
get_modifier_for_button (int i)
{
switch (i)
{
case 1:
return CLUTTER_BUTTON1_MASK;
case 2:
return CLUTTER_BUTTON2_MASK;
case 3:
return CLUTTER_BUTTON3_MASK;
case 4:
return CLUTTER_BUTTON4_MASK;
case 5:
return CLUTTER_BUTTON5_MASK;
default:
return 0;
}
}
void
_clutter_input_device_xi2_translate_state (ClutterEvent *event,
XIModifierState *modifiers_state,
XIButtonState *buttons_state,
XIGroupState *group_state)
{
guint button = 0;
guint base = 0;
guint latched = 0;
guint locked = 0;
guint effective;
if (modifiers_state)
{
base = (guint) modifiers_state->base;
latched = (guint) modifiers_state->latched;
locked = (guint) modifiers_state->locked;
}
if (buttons_state)
{
int len, i;
len = MIN (N_BUTTONS, buttons_state->mask_len * 8);
for (i = 0; i < len; i++)
{
if (!XIMaskIsSet (buttons_state->mask, i))
continue;
button |= get_modifier_for_button (i);
}
}
/* The XIButtonState sent in the event specifies the
* state of the buttons before the event. In order to
* get the current state of the buttons, we need to
* filter out the current button.
*/
switch (event->type)
{
case CLUTTER_BUTTON_PRESS:
button |= (get_modifier_for_button (event->button.button));
break;
case CLUTTER_BUTTON_RELEASE:
button &= ~(get_modifier_for_button (event->button.button));
break;
default:
break;
}
effective = button | base | latched | locked;
if (group_state)
effective |= (group_state->effective) << 13;
_clutter_event_set_state_full (event, button, base, latched, locked, effective);
}
void
clutter_input_device_xi2_update_tool (ClutterInputDevice *device,
ClutterInputDeviceTool *tool)
{
ClutterInputDeviceXI2 *device_xi2 = CLUTTER_INPUT_DEVICE_XI2 (device);
g_set_object (&device_xi2->current_tool, tool);
}
ClutterInputDeviceTool *
clutter_input_device_xi2_get_current_tool (ClutterInputDevice *device)
{
ClutterInputDeviceXI2 *device_xi2 = CLUTTER_INPUT_DEVICE_XI2 (device);
return device_xi2->current_tool;
}
#ifdef HAVE_LIBWACOM
void
clutter_input_device_xi2_ensure_wacom_info (ClutterInputDevice *device,
WacomDeviceDatabase *wacom_db)
{
ClutterInputDeviceXI2 *device_xi2 = CLUTTER_INPUT_DEVICE_XI2 (device);
const gchar *node_path;
node_path = clutter_input_device_get_device_node (device);
device_xi2->wacom_device = libwacom_new_from_path (wacom_db, node_path,
WFALLBACK_NONE, NULL);
}
guint
clutter_input_device_xi2_get_pad_group_mode (ClutterInputDevice *device,
guint group)
{
ClutterInputDeviceXI2 *device_xi2 = CLUTTER_INPUT_DEVICE_XI2 (device);
if (group >= device_xi2->group_modes->len)
return 0;
return g_array_index (device_xi2->group_modes, guint, group);
}
void
clutter_input_device_xi2_update_pad_state (ClutterInputDevice *device,
guint button,
guint state,
guint *group,
guint *mode)
{
ClutterInputDeviceXI2 *device_xi2 = CLUTTER_INPUT_DEVICE_XI2 (device);
guint button_group, *group_mode;
gboolean is_mode_switch = FALSE;
button_group = clutter_input_device_xi2_get_button_group (device, button);
is_mode_switch = button_group >= 0;
/* Assign all non-mode-switch buttons to group 0 so far */
button_group = MAX (0, button_group);
if (button_group >= device_xi2->group_modes->len)
return;
group_mode = &g_array_index (device_xi2->group_modes, guint, button_group);
if (is_mode_switch && state)
{
guint next, n_modes;
n_modes = clutter_input_device_get_group_n_modes (device, button_group);
next = (*group_mode + 1) % n_modes;
*group_mode = next;
}
if (group)
*group = button_group;
if (mode)
*mode = *group_mode;
}
#endif