
Since the backend also knows when did we start/stop being grabbed, we can shuffle this backend-y code to the backend. Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/3420>
2713 lines
82 KiB
C
2713 lines
82 KiB
C
/*
|
|
* Copyright (C) 2019 Red Hat Inc.
|
|
*
|
|
* 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: Carlos Garnacho <carlosg@gnome.org>
|
|
*/
|
|
#include "config.h"
|
|
|
|
#include <linux/input-event-codes.h>
|
|
#include <X11/extensions/XInput2.h>
|
|
#include <X11/extensions/XKB.h>
|
|
|
|
#ifdef HAVE_LIBGUDEV
|
|
#include <gudev/gudev.h>
|
|
#endif
|
|
|
|
#include "backends/meta-input-settings-private.h"
|
|
#include "backends/x11/meta-backend-x11.h"
|
|
#include "backends/x11/meta-clutter-backend-x11.h"
|
|
#include "backends/x11/meta-event-x11.h"
|
|
#include "backends/x11/meta-input-device-tool-x11.h"
|
|
#include "backends/x11/meta-input-device-x11.h"
|
|
#include "backends/x11/meta-keymap-x11.h"
|
|
#include "backends/x11/meta-stage-x11.h"
|
|
#include "backends/x11/meta-virtual-input-device-x11.h"
|
|
#include "backends/x11/meta-xkb-a11y-x11.h"
|
|
#include "clutter/clutter-mutter.h"
|
|
#include "core/bell.h"
|
|
#include "meta-seat-x11.h"
|
|
#include "mtk/mtk-x11.h"
|
|
|
|
enum
|
|
{
|
|
PROP_0,
|
|
PROP_BACKEND,
|
|
PROP_OPCODE,
|
|
PROP_POINTER_ID,
|
|
PROP_KEYBOARD_ID,
|
|
N_PROPS,
|
|
|
|
/* This property is overridden */
|
|
PROP_TOUCH_MODE,
|
|
};
|
|
|
|
typedef struct _MetaTouchInfo MetaTouchInfo;
|
|
|
|
struct _MetaTouchInfo
|
|
{
|
|
ClutterEventSequence *sequence;
|
|
double x;
|
|
double y;
|
|
};
|
|
|
|
struct _MetaSeatX11
|
|
{
|
|
ClutterSeat parent_instance;
|
|
|
|
MetaBackend *backend;
|
|
|
|
ClutterInputDevice *core_pointer;
|
|
ClutterInputDevice *core_keyboard;
|
|
GList *devices;
|
|
GHashTable *devices_by_id;
|
|
GHashTable *tools_by_serial;
|
|
GHashTable *touch_coords;
|
|
MetaKeymapX11 *keymap;
|
|
|
|
#ifdef HAVE_LIBGUDEV
|
|
GUdevClient *udev_client;
|
|
#endif
|
|
|
|
int pointer_id;
|
|
int keyboard_id;
|
|
int opcode;
|
|
ClutterGrabState grab_state;
|
|
guint has_touchscreens : 1;
|
|
guint touch_mode : 1;
|
|
guint has_pointer_focus : 1;
|
|
};
|
|
|
|
static GParamSpec *props[N_PROPS] = { 0 };
|
|
|
|
G_DEFINE_TYPE (MetaSeatX11, meta_seat_x11, CLUTTER_TYPE_SEAT)
|
|
|
|
static const char *clutter_input_axis_atom_names[] = {
|
|
"Abs X", /* CLUTTER_INPUT_AXIS_X */
|
|
"Abs Y", /* CLUTTER_INPUT_AXIS_Y */
|
|
"Abs Pressure", /* CLUTTER_INPUT_AXIS_PRESSURE */
|
|
"Abs Tilt X", /* CLUTTER_INPUT_AXIS_XTILT */
|
|
"Abs Tilt Y", /* CLUTTER_INPUT_AXIS_YTILT */
|
|
"Abs Wheel", /* CLUTTER_INPUT_AXIS_WHEEL */
|
|
"Abs Distance", /* CLUTTER_INPUT_AXIS_DISTANCE */
|
|
};
|
|
|
|
static const char *wacom_type_atoms[] = {
|
|
"STYLUS",
|
|
"CURSOR",
|
|
"ERASER",
|
|
"PAD",
|
|
"TOUCH"
|
|
};
|
|
#define N_WACOM_TYPE_ATOMS G_N_ELEMENTS (wacom_type_atoms)
|
|
|
|
enum
|
|
{
|
|
WACOM_TYPE_STYLUS,
|
|
WACOM_TYPE_CURSOR,
|
|
WACOM_TYPE_ERASER,
|
|
WACOM_TYPE_PAD,
|
|
WACOM_TYPE_TOUCH,
|
|
};
|
|
|
|
enum
|
|
{
|
|
PAD_AXIS_FIRST = 3, /* First axes are always x/y/pressure, ignored in pads */
|
|
PAD_AXIS_STRIP1 = PAD_AXIS_FIRST,
|
|
PAD_AXIS_STRIP2,
|
|
PAD_AXIS_RING1,
|
|
PAD_AXIS_RING2,
|
|
};
|
|
|
|
#define N_AXIS_ATOMS G_N_ELEMENTS (clutter_input_axis_atom_names)
|
|
|
|
static Atom clutter_input_axis_atoms[N_AXIS_ATOMS] = { 0, };
|
|
|
|
static Display *
|
|
xdisplay_from_seat (MetaSeatX11 *seat_x11)
|
|
{
|
|
return meta_backend_x11_get_xdisplay (META_BACKEND_X11 (seat_x11->backend));
|
|
}
|
|
|
|
static Window
|
|
root_xwindow_from_seat (MetaSeatX11 *seat_x11)
|
|
{
|
|
MetaBackendX11 *backend_x11 = META_BACKEND_X11 (seat_x11->backend);
|
|
|
|
return meta_backend_x11_get_root_xwindow (backend_x11);
|
|
}
|
|
|
|
static void
|
|
translate_valuator_class (Display *xdisplay,
|
|
ClutterInputDevice *device,
|
|
XIValuatorClassInfo *class)
|
|
{
|
|
static gboolean atoms_initialized = FALSE;
|
|
ClutterInputAxis i, axis = CLUTTER_INPUT_AXIS_IGNORE;
|
|
|
|
if (G_UNLIKELY (!atoms_initialized))
|
|
{
|
|
XInternAtoms (xdisplay,
|
|
(char **) clutter_input_axis_atom_names, N_AXIS_ATOMS,
|
|
False,
|
|
clutter_input_axis_atoms);
|
|
|
|
atoms_initialized = TRUE;
|
|
}
|
|
|
|
for (i = 0;
|
|
i < N_AXIS_ATOMS;
|
|
i += 1)
|
|
{
|
|
if (clutter_input_axis_atoms[i] == class->label)
|
|
{
|
|
axis = i + 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
meta_input_device_x11_add_axis (device, axis,
|
|
class->min,
|
|
class->max,
|
|
class->resolution);
|
|
|
|
g_debug ("Added axis '%s' (min:%.2f, max:%.2fd, res:%d) of device %d",
|
|
axis == CLUTTER_INPUT_AXIS_IGNORE ?
|
|
"Ignored" :
|
|
clutter_input_axis_atom_names[axis - 1],
|
|
class->min,
|
|
class->max,
|
|
class->resolution,
|
|
meta_input_device_x11_get_device_id (device));
|
|
}
|
|
|
|
static void
|
|
translate_device_classes (Display *xdisplay,
|
|
ClutterInputDevice *device,
|
|
XIAnyClassInfo **classes,
|
|
int n_classes)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < n_classes; i++)
|
|
{
|
|
XIAnyClassInfo *class_info = classes[i];
|
|
|
|
switch (class_info->type)
|
|
{
|
|
case XIValuatorClass:
|
|
translate_valuator_class (xdisplay, device,
|
|
(XIValuatorClassInfo *) class_info);
|
|
break;
|
|
|
|
case XIScrollClass:
|
|
{
|
|
XIScrollClassInfo *scroll_info = (XIScrollClassInfo *) class_info;
|
|
ClutterScrollDirection direction;
|
|
|
|
if (scroll_info->scroll_type == XIScrollTypeVertical)
|
|
direction = CLUTTER_SCROLL_DOWN;
|
|
else
|
|
direction = CLUTTER_SCROLL_RIGHT;
|
|
|
|
g_debug ("Scroll valuator %d: %s, increment: %f",
|
|
scroll_info->number,
|
|
scroll_info->scroll_type == XIScrollTypeVertical
|
|
? "vertical"
|
|
: "horizontal",
|
|
scroll_info->increment);
|
|
|
|
meta_input_device_x11_add_scroll_info (device,
|
|
scroll_info->number,
|
|
direction,
|
|
scroll_info->increment);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
is_touch_device (XIAnyClassInfo **classes,
|
|
int n_classes,
|
|
ClutterInputDeviceType *device_type,
|
|
ClutterInputCapabilities *capabilities,
|
|
uint32_t *n_touch_points)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < n_classes; i++)
|
|
{
|
|
XITouchClassInfo *class = (XITouchClassInfo *) classes[i];
|
|
|
|
if (class->type != XITouchClass)
|
|
continue;
|
|
|
|
if (class->num_touches > 0)
|
|
{
|
|
if (class->mode == XIDirectTouch)
|
|
{
|
|
*device_type = CLUTTER_TOUCHSCREEN_DEVICE;
|
|
*capabilities = CLUTTER_INPUT_CAPABILITY_TOUCH;
|
|
}
|
|
else if (class->mode == XIDependentTouch)
|
|
{
|
|
*device_type = CLUTTER_TOUCHPAD_DEVICE;
|
|
*capabilities =
|
|
CLUTTER_INPUT_CAPABILITY_POINTER |
|
|
CLUTTER_INPUT_CAPABILITY_TOUCHPAD;
|
|
}
|
|
else
|
|
{
|
|
continue;
|
|
}
|
|
|
|
*n_touch_points = class->num_touches;
|
|
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static gboolean
|
|
is_touchpad_device (MetaSeatX11 *seat_x11,
|
|
XIDeviceInfo *info)
|
|
{
|
|
Display *xdisplay = xdisplay_from_seat (seat_x11);
|
|
gulong nitems, bytes_after;
|
|
uint32_t *data = NULL;
|
|
int rc, format;
|
|
Atom type;
|
|
Atom prop;
|
|
|
|
prop = XInternAtom (xdisplay,
|
|
"libinput Tapping Enabled", True);
|
|
if (prop == None)
|
|
return FALSE;
|
|
|
|
mtk_x11_error_trap_push (xdisplay);
|
|
rc = XIGetProperty (xdisplay,
|
|
info->deviceid,
|
|
prop,
|
|
0, 1, False, XA_INTEGER, &type, &format, &nitems, &bytes_after,
|
|
(guchar **) &data);
|
|
mtk_x11_error_trap_pop (xdisplay);
|
|
|
|
/* We don't care about the data */
|
|
XFree (data);
|
|
|
|
if (rc != Success || type != XA_INTEGER || format != 8 || nitems != 1)
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
get_device_ids (MetaSeatX11 *seat_x11,
|
|
XIDeviceInfo *info,
|
|
char **vendor_id,
|
|
char **product_id)
|
|
{
|
|
Display *xdisplay = xdisplay_from_seat (seat_x11);
|
|
gulong nitems, bytes_after;
|
|
uint32_t *data = NULL;
|
|
int rc, format;
|
|
Atom type;
|
|
|
|
mtk_x11_error_trap_push (xdisplay);
|
|
rc = XIGetProperty (xdisplay,
|
|
info->deviceid,
|
|
XInternAtom (xdisplay, "Device Product ID", False),
|
|
0, 2, False, XA_INTEGER, &type, &format, &nitems, &bytes_after,
|
|
(guchar **) &data);
|
|
mtk_x11_error_trap_pop (xdisplay);
|
|
|
|
if (rc != Success || type != XA_INTEGER || format != 32 || nitems != 2)
|
|
{
|
|
XFree (data);
|
|
return FALSE;
|
|
}
|
|
|
|
if (vendor_id)
|
|
*vendor_id = g_strdup_printf ("%.4x", data[0]);
|
|
if (product_id)
|
|
*product_id = g_strdup_printf ("%.4x", data[1]);
|
|
|
|
XFree (data);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static char *
|
|
get_device_node_path (MetaSeatX11 *seat_x11,
|
|
XIDeviceInfo *info)
|
|
{
|
|
Display *xdisplay = xdisplay_from_seat (seat_x11);
|
|
gulong nitems, bytes_after;
|
|
guchar *data;
|
|
int rc, format;
|
|
Atom prop, type;
|
|
char *node_path;
|
|
|
|
prop = XInternAtom (xdisplay, "Device Node", False);
|
|
if (prop == None)
|
|
return NULL;
|
|
|
|
mtk_x11_error_trap_push (xdisplay);
|
|
|
|
rc = XIGetProperty (xdisplay,
|
|
info->deviceid, prop, 0, 1024, False,
|
|
XA_STRING, &type, &format, &nitems, &bytes_after,
|
|
(guchar **) &data);
|
|
|
|
if (mtk_x11_error_trap_pop_with_return (xdisplay))
|
|
return NULL;
|
|
|
|
if (rc != Success || type != XA_STRING || format != 8)
|
|
{
|
|
XFree (data);
|
|
return FALSE;
|
|
}
|
|
|
|
node_path = g_strdup ((char *) data);
|
|
XFree (data);
|
|
|
|
return node_path;
|
|
}
|
|
|
|
static void
|
|
get_pad_features (XIDeviceInfo *info,
|
|
uint32_t *n_rings,
|
|
uint32_t *n_strips)
|
|
{
|
|
int i, rings = 0, strips = 0;
|
|
|
|
for (i = PAD_AXIS_FIRST; i < info->num_classes; i++)
|
|
{
|
|
XIValuatorClassInfo *valuator = (XIValuatorClassInfo*) info->classes[i];
|
|
int axis = valuator->number;
|
|
|
|
if (valuator->type != XIValuatorClass)
|
|
continue;
|
|
if (valuator->max <= 1)
|
|
continue;
|
|
|
|
/* Ring/strip axes are fixed in pad devices as handled by the
|
|
* wacom driver. Match those to detect pad features.
|
|
*/
|
|
if (axis == PAD_AXIS_STRIP1 || axis == PAD_AXIS_STRIP2)
|
|
strips++;
|
|
else if (axis == PAD_AXIS_RING1 || axis == PAD_AXIS_RING2)
|
|
rings++;
|
|
}
|
|
|
|
*n_rings = rings;
|
|
*n_strips = strips;
|
|
}
|
|
|
|
static gboolean
|
|
guess_serial_from_libinput_prop (MetaSeatX11 *seat_x11,
|
|
ClutterInputDevice *device,
|
|
unsigned int *serial_out)
|
|
{
|
|
Display *xdisplay = xdisplay_from_seat (seat_x11);
|
|
int device_id = meta_input_device_x11_get_device_id (device);
|
|
gulong nitems, bytes_after;
|
|
uint32_t *data = NULL;
|
|
int rc, format;
|
|
Atom type;
|
|
Atom prop;
|
|
|
|
prop = XInternAtom (xdisplay, "libinput Tablet Tool Serial", True);
|
|
if (prop != None)
|
|
{
|
|
mtk_x11_error_trap_push (xdisplay);
|
|
rc = XIGetProperty (xdisplay,
|
|
device_id,
|
|
prop,
|
|
0, 1, False, XA_CARDINAL, &type, &format, &nitems, &bytes_after,
|
|
(guchar **) &data);
|
|
mtk_x11_error_trap_pop (xdisplay);
|
|
|
|
if (rc != Success || type != XA_CARDINAL || format != 32 || nitems != 1)
|
|
{
|
|
XFree (data);
|
|
return FALSE;
|
|
}
|
|
|
|
*serial_out = *data;
|
|
XFree (data);
|
|
return TRUE;
|
|
}
|
|
|
|
/* xf86-input-libinput < 1.5.0 doesn't have the serial prop, let's check
|
|
* for a generic property that tells us if it's libinput handling this device.
|
|
* If that's the case we give the tool a "random" fixed serial to pass through
|
|
* the code that assumes 0 == no tool.
|
|
*/
|
|
prop = XInternAtom (xdisplay, "libinput Send Events Mode Enabled", True);
|
|
if (prop == None)
|
|
return FALSE;
|
|
|
|
mtk_x11_error_trap_push (xdisplay);
|
|
rc = XIGetProperty (xdisplay,
|
|
device_id,
|
|
prop,
|
|
0, 1, False, XA_INTEGER, &type, &format, &nitems, &bytes_after,
|
|
(guchar **) &data);
|
|
mtk_x11_error_trap_pop (xdisplay);
|
|
XFree (data);
|
|
|
|
if (rc != Success)
|
|
return FALSE;
|
|
|
|
*serial_out = 0xffffffaa;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/* The Wacom driver exports the tool type as property. Use that over
|
|
guessing based on the device name */
|
|
static gboolean
|
|
guess_source_from_wacom_type (MetaSeatX11 *seat_x11,
|
|
XIDeviceInfo *info,
|
|
ClutterInputDeviceType *source_out,
|
|
ClutterInputCapabilities *capabilities_out)
|
|
{
|
|
Display *xdisplay = xdisplay_from_seat (seat_x11);
|
|
gulong nitems, bytes_after;
|
|
uint32_t *data = NULL;
|
|
int rc, format;
|
|
Atom type;
|
|
Atom prop;
|
|
Atom device_type;
|
|
Atom types[N_WACOM_TYPE_ATOMS];
|
|
|
|
prop = XInternAtom (xdisplay, "Wacom Tool Type", True);
|
|
if (prop == None)
|
|
return FALSE;
|
|
|
|
mtk_x11_error_trap_push (xdisplay);
|
|
rc = XIGetProperty (xdisplay,
|
|
info->deviceid,
|
|
prop,
|
|
0, 1, False, XA_ATOM, &type, &format, &nitems, &bytes_after,
|
|
(guchar **) &data);
|
|
mtk_x11_error_trap_pop (xdisplay);
|
|
|
|
if (rc != Success || type != XA_ATOM || format != 32 || nitems != 1)
|
|
{
|
|
XFree (data);
|
|
return FALSE;
|
|
}
|
|
|
|
device_type = *data;
|
|
XFree (data);
|
|
|
|
if (device_type == 0)
|
|
return FALSE;
|
|
|
|
rc = XInternAtoms (xdisplay,
|
|
(char **)wacom_type_atoms,
|
|
N_WACOM_TYPE_ATOMS,
|
|
False,
|
|
types);
|
|
if (rc == 0)
|
|
return FALSE;
|
|
|
|
if (device_type == types[WACOM_TYPE_STYLUS])
|
|
{
|
|
*source_out = CLUTTER_PEN_DEVICE;
|
|
*capabilities_out = CLUTTER_INPUT_CAPABILITY_TABLET_TOOL;
|
|
}
|
|
else if (device_type == types[WACOM_TYPE_CURSOR])
|
|
{
|
|
*source_out = CLUTTER_CURSOR_DEVICE;
|
|
*capabilities_out = CLUTTER_INPUT_CAPABILITY_TABLET_TOOL;
|
|
}
|
|
else if (device_type == types[WACOM_TYPE_ERASER])
|
|
{
|
|
*source_out = CLUTTER_ERASER_DEVICE;
|
|
*capabilities_out = CLUTTER_INPUT_CAPABILITY_TABLET_TOOL;
|
|
}
|
|
else if (device_type == types[WACOM_TYPE_PAD])
|
|
{
|
|
*source_out = CLUTTER_PAD_DEVICE;
|
|
*capabilities_out = CLUTTER_INPUT_CAPABILITY_TABLET_PAD;
|
|
}
|
|
else if (device_type == types[WACOM_TYPE_TOUCH])
|
|
{
|
|
uint32_t num_touches = 0;
|
|
|
|
if (!is_touch_device (info->classes, info->num_classes,
|
|
source_out, capabilities_out, &num_touches))
|
|
{
|
|
*source_out = CLUTTER_TOUCHSCREEN_DEVICE;
|
|
*capabilities_out = CLUTTER_INPUT_CAPABILITY_TOUCH;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
#ifdef HAVE_LIBGUDEV
|
|
static gboolean
|
|
has_udev_property (GUdevDevice *udev_device,
|
|
const char *property_name)
|
|
{
|
|
g_autoptr (GUdevDevice) parent_udev_device = NULL;
|
|
|
|
if (NULL != g_udev_device_get_property (udev_device, property_name))
|
|
return TRUE;
|
|
|
|
parent_udev_device = g_udev_device_get_parent (udev_device);
|
|
|
|
if (!parent_udev_device)
|
|
return FALSE;
|
|
|
|
return g_udev_device_get_property (parent_udev_device, property_name) != NULL;
|
|
}
|
|
#endif
|
|
|
|
static ClutterInputDevice *
|
|
create_device (MetaSeatX11 *seat_x11,
|
|
ClutterBackend *clutter_backend,
|
|
XIDeviceInfo *info)
|
|
{
|
|
Display *xdisplay = xdisplay_from_seat (seat_x11);
|
|
ClutterInputDeviceType source, touch_source;
|
|
ClutterInputCapabilities capabilities = 0;
|
|
ClutterInputDevice *retval;
|
|
ClutterInputMode mode;
|
|
uint32_t num_touches = 0, num_rings = 0, num_strips = 0;
|
|
char *vendor_id = NULL, *product_id = NULL, *node_path = NULL;
|
|
|
|
if (info->use == XIMasterKeyboard || info->use == XISlaveKeyboard)
|
|
{
|
|
source = CLUTTER_KEYBOARD_DEVICE;
|
|
capabilities = CLUTTER_INPUT_CAPABILITY_KEYBOARD;
|
|
}
|
|
else if (is_touchpad_device (seat_x11, info))
|
|
{
|
|
source = CLUTTER_TOUCHPAD_DEVICE;
|
|
}
|
|
else if (info->use == XISlavePointer &&
|
|
is_touch_device (info->classes, info->num_classes,
|
|
&touch_source, &capabilities,
|
|
&num_touches))
|
|
{
|
|
source = touch_source;
|
|
}
|
|
else if (!guess_source_from_wacom_type (seat_x11, info, &source, &capabilities))
|
|
{
|
|
char *name;
|
|
|
|
name = g_ascii_strdown (info->name, -1);
|
|
|
|
if (strstr (name, "eraser") != NULL)
|
|
{
|
|
source = CLUTTER_ERASER_DEVICE;
|
|
capabilities = CLUTTER_INPUT_CAPABILITY_TABLET_TOOL;
|
|
}
|
|
else if (strstr (name, "cursor") != NULL)
|
|
{
|
|
source = CLUTTER_CURSOR_DEVICE;
|
|
capabilities = CLUTTER_INPUT_CAPABILITY_TABLET_TOOL;
|
|
}
|
|
else if (strstr (name, " pad") != NULL)
|
|
{
|
|
source = CLUTTER_PAD_DEVICE;
|
|
capabilities = CLUTTER_INPUT_CAPABILITY_TABLET_PAD;
|
|
}
|
|
else if (strstr (name, "wacom") != NULL || strstr (name, "pen") != NULL)
|
|
{
|
|
source = CLUTTER_PEN_DEVICE;
|
|
capabilities = CLUTTER_INPUT_CAPABILITY_TABLET_TOOL;
|
|
}
|
|
else if (strstr (name, "touchpad") != NULL)
|
|
{
|
|
source = CLUTTER_TOUCHPAD_DEVICE;
|
|
capabilities =
|
|
CLUTTER_INPUT_CAPABILITY_POINTER |
|
|
CLUTTER_INPUT_CAPABILITY_TOUCHPAD;
|
|
}
|
|
else
|
|
{
|
|
source = CLUTTER_POINTER_DEVICE;
|
|
capabilities = CLUTTER_INPUT_CAPABILITY_POINTER;
|
|
}
|
|
|
|
g_free (name);
|
|
}
|
|
|
|
switch (info->use)
|
|
{
|
|
case XIMasterKeyboard:
|
|
case XIMasterPointer:
|
|
mode = CLUTTER_INPUT_MODE_LOGICAL;
|
|
break;
|
|
|
|
case XISlaveKeyboard:
|
|
case XISlavePointer:
|
|
mode = CLUTTER_INPUT_MODE_PHYSICAL;
|
|
break;
|
|
|
|
case XIFloatingSlave:
|
|
default:
|
|
mode = CLUTTER_INPUT_MODE_FLOATING;
|
|
break;
|
|
}
|
|
|
|
if (info->use != XIMasterKeyboard &&
|
|
info->use != XIMasterPointer)
|
|
{
|
|
get_device_ids (seat_x11, info, &vendor_id, &product_id);
|
|
node_path = get_device_node_path (seat_x11, info);
|
|
}
|
|
|
|
#ifdef HAVE_LIBGUDEV
|
|
if (node_path)
|
|
{
|
|
g_autoptr (GUdevDevice) udev_device = NULL;
|
|
|
|
udev_device = g_udev_client_query_by_device_file (seat_x11->udev_client,
|
|
node_path);
|
|
if (udev_device)
|
|
{
|
|
if (has_udev_property (udev_device, "ID_INPUT_TRACKBALL"))
|
|
capabilities |= CLUTTER_INPUT_CAPABILITY_TRACKBALL;
|
|
if (has_udev_property (udev_device, "ID_INPUT_POINTINGSTICK"))
|
|
capabilities |= CLUTTER_INPUT_CAPABILITY_TRACKPOINT;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (source == CLUTTER_PAD_DEVICE)
|
|
get_pad_features (info, &num_rings, &num_strips);
|
|
|
|
retval = g_object_new (META_TYPE_INPUT_DEVICE_X11,
|
|
"backend", seat_x11->backend,
|
|
"name", info->name,
|
|
"id", info->deviceid,
|
|
"has-cursor", (info->use == XIMasterPointer),
|
|
"device-type", source,
|
|
"capabilities", capabilities,
|
|
"device-mode", mode,
|
|
"vendor-id", vendor_id,
|
|
"product-id", product_id,
|
|
"device-node", node_path,
|
|
"n-rings", num_rings,
|
|
"n-strips", num_strips,
|
|
"n-mode-groups", MAX (num_rings, num_strips),
|
|
"seat", seat_x11,
|
|
NULL);
|
|
|
|
translate_device_classes (xdisplay, retval,
|
|
info->classes,
|
|
info->num_classes);
|
|
|
|
g_free (vendor_id);
|
|
g_free (product_id);
|
|
g_free (node_path);
|
|
|
|
g_debug ("Created device '%s' (id: %d, has-cursor: %s)",
|
|
info->name,
|
|
info->deviceid,
|
|
info->use == XIMasterPointer ? "yes" : "no");
|
|
|
|
return retval;
|
|
}
|
|
|
|
static void
|
|
pad_passive_button_grab (MetaSeatX11 *seat_x11,
|
|
ClutterInputDevice *device)
|
|
{
|
|
Display *xdisplay = xdisplay_from_seat (seat_x11);
|
|
Window root_xwindow = root_xwindow_from_seat (seat_x11);
|
|
XIGrabModifiers xi_grab_mods = { XIAnyModifier, };
|
|
XIEventMask xi_event_mask;
|
|
int device_id, rc;
|
|
|
|
device_id = meta_input_device_x11_get_device_id (device);
|
|
|
|
xi_event_mask.deviceid = device_id;
|
|
xi_event_mask.mask_len = XIMaskLen (XI_LASTEVENT);
|
|
xi_event_mask.mask = g_new0 (unsigned char, xi_event_mask.mask_len);
|
|
|
|
XISetMask (xi_event_mask.mask, XI_Motion);
|
|
XISetMask (xi_event_mask.mask, XI_ButtonPress);
|
|
XISetMask (xi_event_mask.mask, XI_ButtonRelease);
|
|
|
|
mtk_x11_error_trap_push (xdisplay);
|
|
rc = XIGrabButton (xdisplay,
|
|
device_id, XIAnyButton,
|
|
root_xwindow, None,
|
|
XIGrabModeSync, XIGrabModeSync,
|
|
True, &xi_event_mask, 1, &xi_grab_mods);
|
|
if (rc != 0)
|
|
{
|
|
g_warning ("Could not passively grab pad device: %s",
|
|
clutter_input_device_get_device_name (device));
|
|
}
|
|
else
|
|
{
|
|
XIAllowEvents (xdisplay, device_id, XIAsyncDevice, CLUTTER_CURRENT_TIME);
|
|
}
|
|
|
|
mtk_x11_error_trap_pop (xdisplay);
|
|
|
|
g_free (xi_event_mask.mask);
|
|
}
|
|
|
|
static void
|
|
update_touch_mode (MetaSeatX11 *seat_x11)
|
|
{
|
|
gboolean touch_mode;
|
|
|
|
touch_mode = seat_x11->has_touchscreens;
|
|
|
|
if (seat_x11->touch_mode == touch_mode)
|
|
return;
|
|
|
|
seat_x11->touch_mode = touch_mode;
|
|
g_object_notify (G_OBJECT (seat_x11), "touch-mode");
|
|
}
|
|
|
|
static ClutterInputDevice *
|
|
add_device (MetaSeatX11 *seat_x11,
|
|
ClutterBackend *clutter_backend,
|
|
XIDeviceInfo *info)
|
|
{
|
|
ClutterInputDevice *device;
|
|
|
|
device = create_device (seat_x11, clutter_backend, info);
|
|
|
|
g_hash_table_replace (seat_x11->devices_by_id,
|
|
GINT_TO_POINTER (info->deviceid),
|
|
device);
|
|
|
|
if (info->use == XIMasterPointer &&
|
|
info->deviceid == seat_x11->pointer_id)
|
|
{
|
|
seat_x11->core_pointer = device;
|
|
}
|
|
else if (info->use == XIMasterKeyboard &&
|
|
info->deviceid == seat_x11->keyboard_id)
|
|
{
|
|
seat_x11->core_keyboard = device;
|
|
}
|
|
else if ((info->use == XISlavePointer &&
|
|
info->attachment == seat_x11->pointer_id) ||
|
|
(info->use == XISlaveKeyboard &&
|
|
info->attachment == seat_x11->keyboard_id))
|
|
{
|
|
seat_x11->devices = g_list_prepend (seat_x11->devices, device);
|
|
}
|
|
else
|
|
{
|
|
g_warning ("Unhandled device: %s",
|
|
clutter_input_device_get_device_name (device));
|
|
}
|
|
|
|
if (clutter_input_device_get_device_type (device) == CLUTTER_PAD_DEVICE)
|
|
pad_passive_button_grab (seat_x11, device);
|
|
|
|
return device;
|
|
}
|
|
|
|
static gboolean
|
|
has_touchscreens (MetaSeatX11 *seat_x11)
|
|
{
|
|
GList *l;
|
|
|
|
for (l = seat_x11->devices; l; l = l->next)
|
|
{
|
|
if (clutter_input_device_get_device_type (l->data) == CLUTTER_TOUCHSCREEN_DEVICE)
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static void
|
|
remove_device (MetaSeatX11 *seat_x11,
|
|
ClutterInputDevice *device)
|
|
{
|
|
if (seat_x11->core_pointer == device)
|
|
{
|
|
seat_x11->core_pointer = NULL;
|
|
}
|
|
else if (seat_x11->core_keyboard == device)
|
|
{
|
|
seat_x11->core_keyboard = NULL;
|
|
}
|
|
else
|
|
{
|
|
seat_x11->devices = g_list_remove (seat_x11->devices, device);
|
|
}
|
|
}
|
|
|
|
static void
|
|
update_tool (MetaSeatX11 *seat_x11,
|
|
ClutterInputDevice *device,
|
|
int serial_id)
|
|
{
|
|
ClutterInputDeviceTool *tool = NULL;
|
|
ClutterInputDeviceToolType type;
|
|
MetaInputSettings *input_settings;
|
|
int lookup_serial;
|
|
|
|
if (serial_id != 0)
|
|
{
|
|
/* stylus and eraser share the same serial, so bitflip the eraser's
|
|
* serial to make them distinct in the hashtable */
|
|
if (clutter_input_device_get_device_type (device) == CLUTTER_ERASER_DEVICE)
|
|
{
|
|
lookup_serial = ~serial_id;
|
|
type = CLUTTER_INPUT_DEVICE_TOOL_ERASER;
|
|
}
|
|
else
|
|
{
|
|
lookup_serial = serial_id;
|
|
type = CLUTTER_INPUT_DEVICE_TOOL_PEN;
|
|
}
|
|
|
|
tool = g_hash_table_lookup (seat_x11->tools_by_serial,
|
|
GUINT_TO_POINTER (lookup_serial));
|
|
if (!tool)
|
|
{
|
|
tool = meta_input_device_tool_x11_new (serial_id, type);
|
|
g_hash_table_insert (seat_x11->tools_by_serial,
|
|
GUINT_TO_POINTER (lookup_serial),
|
|
tool);
|
|
}
|
|
}
|
|
|
|
meta_input_device_x11_update_tool (device, tool);
|
|
input_settings = meta_backend_get_input_settings (seat_x11->backend);
|
|
meta_input_settings_notify_tool_change (input_settings, device, tool);
|
|
}
|
|
|
|
static gboolean
|
|
meta_seat_x11_handle_event_post (ClutterSeat *seat,
|
|
const ClutterEvent *event)
|
|
{
|
|
MetaSeatX11 *seat_x11 = META_SEAT_X11 (seat);
|
|
ClutterInputDevice *device;
|
|
MetaInputSettings *input_settings;
|
|
ClutterEventType event_type;
|
|
gboolean is_touch, is_tablet_tool;
|
|
unsigned int serial;
|
|
ClutterInputDeviceType type;
|
|
|
|
event_type = clutter_event_type (event);
|
|
|
|
if (event_type != CLUTTER_DEVICE_ADDED &&
|
|
event_type != CLUTTER_DEVICE_REMOVED)
|
|
return TRUE;
|
|
|
|
device = clutter_event_get_device (event);
|
|
type = clutter_input_device_get_device_type (device);
|
|
is_touch = type == CLUTTER_TOUCHSCREEN_DEVICE;
|
|
is_tablet_tool = type == CLUTTER_PEN_DEVICE || type == CLUTTER_ERASER_DEVICE;
|
|
input_settings = meta_backend_get_input_settings (seat_x11->backend);
|
|
|
|
switch (event_type)
|
|
{
|
|
case CLUTTER_DEVICE_ADDED:
|
|
meta_input_settings_add_device (input_settings, device);
|
|
seat_x11->has_touchscreens |= is_touch;
|
|
if (is_tablet_tool && guess_serial_from_libinput_prop (seat_x11, device, &serial))
|
|
update_tool(seat_x11, device, serial);
|
|
break;
|
|
case CLUTTER_DEVICE_REMOVED:
|
|
if (is_touch)
|
|
seat_x11->has_touchscreens = has_touchscreens (seat_x11);
|
|
meta_input_settings_remove_device (input_settings, device);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (is_touch)
|
|
update_touch_mode (seat_x11);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static uint
|
|
device_get_tool_serial (MetaSeatX11 *seat_x11,
|
|
ClutterInputDevice *device)
|
|
{
|
|
Display *xdisplay = xdisplay_from_seat (seat_x11);
|
|
gulong nitems, bytes_after;
|
|
uint32_t *data = NULL;
|
|
int serial_id = 0;
|
|
int rc, format;
|
|
Atom type;
|
|
Atom prop;
|
|
|
|
prop = XInternAtom (xdisplay, "Wacom Serial IDs", True);
|
|
if (prop == None)
|
|
return 0;
|
|
|
|
mtk_x11_error_trap_push (xdisplay);
|
|
rc = XIGetProperty (xdisplay,
|
|
meta_input_device_x11_get_device_id (device),
|
|
prop, 0, 4, FALSE, XA_INTEGER, &type, &format, &nitems, &bytes_after,
|
|
(guchar **) &data);
|
|
mtk_x11_error_trap_pop (xdisplay);
|
|
|
|
if (rc == Success && type == XA_INTEGER && format == 32 && nitems >= 4)
|
|
serial_id = data[3];
|
|
|
|
XFree (data);
|
|
|
|
return serial_id;
|
|
}
|
|
|
|
static ClutterEvent *
|
|
translate_hierarchy_event (ClutterBackend *clutter_backend,
|
|
MetaSeatX11 *seat_x11,
|
|
XIHierarchyEvent *ev)
|
|
{
|
|
Display *xdisplay = xdisplay_from_seat (seat_x11);
|
|
int i;
|
|
ClutterEvent *event = NULL;
|
|
|
|
for (i = 0; i < ev->num_info; i++)
|
|
{
|
|
if (ev->info[i].flags & XIDeviceEnabled &&
|
|
!g_hash_table_lookup (seat_x11->devices_by_id,
|
|
GINT_TO_POINTER (ev->info[i].deviceid)))
|
|
{
|
|
XIDeviceInfo *info;
|
|
int n_devices;
|
|
|
|
g_debug ("Hierarchy event: device enabled");
|
|
|
|
mtk_x11_error_trap_push (xdisplay);
|
|
info = XIQueryDevice (xdisplay,
|
|
ev->info[i].deviceid,
|
|
&n_devices);
|
|
mtk_x11_error_trap_pop (xdisplay);
|
|
if (info != NULL)
|
|
{
|
|
ClutterInputDevice *device;
|
|
|
|
device = add_device (seat_x11, clutter_backend, &info[0]);
|
|
|
|
event = clutter_event_device_notify_new (CLUTTER_DEVICE_ADDED,
|
|
CLUTTER_EVENT_NONE,
|
|
ms2us (ev->time),
|
|
device);
|
|
XIFreeDeviceInfo (info);
|
|
}
|
|
}
|
|
else if (ev->info[i].flags & XIDeviceDisabled)
|
|
{
|
|
g_autoptr (ClutterInputDevice) device = NULL;
|
|
g_debug ("Hierarchy event: device disabled");
|
|
|
|
g_hash_table_steal_extended (seat_x11->devices_by_id,
|
|
GINT_TO_POINTER (ev->info[i].deviceid),
|
|
NULL,
|
|
(gpointer) &device);
|
|
|
|
if (device != NULL)
|
|
{
|
|
remove_device (seat_x11, device);
|
|
|
|
event = clutter_event_device_notify_new (CLUTTER_DEVICE_REMOVED,
|
|
CLUTTER_EVENT_NONE,
|
|
ms2us (ev->time),
|
|
device);
|
|
}
|
|
}
|
|
else if ((ev->info[i].flags & XISlaveAttached) ||
|
|
(ev->info[i].flags & XISlaveDetached))
|
|
{
|
|
g_debug ("Hierarchy event: physical device %s",
|
|
(ev->info[i].flags & XISlaveAttached)
|
|
? "attached"
|
|
: "detached");
|
|
}
|
|
}
|
|
|
|
return event;
|
|
}
|
|
|
|
static void
|
|
translate_property_event (MetaSeatX11 *seat_x11,
|
|
XIEvent *event)
|
|
{
|
|
Display *xdisplay = xdisplay_from_seat (seat_x11);
|
|
XIPropertyEvent *xev = (XIPropertyEvent *) event;
|
|
Atom serial_ids_prop;
|
|
ClutterInputDevice *device;
|
|
|
|
serial_ids_prop = XInternAtom (xdisplay, "Wacom Serial IDs", True);
|
|
if (serial_ids_prop == None)
|
|
return;
|
|
|
|
device = g_hash_table_lookup (seat_x11->devices_by_id,
|
|
GINT_TO_POINTER (xev->deviceid));
|
|
if (!device)
|
|
return;
|
|
|
|
if (xev->property == serial_ids_prop)
|
|
{
|
|
int serial_id;
|
|
|
|
serial_id = device_get_tool_serial (seat_x11, device);
|
|
update_tool(seat_x11, device, serial_id);
|
|
}
|
|
}
|
|
|
|
static void
|
|
emulate_motion (MetaSeatX11 *seat_x11,
|
|
double x,
|
|
double y)
|
|
{
|
|
ClutterInputDevice *pointer;
|
|
ClutterEvent *event;
|
|
|
|
pointer = clutter_seat_get_pointer (CLUTTER_SEAT (seat_x11));
|
|
|
|
event = clutter_event_motion_new (CLUTTER_EVENT_FLAG_SYNTHETIC,
|
|
CLUTTER_CURRENT_TIME,
|
|
pointer,
|
|
NULL, 0,
|
|
GRAPHENE_POINT_INIT (x, y),
|
|
GRAPHENE_POINT_INIT (0, 0),
|
|
GRAPHENE_POINT_INIT (0, 0),
|
|
GRAPHENE_POINT_INIT (0, 0),
|
|
NULL);
|
|
|
|
clutter_event_put (event);
|
|
clutter_event_free (event);
|
|
}
|
|
|
|
static void
|
|
translate_raw_event (MetaSeatX11 *seat_x11,
|
|
XEvent *xevent)
|
|
{
|
|
ClutterInputDevice *device;
|
|
XGenericEventCookie *cookie;
|
|
XIEvent *xi_event;
|
|
XIRawEvent *xev;
|
|
float x,y;
|
|
|
|
cookie = &xevent->xcookie;
|
|
xi_event = (XIEvent *) cookie->data;
|
|
xev = (XIRawEvent *) xi_event;
|
|
|
|
device = g_hash_table_lookup (seat_x11->devices_by_id,
|
|
GINT_TO_POINTER (xev->deviceid));
|
|
if (device == NULL)
|
|
return;
|
|
|
|
switch (cookie->evtype)
|
|
{
|
|
case XI_RawMotion:
|
|
g_debug ("raw motion: device:%d '%s'",
|
|
meta_input_device_x11_get_device_id (device),
|
|
clutter_input_device_get_device_name (device));
|
|
|
|
/* We don't get actual pointer location with raw events, and we cannot
|
|
* rely on `clutter_input_device_get_coords()` either because of
|
|
* unreparented toplevels (like all client-side decoration windows),
|
|
* so we need to explicitly query the pointer here...
|
|
*/
|
|
if (meta_input_device_x11_get_pointer_location (device, &x, &y))
|
|
{
|
|
if (_clutter_is_input_pointer_a11y_enabled (device))
|
|
_clutter_input_pointer_a11y_on_motion_event (device, x, y);
|
|
if (!seat_x11->has_pointer_focus)
|
|
emulate_motion (seat_x11, x, y);
|
|
}
|
|
break;
|
|
case XI_RawButtonPress:
|
|
case XI_RawButtonRelease:
|
|
g_debug ("raw button %s: device:%d '%s' button %i",
|
|
cookie->evtype == XI_RawButtonPress
|
|
? "press "
|
|
: "release",
|
|
meta_input_device_x11_get_device_id (device),
|
|
clutter_input_device_get_device_name (device),
|
|
xev->detail);
|
|
if (_clutter_is_input_pointer_a11y_enabled (device))
|
|
{
|
|
_clutter_input_pointer_a11y_on_button_event (device,
|
|
xev->detail,
|
|
(cookie->evtype == XI_RawButtonPress));
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
translate_pad_axis (ClutterInputDevice *device,
|
|
XIValuatorState *valuators,
|
|
ClutterEventType *evtype,
|
|
uint32_t *number,
|
|
double *value)
|
|
{
|
|
double *values;
|
|
int i;
|
|
|
|
values = valuators->values;
|
|
|
|
for (i = PAD_AXIS_FIRST; i < valuators->mask_len * 8; i++)
|
|
{
|
|
double val;
|
|
uint32_t axis_number = 0;
|
|
|
|
if (!XIMaskIsSet (valuators->mask, i))
|
|
continue;
|
|
|
|
val = *values++;
|
|
if (val <= 0)
|
|
continue;
|
|
|
|
meta_input_device_x11_translate_axis (device, i, val, value);
|
|
|
|
if (i == PAD_AXIS_RING1 || i == PAD_AXIS_RING2)
|
|
{
|
|
*evtype = CLUTTER_PAD_RING;
|
|
(*value) *= 360.0;
|
|
}
|
|
else if (i == PAD_AXIS_STRIP1 || i == PAD_AXIS_STRIP2)
|
|
{
|
|
*evtype = CLUTTER_PAD_STRIP;
|
|
}
|
|
else
|
|
continue;
|
|
|
|
if (i == PAD_AXIS_STRIP2 || i == PAD_AXIS_RING2)
|
|
axis_number++;
|
|
|
|
*number = axis_number;
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static ClutterEvent *
|
|
translate_pad_axis_event (XIDeviceEvent *xev,
|
|
ClutterInputDevice *device)
|
|
{
|
|
double value;
|
|
uint32_t number, mode = 0;
|
|
ClutterEventType evtype;
|
|
ClutterEvent *event = NULL;
|
|
|
|
if (!translate_pad_axis (device, &xev->valuators,
|
|
&evtype, &number, &value))
|
|
return FALSE;
|
|
|
|
/* When touching a ring/strip a first XI_Motion event
|
|
* is generated. Use it to reset the pad state, so
|
|
* later events actually have a directionality.
|
|
*/
|
|
if (xev->evtype == XI_Motion)
|
|
value = -1;
|
|
|
|
#ifdef HAVE_LIBWACOM
|
|
mode = meta_input_device_x11_get_pad_group_mode (device, number);
|
|
#endif
|
|
|
|
if (evtype == CLUTTER_PAD_RING)
|
|
{
|
|
event = clutter_event_pad_ring_new (CLUTTER_EVENT_NONE,
|
|
ms2us (xev->time),
|
|
device,
|
|
CLUTTER_INPUT_DEVICE_PAD_SOURCE_UNKNOWN,
|
|
number,
|
|
0,
|
|
value,
|
|
mode);
|
|
}
|
|
else
|
|
{
|
|
event = clutter_event_pad_strip_new (CLUTTER_EVENT_NONE,
|
|
ms2us (xev->time),
|
|
device,
|
|
CLUTTER_INPUT_DEVICE_PAD_SOURCE_UNKNOWN,
|
|
number,
|
|
0,
|
|
value,
|
|
mode);
|
|
}
|
|
|
|
g_debug ("%s: win:0x%x, device:%d '%s', time:%lu "
|
|
"(value:%f)",
|
|
evtype == CLUTTER_PAD_RING
|
|
? "pad ring "
|
|
: "pad strip",
|
|
(unsigned int) xev->event,
|
|
meta_input_device_x11_get_device_id (device),
|
|
clutter_input_device_get_device_name (device),
|
|
xev->time, value);
|
|
|
|
return event;
|
|
}
|
|
|
|
static ClutterStage *
|
|
get_event_stage (MetaSeatX11 *seat_x11,
|
|
XIEvent *xi_event)
|
|
{
|
|
Window xwindow = None;
|
|
|
|
switch (xi_event->evtype)
|
|
{
|
|
case XI_KeyPress:
|
|
case XI_KeyRelease:
|
|
case XI_ButtonPress:
|
|
case XI_ButtonRelease:
|
|
case XI_Motion:
|
|
case XI_TouchBegin:
|
|
case XI_TouchUpdate:
|
|
case XI_TouchEnd:
|
|
{
|
|
XIDeviceEvent *xev = (XIDeviceEvent *) xi_event;
|
|
|
|
xwindow = xev->event;
|
|
}
|
|
break;
|
|
|
|
case XI_Enter:
|
|
case XI_Leave:
|
|
case XI_FocusIn:
|
|
case XI_FocusOut:
|
|
{
|
|
XIEnterEvent *xev = (XIEnterEvent *) xi_event;
|
|
|
|
xwindow = xev->event;
|
|
}
|
|
break;
|
|
|
|
case XI_HierarchyChanged:
|
|
return CLUTTER_STAGE (meta_backend_get_stage (seat_x11->backend));
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (xwindow == None)
|
|
return NULL;
|
|
|
|
return meta_x11_get_stage_from_window (xwindow);
|
|
}
|
|
|
|
/*
|
|
* print_key_sym: Translate a symbol to its printable form if any
|
|
* @symbol: the symbol to translate
|
|
* @buffer: the buffer where to put the translated string
|
|
* @len: size of the buffer
|
|
*
|
|
* Translates @symbol into a printable representation in @buffer, if possible.
|
|
*
|
|
* Return value: The number of bytes of the translated string, 0 if the
|
|
* symbol can't be printed
|
|
*
|
|
* Note: The code is derived from libX11's src/KeyBind.c
|
|
* Copyright 1985, 1987, 1998 The Open Group
|
|
*
|
|
* Note: This code works for Latin-1 symbols. clutter_keysym_to_unicode()
|
|
* does the work for the other keysyms.
|
|
*/
|
|
static int
|
|
print_keysym (uint32_t symbol,
|
|
char *buffer,
|
|
int len)
|
|
{
|
|
unsigned long high_bytes;
|
|
unsigned char c;
|
|
|
|
high_bytes = symbol >> 8;
|
|
if (!(len &&
|
|
((high_bytes == 0) ||
|
|
((high_bytes == 0xFF) &&
|
|
(((symbol >= CLUTTER_KEY_BackSpace) &&
|
|
(symbol <= CLUTTER_KEY_Clear)) ||
|
|
(symbol == CLUTTER_KEY_Return) ||
|
|
(symbol == CLUTTER_KEY_Escape) ||
|
|
(symbol == CLUTTER_KEY_KP_Space) ||
|
|
(symbol == CLUTTER_KEY_KP_Tab) ||
|
|
(symbol == CLUTTER_KEY_KP_Enter) ||
|
|
((symbol >= CLUTTER_KEY_KP_Multiply) &&
|
|
(symbol <= CLUTTER_KEY_KP_9)) ||
|
|
(symbol == CLUTTER_KEY_KP_Equal) ||
|
|
(symbol == CLUTTER_KEY_Delete))))))
|
|
return 0;
|
|
|
|
/* if X keysym, convert to ascii by grabbing low 7 bits */
|
|
if (symbol == CLUTTER_KEY_KP_Space)
|
|
c = CLUTTER_KEY_space & 0x7F; /* patch encoding botch */
|
|
else if (high_bytes == 0xFF)
|
|
c = symbol & 0x7F;
|
|
else
|
|
c = symbol & 0xFF;
|
|
|
|
buffer[0] = c;
|
|
return 1;
|
|
}
|
|
|
|
static double *
|
|
translate_axes (ClutterInputDevice *device,
|
|
double x,
|
|
double y,
|
|
XIValuatorState *valuators)
|
|
{
|
|
uint32_t i;
|
|
double *retval;
|
|
double *values;
|
|
|
|
retval = g_new0 (double, CLUTTER_INPUT_AXIS_LAST);
|
|
values = valuators->values;
|
|
|
|
for (i = 0; i < valuators->mask_len * 8; i++)
|
|
{
|
|
ClutterInputAxis axis;
|
|
double val;
|
|
|
|
if (!XIMaskIsSet (valuators->mask, i))
|
|
continue;
|
|
if (!meta_input_device_x11_get_axis (device, i, &axis))
|
|
continue;
|
|
|
|
val = *values++;
|
|
|
|
switch (axis)
|
|
{
|
|
case CLUTTER_INPUT_AXIS_X:
|
|
retval[axis] = x;
|
|
break;
|
|
|
|
case CLUTTER_INPUT_AXIS_Y:
|
|
retval[axis] = y;
|
|
break;
|
|
|
|
default:
|
|
meta_input_device_x11_translate_axis (device, i, val, &retval[axis]);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
static double
|
|
scroll_valuators_changed (ClutterInputDevice *device,
|
|
XIValuatorState *valuators,
|
|
double *dx_p,
|
|
double *dy_p)
|
|
{
|
|
gboolean retval = FALSE;
|
|
uint32_t n_axes, n_val, i;
|
|
double *values;
|
|
|
|
n_axes = meta_input_device_x11_get_n_axes (device);
|
|
values = valuators->values;
|
|
|
|
*dx_p = *dy_p = 0.0;
|
|
|
|
n_val = 0;
|
|
|
|
for (i = 0; i < MIN (valuators->mask_len * 8, n_axes); i++)
|
|
{
|
|
ClutterScrollDirection direction;
|
|
double delta;
|
|
|
|
if (!XIMaskIsSet (valuators->mask, i))
|
|
continue;
|
|
|
|
if (meta_input_device_x11_get_scroll_delta (device, i,
|
|
values[n_val],
|
|
&direction,
|
|
&delta))
|
|
{
|
|
retval = TRUE;
|
|
|
|
if (direction == CLUTTER_SCROLL_UP ||
|
|
direction == CLUTTER_SCROLL_DOWN)
|
|
*dy_p = delta;
|
|
else
|
|
*dx_p = delta;
|
|
}
|
|
|
|
n_val += 1;
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
static void
|
|
translate_coords (MetaStageX11 *stage_x11,
|
|
double event_x,
|
|
double event_y,
|
|
float *x_out,
|
|
float *y_out)
|
|
{
|
|
MetaStageImpl *stage_impl = META_STAGE_IMPL (stage_x11);
|
|
ClutterActor *stage = CLUTTER_ACTOR (stage_impl->wrapper);
|
|
float stage_width;
|
|
float stage_height;
|
|
|
|
clutter_actor_get_size (stage, &stage_width, &stage_height);
|
|
|
|
*x_out = CLAMP (event_x, 0, stage_width);
|
|
*y_out = CLAMP (event_y, 0, stage_height);
|
|
}
|
|
|
|
static void
|
|
on_keymap_state_change (MetaKeymapX11 *keymap_x11,
|
|
gpointer data)
|
|
{
|
|
ClutterSeat *seat = data;
|
|
MetaSeatX11 *seat_x11 = META_SEAT_X11 (seat);
|
|
MetaInputSettings *input_settings;
|
|
MetaKbdA11ySettings kbd_a11y_settings;
|
|
|
|
/* On keymaps state change, just reapply the current settings, it'll
|
|
* take care of enabling/disabling mousekeys based on NumLock state.
|
|
*/
|
|
input_settings = meta_backend_get_input_settings (seat_x11->backend);
|
|
meta_input_settings_get_kbd_a11y_settings (input_settings, &kbd_a11y_settings);
|
|
meta_seat_x11_apply_kbd_a11y_settings (seat, &kbd_a11y_settings);
|
|
}
|
|
|
|
static void
|
|
meta_seat_x11_set_property (GObject *object,
|
|
guint prop_id,
|
|
const GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
MetaSeatX11 *seat_x11 = META_SEAT_X11 (object);
|
|
|
|
switch (prop_id)
|
|
{
|
|
case PROP_BACKEND:
|
|
seat_x11->backend = g_value_get_object (value);
|
|
break;
|
|
case PROP_OPCODE:
|
|
seat_x11->opcode = g_value_get_int (value);
|
|
break;
|
|
case PROP_POINTER_ID:
|
|
seat_x11->pointer_id = g_value_get_int (value);
|
|
break;
|
|
case PROP_KEYBOARD_ID:
|
|
seat_x11->keyboard_id = g_value_get_int (value);
|
|
break;
|
|
case PROP_TOUCH_MODE:
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
}
|
|
}
|
|
|
|
static void
|
|
meta_seat_x11_get_property (GObject *object,
|
|
guint prop_id,
|
|
GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
MetaSeatX11 *seat_x11 = META_SEAT_X11 (object);
|
|
|
|
switch (prop_id)
|
|
{
|
|
case PROP_BACKEND:
|
|
g_value_set_object (value, seat_x11->backend);
|
|
break;
|
|
case PROP_OPCODE:
|
|
g_value_set_int (value, seat_x11->opcode);
|
|
break;
|
|
case PROP_POINTER_ID:
|
|
g_value_set_int (value, seat_x11->pointer_id);
|
|
break;
|
|
case PROP_KEYBOARD_ID:
|
|
g_value_set_int (value, seat_x11->keyboard_id);
|
|
break;
|
|
case PROP_TOUCH_MODE:
|
|
g_value_set_boolean (value, seat_x11->touch_mode);
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
}
|
|
}
|
|
|
|
void
|
|
meta_seat_x11_notify_devices (MetaSeatX11 *seat_x11,
|
|
ClutterStage *stage)
|
|
{
|
|
GHashTableIter iter;
|
|
ClutterInputDevice *device;
|
|
|
|
g_hash_table_iter_init (&iter, seat_x11->devices_by_id);
|
|
while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &device))
|
|
{
|
|
ClutterEvent *event;
|
|
|
|
event = clutter_event_device_notify_new (CLUTTER_DEVICE_ADDED,
|
|
CLUTTER_EVENT_NONE,
|
|
CLUTTER_CURRENT_TIME,
|
|
device);
|
|
clutter_event_put (event);
|
|
clutter_event_free (event);
|
|
}
|
|
}
|
|
|
|
static void
|
|
meta_seat_x11_constructed (GObject *object)
|
|
{
|
|
MetaSeatX11 *seat_x11 = META_SEAT_X11 (object);
|
|
Display *xdisplay = xdisplay_from_seat (seat_x11);
|
|
Window root_xwindow = root_xwindow_from_seat (seat_x11);
|
|
ClutterBackend *clutter_backend =
|
|
meta_backend_get_clutter_backend (seat_x11->backend);
|
|
XIDeviceInfo *info;
|
|
XIEventMask event_mask;
|
|
unsigned char mask[XIMaskLen(XI_LASTEVENT)] = { 0, };
|
|
int n_devices, i;
|
|
#ifdef HAVE_LIBGUDEV
|
|
const char *udev_subsystems[] = { "input", NULL };
|
|
|
|
seat_x11->udev_client = g_udev_client_new (udev_subsystems);
|
|
#endif
|
|
|
|
info = XIQueryDevice (xdisplay, XIAllDevices, &n_devices);
|
|
|
|
for (i = 0; i < n_devices; i++)
|
|
{
|
|
XIDeviceInfo *xi_device = &info[i];
|
|
|
|
if (!xi_device->enabled)
|
|
continue;
|
|
|
|
add_device (seat_x11, clutter_backend, xi_device);
|
|
}
|
|
|
|
XIFreeDeviceInfo (info);
|
|
|
|
XISetMask (mask, XI_HierarchyChanged);
|
|
XISetMask (mask, XI_DeviceChanged);
|
|
XISetMask (mask, XI_PropertyEvent);
|
|
|
|
event_mask.deviceid = XIAllDevices;
|
|
event_mask.mask_len = sizeof (mask);
|
|
event_mask.mask = mask;
|
|
|
|
XISelectEvents (xdisplay, root_xwindow,
|
|
&event_mask, 1);
|
|
|
|
memset(mask, 0, sizeof (mask));
|
|
XISetMask (mask, XI_RawMotion);
|
|
XISetMask (mask, XI_RawButtonPress);
|
|
XISetMask (mask, XI_RawButtonRelease);
|
|
|
|
if (meta_backend_x11_get_barriers (META_BACKEND_X11 (seat_x11->backend)))
|
|
{
|
|
XISetMask (mask, XI_BarrierHit);
|
|
XISetMask (mask, XI_BarrierLeave);
|
|
}
|
|
|
|
event_mask.deviceid = XIAllMasterDevices;
|
|
event_mask.mask_len = sizeof (mask);
|
|
event_mask.mask = mask;
|
|
|
|
XISelectEvents (xdisplay, root_xwindow,
|
|
&event_mask, 1);
|
|
|
|
XSync (xdisplay, False);
|
|
|
|
seat_x11->keymap = g_object_new (META_TYPE_KEYMAP_X11,
|
|
"backend", seat_x11->backend,
|
|
NULL);
|
|
g_signal_connect (seat_x11->keymap,
|
|
"state-changed",
|
|
G_CALLBACK (on_keymap_state_change),
|
|
seat_x11);
|
|
|
|
meta_seat_x11_a11y_init (CLUTTER_SEAT (seat_x11));
|
|
|
|
if (G_OBJECT_CLASS (meta_seat_x11_parent_class)->constructed)
|
|
G_OBJECT_CLASS (meta_seat_x11_parent_class)->constructed (object);
|
|
}
|
|
|
|
static void
|
|
meta_seat_x11_finalize (GObject *object)
|
|
{
|
|
MetaSeatX11 *seat_x11 = META_SEAT_X11 (object);
|
|
|
|
#ifdef HAVE_LIBGUDEV
|
|
g_clear_object (&seat_x11->udev_client);
|
|
#endif
|
|
|
|
g_hash_table_unref (seat_x11->devices_by_id);
|
|
g_hash_table_unref (seat_x11->tools_by_serial);
|
|
g_hash_table_unref (seat_x11->touch_coords);
|
|
g_list_free (seat_x11->devices);
|
|
|
|
G_OBJECT_CLASS (meta_seat_x11_parent_class)->finalize (object);
|
|
}
|
|
|
|
static ClutterInputDevice *
|
|
meta_seat_x11_get_pointer (ClutterSeat *seat)
|
|
{
|
|
MetaSeatX11 *seat_x11 = META_SEAT_X11 (seat);
|
|
|
|
return seat_x11->core_pointer;
|
|
}
|
|
|
|
static ClutterInputDevice *
|
|
meta_seat_x11_get_keyboard (ClutterSeat *seat)
|
|
{
|
|
MetaSeatX11 *seat_x11 = META_SEAT_X11 (seat);
|
|
|
|
return seat_x11->core_keyboard;
|
|
}
|
|
|
|
static const GList *
|
|
meta_seat_x11_peek_devices (ClutterSeat *seat)
|
|
{
|
|
MetaSeatX11 *seat_x11 = META_SEAT_X11 (seat);
|
|
|
|
return (const GList *) seat_x11->devices;
|
|
}
|
|
|
|
static void
|
|
meta_seat_x11_bell_notify (ClutterSeat *seat)
|
|
{
|
|
MetaSeatX11 *seat_x11 = META_SEAT_X11 (seat);
|
|
MetaContext *context = meta_backend_get_context (seat_x11->backend);
|
|
MetaDisplay *display = meta_context_get_display (context);
|
|
|
|
meta_bell_notify (display, NULL);
|
|
}
|
|
|
|
static ClutterKeymap *
|
|
meta_seat_x11_get_keymap (ClutterSeat *seat)
|
|
{
|
|
return CLUTTER_KEYMAP (META_SEAT_X11 (seat)->keymap);
|
|
}
|
|
|
|
static ClutterVirtualInputDevice *
|
|
meta_seat_x11_create_virtual_device (ClutterSeat *seat,
|
|
ClutterInputDeviceType device_type)
|
|
{
|
|
return g_object_new (META_TYPE_VIRTUAL_INPUT_DEVICE_X11,
|
|
"seat", seat,
|
|
"device-type", device_type,
|
|
NULL);
|
|
}
|
|
|
|
static ClutterVirtualDeviceType
|
|
meta_seat_x11_get_supported_virtual_device_types (ClutterSeat *seat)
|
|
{
|
|
return (CLUTTER_VIRTUAL_DEVICE_TYPE_KEYBOARD |
|
|
CLUTTER_VIRTUAL_DEVICE_TYPE_POINTER);
|
|
}
|
|
|
|
static void
|
|
meta_seat_x11_warp_pointer (ClutterSeat *seat,
|
|
int x,
|
|
int y)
|
|
{
|
|
MetaSeatX11 *seat_x11 = META_SEAT_X11 (seat);
|
|
Display *xdisplay = xdisplay_from_seat (seat_x11);
|
|
Window root_xwindow = root_xwindow_from_seat (seat_x11);
|
|
|
|
mtk_x11_error_trap_push (xdisplay);
|
|
XIWarpPointer (xdisplay,
|
|
seat_x11->pointer_id,
|
|
None,
|
|
root_xwindow,
|
|
0, 0, 0, 0,
|
|
x, y);
|
|
mtk_x11_error_trap_pop (xdisplay);
|
|
}
|
|
|
|
static void
|
|
meta_seat_x11_init_pointer_position (ClutterSeat *seat,
|
|
float x,
|
|
float y)
|
|
{
|
|
MetaSeatX11 *seat_x11 = META_SEAT_X11 (seat);
|
|
Display *xdisplay = xdisplay_from_seat (seat_x11);
|
|
Window root_xwindow = root_xwindow_from_seat (seat_x11);
|
|
|
|
mtk_x11_error_trap_push (xdisplay);
|
|
XIWarpPointer (xdisplay,
|
|
seat_x11->pointer_id,
|
|
None,
|
|
root_xwindow,
|
|
0, 0, 0, 0,
|
|
(int) x, (int) y);
|
|
mtk_x11_error_trap_pop (xdisplay);
|
|
}
|
|
|
|
static uint32_t
|
|
translate_state (XIButtonState *button_state,
|
|
XIModifierState *modifier_state,
|
|
XIGroupState *group_state)
|
|
{
|
|
uint32_t state = 0;
|
|
int i;
|
|
|
|
if (modifier_state)
|
|
state |= modifier_state->effective;
|
|
|
|
if (button_state)
|
|
{
|
|
for (i = 1; i < button_state->mask_len * 8; i++)
|
|
{
|
|
if (!XIMaskIsSet (button_state->mask, i))
|
|
continue;
|
|
|
|
switch (i)
|
|
{
|
|
case 1:
|
|
state |= CLUTTER_BUTTON1_MASK;
|
|
break;
|
|
case 2:
|
|
state |= CLUTTER_BUTTON2_MASK;
|
|
break;
|
|
case 3:
|
|
state |= CLUTTER_BUTTON3_MASK;
|
|
break;
|
|
case 8:
|
|
state |= CLUTTER_BUTTON4_MASK;
|
|
break;
|
|
case 9:
|
|
state |= CLUTTER_BUTTON5_MASK;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (group_state)
|
|
state |= XkbBuildCoreState (0, group_state->effective);
|
|
|
|
return state;
|
|
}
|
|
|
|
static gboolean
|
|
meta_seat_x11_query_state (ClutterSeat *seat,
|
|
ClutterInputDevice *device,
|
|
ClutterEventSequence *sequence,
|
|
graphene_point_t *coords,
|
|
ClutterModifierType *modifiers)
|
|
{
|
|
MetaSeatX11 *seat_x11 = META_SEAT_X11 (seat);
|
|
MetaBackendX11 *backend_x11 = META_BACKEND_X11 (seat_x11->backend);
|
|
Display *xdisplay = xdisplay_from_seat (seat_x11);
|
|
Window root_ret, child_ret;
|
|
double root_x, root_y, win_x, win_y;
|
|
XIButtonState button_state = { 0 };
|
|
XIModifierState modifier_state;
|
|
XIGroupState group_state;
|
|
|
|
mtk_x11_error_trap_push (xdisplay);
|
|
XIQueryPointer (xdisplay,
|
|
seat_x11->pointer_id,
|
|
meta_backend_x11_get_xwindow (backend_x11),
|
|
&root_ret, &child_ret,
|
|
&root_x, &root_y, &win_x, &win_y,
|
|
&button_state, &modifier_state, &group_state);
|
|
if (mtk_x11_error_trap_pop_with_return (xdisplay))
|
|
{
|
|
g_free (button_state.mask);
|
|
return FALSE;
|
|
}
|
|
|
|
if (sequence)
|
|
{
|
|
MetaTouchInfo *touch_info;
|
|
|
|
touch_info = g_hash_table_lookup (seat_x11->touch_coords, sequence);
|
|
if (!touch_info)
|
|
{
|
|
g_free (button_state.mask);
|
|
return FALSE;
|
|
}
|
|
|
|
if (coords)
|
|
{
|
|
coords->x = touch_info->x;
|
|
coords->y = touch_info->y;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (coords)
|
|
{
|
|
coords->x = win_x;
|
|
coords->y = win_y;
|
|
}
|
|
}
|
|
|
|
if (modifiers)
|
|
*modifiers = translate_state (&button_state, &modifier_state, &group_state);
|
|
|
|
g_free (button_state.mask);
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
meta_seat_x11_update_touchpoint (MetaSeatX11 *seat,
|
|
ClutterEventSequence *sequence,
|
|
double x,
|
|
double y)
|
|
{
|
|
MetaTouchInfo *touch_info;
|
|
|
|
touch_info = g_hash_table_lookup (seat->touch_coords, sequence);
|
|
if (!touch_info)
|
|
{
|
|
touch_info = g_new0 (MetaTouchInfo, 1);
|
|
touch_info->sequence = sequence;
|
|
g_hash_table_insert (seat->touch_coords, sequence, touch_info);
|
|
}
|
|
|
|
touch_info->x = x;
|
|
touch_info->y = y;
|
|
}
|
|
|
|
static void
|
|
meta_seat_x11_remove_touchpoint (MetaSeatX11 *seat,
|
|
ClutterEventSequence *sequence)
|
|
{
|
|
g_hash_table_remove (seat->touch_coords, sequence);
|
|
}
|
|
|
|
static void
|
|
meta_touch_info_free (MetaTouchInfo *touch_info)
|
|
{
|
|
g_free (touch_info);
|
|
}
|
|
|
|
static ClutterGrabState
|
|
meta_seat_x11_grab (ClutterSeat *seat,
|
|
uint32_t time)
|
|
{
|
|
MetaSeatX11 *seat_x11 = META_SEAT_X11 (seat);
|
|
MetaBackend *backend = seat_x11->backend;
|
|
ClutterGrabState state = CLUTTER_GRAB_STATE_NONE;
|
|
|
|
g_return_val_if_fail (seat_x11->grab_state == CLUTTER_GRAB_STATE_NONE,
|
|
seat_x11->grab_state);
|
|
|
|
if (meta_backend_grab_device (backend,
|
|
META_VIRTUAL_CORE_POINTER_ID,
|
|
time))
|
|
state |= CLUTTER_GRAB_STATE_POINTER;
|
|
|
|
if (meta_backend_grab_device (backend,
|
|
META_VIRTUAL_CORE_KEYBOARD_ID,
|
|
time))
|
|
state |= CLUTTER_GRAB_STATE_KEYBOARD;
|
|
|
|
seat_x11->grab_state = state;
|
|
|
|
meta_backend_x11_sync_pointer (META_BACKEND_X11 (backend));
|
|
|
|
return state;
|
|
}
|
|
|
|
static void
|
|
meta_seat_x11_ungrab (ClutterSeat *seat,
|
|
uint32_t time)
|
|
{
|
|
MetaSeatX11 *seat_x11 = META_SEAT_X11 (seat);
|
|
MetaBackend *backend = seat_x11->backend;
|
|
|
|
if ((seat_x11->grab_state & CLUTTER_GRAB_STATE_POINTER) != 0)
|
|
{
|
|
meta_backend_ungrab_device (backend,
|
|
META_VIRTUAL_CORE_POINTER_ID,
|
|
time);
|
|
}
|
|
|
|
if ((seat_x11->grab_state & CLUTTER_GRAB_STATE_KEYBOARD) != 0)
|
|
{
|
|
meta_backend_ungrab_device (backend,
|
|
META_VIRTUAL_CORE_KEYBOARD_ID,
|
|
time);
|
|
}
|
|
|
|
seat_x11->grab_state = CLUTTER_GRAB_STATE_NONE;
|
|
|
|
meta_backend_x11_sync_pointer (META_BACKEND_X11 (backend));
|
|
}
|
|
|
|
static void
|
|
meta_seat_x11_class_init (MetaSeatX11Class *klass)
|
|
{
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
ClutterSeatClass *seat_class = CLUTTER_SEAT_CLASS (klass);
|
|
|
|
object_class->set_property = meta_seat_x11_set_property;
|
|
object_class->get_property = meta_seat_x11_get_property;
|
|
object_class->constructed = meta_seat_x11_constructed;
|
|
object_class->finalize = meta_seat_x11_finalize;
|
|
|
|
seat_class->get_pointer = meta_seat_x11_get_pointer;
|
|
seat_class->get_keyboard = meta_seat_x11_get_keyboard;
|
|
seat_class->peek_devices = meta_seat_x11_peek_devices;
|
|
seat_class->bell_notify = meta_seat_x11_bell_notify;
|
|
seat_class->get_keymap = meta_seat_x11_get_keymap;
|
|
seat_class->create_virtual_device = meta_seat_x11_create_virtual_device;
|
|
seat_class->get_supported_virtual_device_types = meta_seat_x11_get_supported_virtual_device_types;
|
|
seat_class->warp_pointer = meta_seat_x11_warp_pointer;
|
|
seat_class->init_pointer_position = meta_seat_x11_init_pointer_position;
|
|
seat_class->handle_event_post = meta_seat_x11_handle_event_post;
|
|
seat_class->query_state = meta_seat_x11_query_state;
|
|
seat_class->grab = meta_seat_x11_grab;
|
|
seat_class->ungrab = meta_seat_x11_ungrab;
|
|
|
|
props[PROP_BACKEND] =
|
|
g_param_spec_object ("backend", NULL, NULL,
|
|
META_TYPE_BACKEND,
|
|
G_PARAM_READWRITE |
|
|
G_PARAM_CONSTRUCT_ONLY);
|
|
props[PROP_OPCODE] =
|
|
g_param_spec_int ("opcode", NULL, NULL,
|
|
0, G_MAXINT, 0,
|
|
G_PARAM_READWRITE |
|
|
G_PARAM_CONSTRUCT_ONLY);
|
|
props[PROP_POINTER_ID] =
|
|
g_param_spec_int ("pointer-id", NULL, NULL,
|
|
2, G_MAXINT, 2,
|
|
G_PARAM_READWRITE |
|
|
G_PARAM_CONSTRUCT_ONLY);
|
|
props[PROP_KEYBOARD_ID] =
|
|
g_param_spec_int ("keyboard-id", NULL, NULL,
|
|
2, G_MAXINT, 2,
|
|
G_PARAM_READWRITE |
|
|
G_PARAM_CONSTRUCT_ONLY);
|
|
|
|
g_object_class_install_properties (object_class, N_PROPS, props);
|
|
|
|
g_object_class_override_property (object_class, PROP_TOUCH_MODE,
|
|
"touch-mode");
|
|
}
|
|
|
|
static void
|
|
meta_seat_x11_init (MetaSeatX11 *seat)
|
|
{
|
|
seat->devices_by_id = g_hash_table_new_full (NULL, NULL,
|
|
NULL,
|
|
(GDestroyNotify) g_object_unref);
|
|
seat->tools_by_serial = g_hash_table_new_full (NULL, NULL, NULL,
|
|
(GDestroyNotify) g_object_unref);
|
|
seat->touch_coords = g_hash_table_new_full (NULL, NULL, NULL,
|
|
(GDestroyNotify) meta_touch_info_free);
|
|
}
|
|
|
|
MetaSeatX11 *
|
|
meta_seat_x11_new (MetaBackend *backend,
|
|
int opcode,
|
|
int logical_pointer,
|
|
int logical_keyboard)
|
|
{
|
|
MetaSeatX11 *seat_x11;
|
|
|
|
seat_x11 = g_object_new (META_TYPE_SEAT_X11,
|
|
"backend", backend,
|
|
"opcode", opcode,
|
|
"pointer-id", logical_pointer,
|
|
"keyboard-id", logical_keyboard,
|
|
NULL);
|
|
|
|
return seat_x11;
|
|
}
|
|
|
|
MetaBackend *
|
|
meta_seat_x11_get_backend (MetaSeatX11 *seat_x11)
|
|
{
|
|
return seat_x11->backend;
|
|
}
|
|
|
|
static ClutterInputDevice *
|
|
get_source_device_checked (MetaSeatX11 *seat,
|
|
XIDeviceEvent *xev)
|
|
{
|
|
ClutterInputDevice *source_device;
|
|
|
|
source_device = g_hash_table_lookup (seat->devices_by_id,
|
|
GINT_TO_POINTER (xev->sourceid));
|
|
|
|
if (!source_device)
|
|
g_warning ("Impossible to get the source device with id %d for event of "
|
|
"type %d", xev->sourceid, xev->evtype);
|
|
|
|
return source_device;
|
|
}
|
|
|
|
static uint32_t
|
|
evdev_button_code (uint32_t x_button)
|
|
{
|
|
uint32_t button;
|
|
|
|
switch (x_button)
|
|
{
|
|
case 1:
|
|
button = BTN_LEFT;
|
|
break;
|
|
|
|
/* The evdev input right and middle button numbers are swapped
|
|
relative to how Clutter numbers them */
|
|
case 2:
|
|
button = BTN_MIDDLE;
|
|
break;
|
|
|
|
case 3:
|
|
button = BTN_RIGHT;
|
|
break;
|
|
|
|
default:
|
|
button = x_button + (BTN_LEFT - 1) + 4;
|
|
break;
|
|
}
|
|
|
|
return button;
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
ClutterEvent *
|
|
meta_seat_x11_translate_event (MetaSeatX11 *seat,
|
|
XEvent *xevent)
|
|
{
|
|
Display *xdisplay = xdisplay_from_seat (seat);
|
|
ClutterBackend *clutter_backend =
|
|
meta_backend_get_clutter_backend (seat->backend);
|
|
ClutterStage *stage = NULL;
|
|
MetaStageX11 *stage_x11 = NULL;
|
|
ClutterInputDevice *device, *source_device;
|
|
XGenericEventCookie *cookie;
|
|
ClutterEvent *event = NULL;
|
|
XIEvent *xi_event;
|
|
|
|
if (meta_keymap_x11_handle_event (seat->keymap, xevent))
|
|
return NULL;
|
|
|
|
cookie = &xevent->xcookie;
|
|
|
|
if (cookie->type != GenericEvent ||
|
|
cookie->extension != seat->opcode)
|
|
return NULL;
|
|
|
|
xi_event = (XIEvent *) cookie->data;
|
|
|
|
if (!xi_event)
|
|
return NULL;
|
|
|
|
if (cookie->evtype == XI_RawMotion ||
|
|
cookie->evtype == XI_RawButtonPress ||
|
|
cookie->evtype == XI_RawButtonRelease)
|
|
{
|
|
translate_raw_event (seat, xevent);
|
|
return NULL;
|
|
}
|
|
|
|
if (!(xi_event->evtype == XI_DeviceChanged ||
|
|
xi_event->evtype == XI_PropertyEvent))
|
|
{
|
|
stage = get_event_stage (seat, xi_event);
|
|
if (stage == NULL || CLUTTER_ACTOR_IN_DESTRUCTION (stage))
|
|
return NULL;
|
|
else
|
|
stage_x11 = META_STAGE_X11 (_clutter_stage_get_window (stage));
|
|
}
|
|
|
|
switch (xi_event->evtype)
|
|
{
|
|
case XI_HierarchyChanged:
|
|
{
|
|
XIHierarchyEvent *xev = (XIHierarchyEvent *) xi_event;
|
|
|
|
event = translate_hierarchy_event (clutter_backend, seat, xev);
|
|
}
|
|
break;
|
|
|
|
case XI_DeviceChanged:
|
|
{
|
|
XIDeviceChangedEvent *xev = (XIDeviceChangedEvent *) xi_event;
|
|
|
|
device = g_hash_table_lookup (seat->devices_by_id,
|
|
GINT_TO_POINTER (xev->deviceid));
|
|
source_device = g_hash_table_lookup (seat->devices_by_id,
|
|
GINT_TO_POINTER (xev->sourceid));
|
|
if (device)
|
|
{
|
|
meta_input_device_x11_reset_axes (device);
|
|
translate_device_classes (xdisplay,
|
|
device,
|
|
xev->classes,
|
|
xev->num_classes);
|
|
}
|
|
|
|
if (source_device)
|
|
meta_input_device_x11_reset_scroll_info (source_device);
|
|
}
|
|
break;
|
|
case XI_KeyPress:
|
|
case XI_KeyRelease:
|
|
{
|
|
XIDeviceEvent *xev = (XIDeviceEvent *) xi_event;
|
|
MetaKeymapX11 *keymap_x11 = seat->keymap;
|
|
char buffer[7] = { 0, };
|
|
uint32_t keyval, evcode, keycode;
|
|
ClutterModifierSet raw_modifiers;
|
|
ClutterModifierType state;
|
|
int len;
|
|
gunichar unicode_value;
|
|
|
|
source_device = get_source_device_checked (seat, xev);
|
|
if (!source_device)
|
|
return NULL;
|
|
|
|
raw_modifiers = (ClutterModifierSet) {
|
|
.pressed = xev->mods.base,
|
|
.latched = xev->mods.latched,
|
|
.locked = xev->mods.locked,
|
|
};
|
|
|
|
state = translate_state (&xev->buttons, &xev->mods, &xev->group);
|
|
|
|
keycode = xev->detail;
|
|
|
|
/* clutter-xkb-utils.c adds a fixed offset of 8 to go into XKB's
|
|
* range, so we do the reverse here. */
|
|
evcode = keycode - 8;
|
|
|
|
/* keyval is the key ignoring all modifiers ('1' vs. '!') */
|
|
keyval = meta_keymap_x11_translate_key_state (keymap_x11,
|
|
keycode,
|
|
&state,
|
|
NULL);
|
|
|
|
/* XXX keep this in sync with the evdev device manager */
|
|
len = print_keysym (keyval, buffer, sizeof (buffer));
|
|
if (len == 0)
|
|
{
|
|
/* not printable */
|
|
unicode_value = (gunichar) '\0';
|
|
}
|
|
else
|
|
{
|
|
unicode_value = g_utf8_get_char_validated (buffer, len);
|
|
if (unicode_value == -1 ||
|
|
unicode_value == -2)
|
|
unicode_value = (gunichar) '\0';
|
|
}
|
|
|
|
event = clutter_event_key_new ((xev->evtype == XI_KeyPress) ?
|
|
CLUTTER_KEY_PRESS : CLUTTER_KEY_RELEASE,
|
|
(xev->evtype == XI_KeyPress &&
|
|
xev->flags & XIKeyRepeat) ?
|
|
CLUTTER_EVENT_FLAG_REPEATED :
|
|
CLUTTER_EVENT_NONE,
|
|
ms2us (xev->time),
|
|
source_device,
|
|
raw_modifiers,
|
|
state,
|
|
keyval,
|
|
evcode,
|
|
keycode,
|
|
unicode_value);
|
|
|
|
g_debug ("%s: win:0x%x device:%d source:%d, key: %12s (%d)",
|
|
clutter_event_type (event) == CLUTTER_KEY_PRESS
|
|
? "key press "
|
|
: "key release",
|
|
(unsigned int) stage_x11->xwin,
|
|
xev->deviceid,
|
|
xev->sourceid,
|
|
keyval ? buffer : "(none)",
|
|
keyval);
|
|
|
|
if (xi_event->evtype == XI_KeyPress)
|
|
meta_stage_x11_set_user_time (stage_x11, xev->time);
|
|
}
|
|
break;
|
|
|
|
case XI_ButtonPress:
|
|
case XI_ButtonRelease:
|
|
{
|
|
XIDeviceEvent *xev = (XIDeviceEvent *) xi_event;
|
|
ClutterScrollDirection scroll_direction;
|
|
ClutterModifierType state;
|
|
ClutterInputDeviceTool *tool;
|
|
float x, y;
|
|
int button;
|
|
uint32_t evdev_code;
|
|
double *axes;
|
|
|
|
source_device = get_source_device_checked (seat, xev);
|
|
if (!source_device)
|
|
return NULL;
|
|
|
|
device = g_hash_table_lookup (seat->devices_by_id,
|
|
GINT_TO_POINTER (xev->deviceid));
|
|
|
|
if (clutter_input_device_get_device_type (source_device) == CLUTTER_PAD_DEVICE)
|
|
{
|
|
uint32_t button, group = 0, mode = 0;
|
|
|
|
/* We got these events because of the passive button grab */
|
|
XIAllowEvents (xdisplay, xev->sourceid, XIAsyncDevice, xev->time);
|
|
|
|
if (xev->detail >= 4 && xev->detail <= 7)
|
|
{
|
|
if (xi_event->evtype == XI_ButtonPress)
|
|
event = translate_pad_axis_event (xev, source_device);
|
|
break;
|
|
}
|
|
|
|
/* The 4-7 button range is taken as non-existent on pad devices,
|
|
* let the buttons above that take over this range.
|
|
*/
|
|
if (xev->detail > 7)
|
|
xev->detail -= 4;
|
|
|
|
/* Pad buttons are 0-indexed */
|
|
button = xev->detail - 1;
|
|
|
|
#ifdef HAVE_LIBWACOM
|
|
meta_input_device_x11_update_pad_state (device,
|
|
button,
|
|
(xi_event->evtype == XI_ButtonPress),
|
|
&group,
|
|
&mode);
|
|
#endif
|
|
|
|
event = clutter_event_pad_button_new ((xi_event->evtype == XI_ButtonPress) ?
|
|
CLUTTER_PAD_BUTTON_PRESS :
|
|
CLUTTER_PAD_BUTTON_RELEASE,
|
|
CLUTTER_EVENT_NONE,
|
|
us2ms (xev->time),
|
|
source_device,
|
|
button,
|
|
group,
|
|
mode);
|
|
|
|
g_debug ("%s: win:0x%x, device:%d '%s', time:%lu "
|
|
"(button:%d)",
|
|
(xi_event->evtype == XI_ButtonPress)
|
|
? "pad button press "
|
|
: "pad button release",
|
|
(unsigned int) stage_x11->xwin,
|
|
meta_input_device_x11_get_device_id (device),
|
|
clutter_input_device_get_device_name (device),
|
|
xev->time,
|
|
button);
|
|
break;
|
|
}
|
|
|
|
switch (xev->detail)
|
|
{
|
|
case 4:
|
|
case 5:
|
|
case 6:
|
|
case 7:
|
|
/* we only generate Scroll events on ButtonPress */
|
|
if (xi_event->evtype == XI_ButtonRelease)
|
|
return NULL;
|
|
|
|
if (xev->detail == 4)
|
|
scroll_direction = CLUTTER_SCROLL_UP;
|
|
else if (xev->detail == 5)
|
|
scroll_direction = CLUTTER_SCROLL_DOWN;
|
|
else if (xev->detail == 6)
|
|
scroll_direction = CLUTTER_SCROLL_LEFT;
|
|
else
|
|
scroll_direction = CLUTTER_SCROLL_RIGHT;
|
|
|
|
translate_coords (stage_x11, xev->event_x, xev->event_y, &x, &y);
|
|
state = translate_state (&xev->buttons, &xev->mods, &xev->group);
|
|
tool = meta_input_device_x11_get_current_tool (source_device);
|
|
|
|
event = clutter_event_scroll_discrete_new (CLUTTER_EVENT_NONE,
|
|
ms2us (xev->time),
|
|
source_device,
|
|
tool,
|
|
state,
|
|
GRAPHENE_POINT_INIT (x, y),
|
|
scroll_direction);
|
|
|
|
g_debug ("scroll: win:0x%x, device:%d '%s', time:%d "
|
|
"(direction:%s, "
|
|
"x:%.2f, y:%.2f, "
|
|
"emulated:%s)",
|
|
(unsigned int) stage_x11->xwin,
|
|
meta_input_device_x11_get_device_id (device),
|
|
clutter_input_device_get_device_name (device),
|
|
clutter_event_get_time (event),
|
|
scroll_direction == CLUTTER_SCROLL_UP ? "up" :
|
|
scroll_direction == CLUTTER_SCROLL_DOWN ? "down" :
|
|
scroll_direction == CLUTTER_SCROLL_LEFT ? "left" :
|
|
scroll_direction == CLUTTER_SCROLL_RIGHT ? "right" :
|
|
"invalid",
|
|
x, y,
|
|
(xev->flags & XIPointerEmulated) ? "yes" : "no");
|
|
break;
|
|
|
|
default:
|
|
translate_coords (stage_x11, xev->event_x, xev->event_y, &x, &y);
|
|
button = xev->detail;
|
|
evdev_code = evdev_button_code (xev->detail);
|
|
state = translate_state (&xev->buttons, &xev->mods, &xev->group);
|
|
tool = meta_input_device_x11_get_current_tool (source_device);
|
|
axes = translate_axes (device, x, y, &xev->valuators);
|
|
|
|
/* 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 (xi_event->evtype)
|
|
{
|
|
case XI_ButtonPress:
|
|
state |= (get_modifier_for_button (button));
|
|
break;
|
|
case XI_ButtonRelease:
|
|
state &= ~(get_modifier_for_button (button));
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
event = clutter_event_button_new ((xi_event->evtype == XI_ButtonPress) ?
|
|
CLUTTER_BUTTON_PRESS :
|
|
CLUTTER_BUTTON_RELEASE,
|
|
(xev->flags & XIPointerEmulated) ?
|
|
CLUTTER_EVENT_FLAG_POINTER_EMULATED :
|
|
CLUTTER_EVENT_NONE,
|
|
ms2us (xev->time),
|
|
source_device,
|
|
tool,
|
|
state,
|
|
GRAPHENE_POINT_INIT (x, y),
|
|
button,
|
|
evdev_code,
|
|
axes);
|
|
|
|
g_debug ("%s: win:0x%x, device:%d '%s', time:%lu "
|
|
"(button:%d, "
|
|
"x:%.2f, y:%.2f, "
|
|
"axes:%s, "
|
|
"emulated:%s)",
|
|
(xi_event->evtype == XI_ButtonPress)
|
|
? "button press "
|
|
: "button release",
|
|
(unsigned int) stage_x11->xwin,
|
|
meta_input_device_x11_get_device_id (device),
|
|
clutter_input_device_get_device_name (device),
|
|
xev->time,
|
|
xev->detail,
|
|
x, y,
|
|
axes != NULL ? "yes" : "no",
|
|
(xev->flags & XIPointerEmulated) ? "yes" : "no");
|
|
break;
|
|
}
|
|
|
|
if (xi_event->evtype == XI_ButtonPress)
|
|
meta_stage_x11_set_user_time (stage_x11, xev->time);
|
|
}
|
|
break;
|
|
|
|
case XI_Motion:
|
|
{
|
|
XIDeviceEvent *xev = (XIDeviceEvent *) xi_event;
|
|
double delta_x, delta_y;
|
|
ClutterModifierType state;
|
|
ClutterInputDeviceTool *tool;
|
|
float x, y;
|
|
double *axes;
|
|
|
|
source_device = get_source_device_checked (seat, xev);
|
|
if (!source_device)
|
|
break;
|
|
|
|
device = g_hash_table_lookup (seat->devices_by_id,
|
|
GINT_TO_POINTER (xev->deviceid));
|
|
|
|
if (clutter_input_device_get_device_type (source_device) == CLUTTER_PAD_DEVICE)
|
|
{
|
|
event = translate_pad_axis_event (xev, source_device);
|
|
break;
|
|
}
|
|
|
|
translate_coords (stage_x11, xev->event_x, xev->event_y, &x, &y);
|
|
state = translate_state (&xev->buttons, &xev->mods, &xev->group);
|
|
tool = meta_input_device_x11_get_current_tool (source_device);
|
|
|
|
if (scroll_valuators_changed (source_device,
|
|
&xev->valuators,
|
|
&delta_x, &delta_y))
|
|
{
|
|
event = clutter_event_scroll_smooth_new (CLUTTER_EVENT_NONE,
|
|
ms2us (xev->time),
|
|
source_device,
|
|
tool,
|
|
state,
|
|
GRAPHENE_POINT_INIT (x, y),
|
|
GRAPHENE_POINT_INIT (delta_x, delta_y),
|
|
CLUTTER_SCROLL_SOURCE_UNKNOWN,
|
|
CLUTTER_SCROLL_FINISHED_NONE);
|
|
|
|
g_debug ("smooth scroll: win:0x%x device:%d '%s' (x:%.2f, y:%.2f, delta:%f, %f)",
|
|
(unsigned int) stage_x11->xwin,
|
|
meta_input_device_x11_get_device_id (source_device),
|
|
clutter_input_device_get_device_name (source_device),
|
|
x, y,
|
|
delta_x, delta_y);
|
|
break;
|
|
}
|
|
|
|
axes = translate_axes (device, x, y, &xev->valuators);
|
|
event = clutter_event_motion_new ((xev->flags & XIPointerEmulated) ?
|
|
CLUTTER_EVENT_FLAG_POINTER_EMULATED :
|
|
CLUTTER_EVENT_NONE,
|
|
ms2us (xev->time),
|
|
source_device,
|
|
tool,
|
|
state,
|
|
GRAPHENE_POINT_INIT (x, y),
|
|
GRAPHENE_POINT_INIT (0, 0),
|
|
GRAPHENE_POINT_INIT (0, 0),
|
|
GRAPHENE_POINT_INIT (0, 0),
|
|
axes);
|
|
|
|
g_debug ("motion: win:0x%x device:%d '%s' (x:%.2f, y:%.2f, axes:%s)",
|
|
(unsigned int) stage_x11->xwin,
|
|
meta_input_device_x11_get_device_id (source_device),
|
|
clutter_input_device_get_device_name (source_device),
|
|
x, y,
|
|
axes != NULL ? "yes" : "no");
|
|
}
|
|
break;
|
|
|
|
case XI_TouchBegin:
|
|
case XI_TouchEnd:
|
|
{
|
|
XIDeviceEvent *xev = (XIDeviceEvent *) xi_event;
|
|
ClutterEventType evtype;
|
|
ClutterModifierType state;
|
|
ClutterEventSequence *sequence;
|
|
float x, y;
|
|
|
|
device = g_hash_table_lookup (seat->devices_by_id,
|
|
GINT_TO_POINTER (xev->deviceid));
|
|
source_device = g_hash_table_lookup (seat->devices_by_id,
|
|
GINT_TO_POINTER (xev->sourceid));
|
|
|
|
if (xi_event->evtype == XI_TouchBegin)
|
|
evtype = CLUTTER_TOUCH_BEGIN;
|
|
else
|
|
evtype = CLUTTER_TOUCH_END;
|
|
|
|
translate_coords (stage_x11, xev->event_x, xev->event_y, &x, &y);
|
|
state = translate_state (&xev->buttons, &xev->mods, &xev->group);
|
|
/* "NULL" sequences are special cased in clutter */
|
|
sequence = GINT_TO_POINTER (MAX (1, xev->detail + 1));
|
|
|
|
if (xi_event->evtype == XI_TouchBegin)
|
|
{
|
|
state |= CLUTTER_BUTTON1_MASK;
|
|
|
|
meta_stage_x11_set_user_time (stage_x11, xev->time);
|
|
meta_seat_x11_update_touchpoint (seat,
|
|
sequence,
|
|
xev->root_x,
|
|
xev->root_y);
|
|
}
|
|
else if (xi_event->evtype == XI_TouchEnd)
|
|
{
|
|
meta_seat_x11_remove_touchpoint (seat, sequence);
|
|
}
|
|
|
|
event = clutter_event_touch_new (evtype,
|
|
(xev->flags & XITouchEmulatingPointer) ?
|
|
CLUTTER_EVENT_FLAG_POINTER_EMULATED :
|
|
CLUTTER_EVENT_NONE,
|
|
ms2us (xev->time),
|
|
source_device,
|
|
sequence,
|
|
state,
|
|
GRAPHENE_POINT_INIT (x, y));
|
|
|
|
g_debug ("touch %s: win:0x%x device:%d '%s' (seq:%d, x:%.2f, y:%.2f)",
|
|
evtype == CLUTTER_TOUCH_BEGIN ? "begin" : "end",
|
|
(unsigned int) stage_x11->xwin,
|
|
meta_input_device_x11_get_device_id (device),
|
|
clutter_input_device_get_device_name (device),
|
|
GPOINTER_TO_UINT (sequence),
|
|
x, y);
|
|
}
|
|
break;
|
|
|
|
case XI_TouchUpdate:
|
|
{
|
|
XIDeviceEvent *xev = (XIDeviceEvent *) xi_event;
|
|
ClutterEventSequence *sequence;
|
|
ClutterModifierType state;
|
|
float x, y;
|
|
|
|
device = g_hash_table_lookup (seat->devices_by_id,
|
|
GINT_TO_POINTER (xev->deviceid));
|
|
source_device = g_hash_table_lookup (seat->devices_by_id,
|
|
GINT_TO_POINTER (xev->sourceid));
|
|
|
|
/* "NULL" sequences are special cased in clutter */
|
|
sequence = GINT_TO_POINTER (MAX (1, xev->detail + 1));
|
|
translate_coords (stage_x11, xev->event_x, xev->event_y, &x, &y);
|
|
state = translate_state (&xev->buttons, &xev->mods, &xev->group);
|
|
state |= CLUTTER_BUTTON1_MASK;
|
|
|
|
meta_seat_x11_update_touchpoint (seat,
|
|
sequence,
|
|
xev->root_x,
|
|
xev->root_y);
|
|
|
|
event = clutter_event_touch_new (CLUTTER_TOUCH_UPDATE,
|
|
(xev->flags & XITouchEmulatingPointer) ?
|
|
CLUTTER_EVENT_FLAG_POINTER_EMULATED :
|
|
CLUTTER_EVENT_NONE,
|
|
ms2us (xev->time),
|
|
source_device,
|
|
sequence,
|
|
state,
|
|
GRAPHENE_POINT_INIT (x, y));
|
|
|
|
g_debug ("touch update: win:0x%x device:%d '%s' (seq:%d, x:%.2f, y:%.2f)",
|
|
(unsigned int) stage_x11->xwin,
|
|
meta_input_device_x11_get_device_id (device),
|
|
clutter_input_device_get_device_name (device),
|
|
GPOINTER_TO_UINT (sequence),
|
|
x, y);
|
|
}
|
|
break;
|
|
|
|
case XI_Enter:
|
|
case XI_Leave:
|
|
{
|
|
XIEnterEvent *xev = (XIEnterEvent *) xi_event;
|
|
float x, y;
|
|
|
|
device = g_hash_table_lookup (seat->devices_by_id,
|
|
GINT_TO_POINTER (xev->deviceid));
|
|
|
|
source_device = g_hash_table_lookup (seat->devices_by_id,
|
|
GINT_TO_POINTER (xev->sourceid));
|
|
|
|
translate_coords (stage_x11, xev->event_x, xev->event_y, &x, &y);
|
|
|
|
event = clutter_event_crossing_new ((xi_event->evtype == XI_Enter) ?
|
|
CLUTTER_ENTER : CLUTTER_LEAVE,
|
|
CLUTTER_EVENT_NONE,
|
|
ms2us (xev->time),
|
|
device,
|
|
NULL,
|
|
GRAPHENE_POINT_INIT (x, y),
|
|
CLUTTER_ACTOR (stage), NULL);
|
|
|
|
if (xev->deviceid == seat->pointer_id)
|
|
seat->has_pointer_focus = (xi_event->evtype == XI_Enter);
|
|
|
|
meta_input_device_x11_reset_scroll_info (source_device);
|
|
}
|
|
break;
|
|
|
|
case XI_FocusIn:
|
|
case XI_FocusOut:
|
|
break;
|
|
case XI_PropertyEvent:
|
|
translate_property_event (seat, xi_event);
|
|
break;
|
|
}
|
|
|
|
return event;
|
|
}
|
|
|
|
void
|
|
meta_seat_x11_select_stage_events (MetaSeatX11 *seat,
|
|
ClutterStage *stage)
|
|
{
|
|
Display *xdisplay = xdisplay_from_seat (seat);
|
|
MetaStageX11 *stage_x11;
|
|
XIEventMask xi_event_mask;
|
|
unsigned char *mask;
|
|
int len;
|
|
|
|
stage_x11 = META_STAGE_X11 (_clutter_stage_get_window (stage));
|
|
|
|
len = XIMaskLen (XI_LASTEVENT);
|
|
mask = g_new0 (unsigned char, len);
|
|
|
|
XISetMask (mask, XI_Motion);
|
|
XISetMask (mask, XI_ButtonPress);
|
|
XISetMask (mask, XI_ButtonRelease);
|
|
XISetMask (mask, XI_KeyPress);
|
|
XISetMask (mask, XI_KeyRelease);
|
|
XISetMask (mask, XI_Enter);
|
|
XISetMask (mask, XI_Leave);
|
|
|
|
XISetMask (mask, XI_TouchBegin);
|
|
XISetMask (mask, XI_TouchUpdate);
|
|
XISetMask (mask, XI_TouchEnd);
|
|
|
|
xi_event_mask.deviceid = XIAllMasterDevices;
|
|
xi_event_mask.mask = mask;
|
|
xi_event_mask.mask_len = len;
|
|
|
|
XISelectEvents (xdisplay, stage_x11->xwin, &xi_event_mask, 1);
|
|
|
|
g_free (mask);
|
|
}
|