1
0
Fork 0
mutter-performance-source/clutter/x11/clutter-device-manager-xi2.c
Emmanuele Bassi 1b1e77b469 event/x11: Rework the way we translate X11 events
This is a lump commit that is fairly difficult to break down without
either breaking bisecting or breaking the test cases.

The new design for handling X11 event translation works this way:

  - ClutterBackend::translate_event() has been added as the central
    point used by a ClutterBackend implementation to translate a
    native event into a ClutterEvent;

  - ClutterEventTranslator is a private interface that should be
    implemented by backend-specific objects, like stage
    implementations and ClutterDeviceManager sub-classes, and
    allows dealing with class-specific event translation;

  - ClutterStageX11 implements EventTranslator, and deals with the
    stage-relative X11 events coming from the X11 event source;

  - ClutterStageGLX overrides EventTranslator, in order to
    deal with the INTEL_GLX_swap_event extension, and it chains up
    to the X11 default implementation;

  - ClutterDeviceManagerX11 has been split into two separate classes,
    one that deals with core and (optionally) XI1 events, and the
    other that deals with XI2 events; the selection is done at run-time,
    since the core+XI1 and XI2 mechanisms are mutually exclusive.

All the other backends we officially support still use their own
custom event source and translation function, but the end goal is to
migrate them to the translate_event() virtual function, and have the
event source be a shared part of Clutter core.
2011-01-21 10:25:43 +00:00

1058 lines
33 KiB
C

/*
* Clutter.
*
* An OpenGL based 'interactive canvas' library.
*
* Copyright (C) 2009 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 "clutter-device-manager-xi2.h"
#include "clutter-backend-x11.h"
#include "clutter-input-device-xi2.h"
#include "clutter-stage-x11.h"
#include "clutter-backend.h"
#include "clutter-debug.h"
#include "clutter-device-manager-private.h"
#include "clutter-event-translator.h"
#include "clutter-stage-private.h"
#include "clutter-private.h"
#include <X11/extensions/XInput2.h>
enum
{
PROP_0,
PROP_OPCODE,
PROP_LAST
};
static GParamSpec *obj_props[PROP_LAST] = { NULL, };
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 */
};
#define N_AXIS_ATOMS G_N_ELEMENTS (clutter_input_axis_atom_names)
static Atom clutter_input_axis_atoms[N_AXIS_ATOMS] = { 0, };
static void clutter_event_translator_iface_init (ClutterEventTranslatorIface *iface);
#define clutter_device_manager_xi2_get_type _clutter_device_manager_xi2_get_type
G_DEFINE_TYPE_WITH_CODE (ClutterDeviceManagerXI2,
clutter_device_manager_xi2,
CLUTTER_TYPE_DEVICE_MANAGER,
G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_EVENT_TRANSLATOR,
clutter_event_translator_iface_init));
static void
translate_valuator_class (Display *xdisplay,
ClutterInputDevice *device,
XIValuatorClassInfo *class)
{
static gboolean atoms_initialized = FALSE;
ClutterInputAxis axis, i;
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 = CLUTTER_INPUT_AXIS_IGNORE;
i <= CLUTTER_INPUT_AXIS_WHEEL;
i += 1)
{
if (clutter_input_axis_atoms[i] == class->label)
{
axis = i;
break;
}
}
_clutter_input_device_add_axis (device, axis,
class->min,
class->max,
class->resolution);
}
static void
translate_device_classes (Display *xdisplay,
ClutterInputDevice *device,
XIAnyClassInfo **classes,
guint n_classes)
{
gint i;
for (i = 0; i < n_classes; i++)
{
XIAnyClassInfo *class_info = classes[i];
switch (class_info->type)
{
case XIKeyClass:
{
XIKeyClassInfo *key_info = (XIKeyClassInfo *) class_info;
gint j;
_clutter_input_device_set_keys (device,
key_info->num_keycodes,
0,
G_MAXUINT);
for (j = 0; j < key_info->num_keycodes; j++)
{
clutter_input_device_set_key (device, j,
key_info->keycodes[i],
0);
}
}
break;
case XIValuatorClass:
translate_valuator_class (xdisplay, device,
(XIValuatorClassInfo *) class_info);
break;
default:
break;
}
}
}
static ClutterInputDevice *
create_device (ClutterDeviceManagerXI2 *manager_xi2,
ClutterBackendX11 *backend_x11,
XIDeviceInfo *info)
{
ClutterInputDeviceType source;
ClutterInputDevice *retval;
ClutterInputMode mode;
if (info->use == XIMasterKeyboard || info->use == XISlaveKeyboard)
source = CLUTTER_KEYBOARD_DEVICE;
else
{
gchar *name;
name = g_ascii_strdown (info->name, -1);
if (strstr (name, "eraser") != NULL)
source = CLUTTER_ERASER_DEVICE;
else if (strstr (name, "cursor") != NULL)
source = CLUTTER_CURSOR_DEVICE;
else if (strstr (name, "wacom") != NULL || strstr (name, "pen") != NULL)
source = CLUTTER_PEN_DEVICE;
else
source = CLUTTER_POINTER_DEVICE;
g_free (name);
}
switch (info->use)
{
case XIMasterKeyboard:
case XIMasterPointer:
mode = CLUTTER_INPUT_MODE_MASTER;
break;
case XISlaveKeyboard:
case XISlavePointer:
mode = CLUTTER_INPUT_MODE_SLAVE;
break;
case XIFloatingSlave:
default:
mode = CLUTTER_INPUT_MODE_FLOATING;
break;
}
retval = g_object_new (CLUTTER_TYPE_INPUT_DEVICE_XI2,
"name", info->name,
"id", info->deviceid,
"has-cursor", (info->use == XIMasterPointer),
"device-manager", manager_xi2,
"device-type", source,
"device-mode", mode,
"backend", backend_x11,
NULL);
translate_device_classes (backend_x11->xdpy, retval,
info->classes,
info->num_classes);
CLUTTER_NOTE (BACKEND, "Created device '%s' (id: %d, has-cursor: %s)",
info->name,
info->deviceid,
info->use == XIMasterPointer ? "yes" : "no");
return retval;
}
static ClutterInputDevice *
add_device (ClutterDeviceManagerXI2 *manager_xi2,
ClutterBackendX11 *backend_x11,
XIDeviceInfo *info,
gboolean in_construction)
{
ClutterInputDevice *device;
device = create_device (manager_xi2, backend_x11, info);
/* we don't go through the DeviceManager::add_device() vfunc because
* that emits the signal, and we only do it conditionally
*
* FIXME: add a boolean "emit_signal" argument to the add_device()
* wrapper in ClutterDeviceManager, and emit the signal only if true
*/
g_hash_table_replace (manager_xi2->devices_by_id,
GINT_TO_POINTER (info->deviceid),
g_object_ref (device));
if (info->use == XIMasterPointer ||
info->use == XIMasterKeyboard)
{
manager_xi2->master_devices =
g_list_prepend (manager_xi2->master_devices, device);
}
else if (info->use == XISlavePointer ||
info->use == XISlaveKeyboard ||
info->use == XIFloatingSlave)
{
manager_xi2->slave_devices =
g_list_prepend (manager_xi2->slave_devices, device);
}
else
g_warning ("Unhandled device: %s",
clutter_input_device_get_device_name (device));
/* relationships between devices and signal emissions are not
* necessary while we're constructing the device manager instance
*/
if (!in_construction)
{
if (info->use == XISlavePointer || info->use == XISlaveKeyboard)
{
ClutterInputDevice *master;
master = g_hash_table_lookup (manager_xi2->devices_by_id,
GINT_TO_POINTER (info->attachment));
_clutter_input_device_set_associated_device (device, master);
_clutter_input_device_add_slave (master, device);
}
g_signal_emit_by_name (manager_xi2, "device-added", device);
}
return device;
}
static void
remove_device (ClutterDeviceManagerXI2 *manager_xi2,
gint device_id)
{
ClutterInputDevice *device;
device = g_hash_table_lookup (manager_xi2->devices_by_id,
GINT_TO_POINTER (device_id));
if (device != NULL)
{
manager_xi2->master_devices =
g_list_remove (manager_xi2->master_devices, device);
manager_xi2->slave_devices =
g_list_remove (manager_xi2->slave_devices, device);
g_signal_emit_by_name (manager_xi2, "device-removed", device);
g_object_run_dispose (G_OBJECT (device));
g_hash_table_remove (manager_xi2->devices_by_id,
GINT_TO_POINTER (device_id));
}
}
static void
translate_hierarchy_event (ClutterBackendX11 *backend_x11,
ClutterDeviceManagerXI2 *manager_xi2,
XIHierarchyEvent *ev)
{
ClutterInputDevice *device;
int i;
for (i = 0; i < ev->num_info; i++)
{
if (ev->info[i].flags & XIDeviceEnabled)
{
XIDeviceInfo *info;
int n_devices;
info = XIQueryDevice (backend_x11->xdpy,
ev->info[i].deviceid,
&n_devices);
device = add_device (manager_xi2, backend_x11, &info[0], FALSE);
}
else if (ev->info[i].flags & XIDeviceDisabled)
{
remove_device (manager_xi2, ev->info[i].deviceid);
}
else if ((ev->info[i].flags & XISlaveAttached) ||
(ev->info[i].flags & XISlaveDetached))
{
ClutterInputDevice *master, *slave;
XIDeviceInfo *info;
int n_devices;
slave = g_hash_table_lookup (manager_xi2->devices_by_id,
GINT_TO_POINTER (ev->info[i].deviceid));
master = clutter_input_device_get_associated_device (slave);
/* detach the slave in both cases */
if (master != NULL)
{
_clutter_input_device_remove_slave (master, slave);
_clutter_input_device_set_associated_device (slave, NULL);
}
/* and attach the slave to the new master if needed */
if (ev->info[i].flags & XISlaveAttached)
{
info = XIQueryDevice (backend_x11->xdpy,
ev->info[i].deviceid,
&n_devices);
master = g_hash_table_lookup (manager_xi2->devices_by_id,
GINT_TO_POINTER (info->attachment));
_clutter_input_device_set_associated_device (slave, master);
_clutter_input_device_add_slave (master, slave);
XIFreeDeviceInfo (info);
}
}
}
}
static void
clutter_device_manager_xi2_select_events (ClutterDeviceManager *manager,
Window xwindow,
XIEventMask *event_mask)
{
Display *xdisplay;
xdisplay = clutter_x11_get_default_display ();
XISelectEvents (xdisplay, xwindow, event_mask, 1);
}
static ClutterStage *
get_event_stage (ClutterEventTranslator *translator,
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:
{
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;
default:
break;
}
if (xwindow == None)
return NULL;
return clutter_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 gdouble *
translate_axes (ClutterInputDevice *device,
gdouble x,
gdouble y,
ClutterStageX11 *stage_x11,
XIValuatorState *valuators)
{
guint n_axes = clutter_input_device_get_n_axes (device);
guint i;
gdouble *retval;
double *values;
retval = g_new0 (gdouble, n_axes);
values = valuators->values;
for (i = 0; i < valuators->mask_len * 8; i++)
{
ClutterInputAxis axis;
gdouble val;
if (!XIMaskIsSet (valuators->mask, i))
continue;
axis = _clutter_input_device_get_axis (device, i);
val = *values++;
switch (axis)
{
case CLUTTER_INPUT_AXIS_X:
retval[i] = x;
break;
case CLUTTER_INPUT_AXIS_Y:
retval[i] = y;
break;
default:
_clutter_input_device_translate_axis (device, i, val, &retval[i]);
break;
}
}
return retval;
}
static ClutterTranslateReturn
clutter_device_manager_xi2_translate_event (ClutterEventTranslator *translator,
gpointer native,
ClutterEvent *event)
{
ClutterDeviceManagerXI2 *manager_xi2 = CLUTTER_DEVICE_MANAGER_XI2 (translator);
ClutterTranslateReturn retval = CLUTTER_TRANSLATE_CONTINUE;
ClutterBackendX11 *backend_x11;
ClutterStageX11 *stage_x11;
ClutterStage *stage;
ClutterInputDevice *device;
XGenericEventCookie *cookie;
XIEvent *xi_event;
XEvent *xevent;
backend_x11 = CLUTTER_BACKEND_X11 (clutter_get_default_backend ());
xevent = native;
cookie = &xevent->xcookie;
if (!XGetEventData (backend_x11->xdpy, cookie))
return CLUTTER_TRANSLATE_CONTINUE;
if (cookie->type != GenericEvent ||
cookie->extension != manager_xi2->opcode)
{
XFreeEventData (backend_x11->xdpy, cookie);
return CLUTTER_TRANSLATE_CONTINUE;
}
xi_event = (XIEvent *) cookie->data;
stage = get_event_stage (translator, xi_event);
if (stage == NULL || CLUTTER_ACTOR_IN_DESTRUCTION (stage))
{
XFreeEventData (backend_x11->xdpy, cookie);
return CLUTTER_TRANSLATE_CONTINUE;
}
stage_x11 = CLUTTER_STAGE_X11 (_clutter_stage_get_window (stage));
event->any.stage = stage;
switch (xi_event->evtype)
{
case XI_HierarchyChanged:
{
XIHierarchyEvent *xev = (XIHierarchyEvent *) xi_event;
translate_hierarchy_event (backend_x11, manager_xi2, xev);
}
retval = CLUTTER_TRANSLATE_REMOVE;
break;
case XI_DeviceChanged:
{
XIDeviceChangedEvent *xev = (XIDeviceChangedEvent *) xi_event;
device = g_hash_table_lookup (manager_xi2->devices_by_id,
GINT_TO_POINTER (xev->deviceid));
_clutter_input_device_reset_axes (device);
translate_device_classes (backend_x11->xdpy,
device,
xev->classes,
xev->num_classes);
}
retval = CLUTTER_TRANSLATE_REMOVE;
break;
case XI_KeyPress:
case XI_KeyRelease:
{
XIDeviceEvent *xev = (XIDeviceEvent *) xi_event;
ClutterEventX11 *event_x11;
char buffer[7] = { 0, };
gunichar n;
event->key.type = event->type = (xev->evtype == XI_KeyPress)
? CLUTTER_KEY_PRESS
: CLUTTER_KEY_RELEASE;
event->key.time = xev->time;
event->key.stage = stage;
event->key.modifier_state =
_clutter_input_device_xi2_translate_state (&xev->mods, &xev->buttons);
event->key.hardware_keycode = xev->detail;
/* keyval is the key ignoring all modifiers ('1' vs. '!') */
event->key.keyval =
_clutter_keymap_x11_translate_key_state (backend_x11->keymap,
event->key.hardware_keycode,
event->key.modifier_state,
NULL);
/* KeyEvents have platform specific data associated to them */
event_x11 = _clutter_event_x11_new ();
_clutter_event_set_platform_data (event, event_x11);
event_x11->key_group =
_clutter_keymap_x11_get_key_group (backend_x11->keymap,
event->key.modifier_state);
event_x11->key_is_modifier =
_clutter_keymap_x11_get_is_modifier (backend_x11->keymap,
event->key.hardware_keycode);
event_x11->num_lock_set =
_clutter_keymap_x11_get_num_lock_state (backend_x11->keymap);
event_x11->caps_lock_set =
_clutter_keymap_x11_get_caps_lock_state (backend_x11->keymap);
device = g_hash_table_lookup (manager_xi2->devices_by_id,
GINT_TO_POINTER (xev->deviceid));
event->key.device = device;
/* XXX keep this in sync with the evdev device manager */
n = print_keysym (event->key.keyval, buffer, sizeof (buffer));
if (n == 0)
{
/* not printable */
event->key.unicode_value = (gunichar) '\0';
}
else
{
event->key.unicode_value = g_utf8_get_char_validated (buffer, n);
if (event->key.unicode_value == -1 ||
event->key.unicode_value == -2)
event->key.unicode_value = (gunichar) '\0';
}
CLUTTER_NOTE (EVENT, "%s: win:0x%x, key: %12s (%d)",
event->any.type == CLUTTER_KEY_PRESS
? "key press "
: "key release",
(unsigned int) stage_x11->xwin,
event->key.keyval ? buffer : "(none)",
event->key.keyval);
if (xi_event->evtype == XI_KeyPress)
_clutter_stage_x11_set_user_time (stage_x11, event->key.time);
retval = CLUTTER_TRANSLATE_QUEUE;
}
break;
case XI_ButtonPress:
case XI_ButtonRelease:
{
XIDeviceEvent *xev = (XIDeviceEvent *) xi_event;
switch (xev->detail)
{
case 4:
case 5:
case 6:
case 7:
event->scroll.type = event->type = CLUTTER_SCROLL;
if (xev->detail == 4)
event->scroll.direction = CLUTTER_SCROLL_UP;
else if (xev->detail == 5)
event->scroll.direction = CLUTTER_SCROLL_DOWN;
else if (xev->detail == 6)
event->scroll.direction = CLUTTER_SCROLL_LEFT;
else
event->scroll.direction = CLUTTER_SCROLL_RIGHT;
event->scroll.stage = stage;
event->scroll.time = xev->time;
event->scroll.x = xev->event_x;
event->scroll.y = xev->event_y;
event->scroll.device =
g_hash_table_lookup (manager_xi2->devices_by_id,
GINT_TO_POINTER (xev->deviceid));
event->scroll.modifier_state =
_clutter_input_device_xi2_translate_state (&xev->mods,
&xev->buttons);
event->scroll.axes = translate_axes (event->scroll.device,
event->scroll.x,
event->scroll.y,
stage_x11,
&xev->valuators);
break;
default:
event->button.type = event->type =
(xi_event->evtype == XI_ButtonPress) ? CLUTTER_BUTTON_PRESS
: CLUTTER_BUTTON_RELEASE;
event->button.stage = stage;
event->button.time = xev->time;
event->button.x = xev->event_x;
event->button.y = xev->event_y;
event->button.button = xev->detail;
event->button.device =
g_hash_table_lookup (manager_xi2->devices_by_id,
GINT_TO_POINTER (xev->deviceid));
event->button.modifier_state =
_clutter_input_device_xi2_translate_state (&xev->mods,
&xev->buttons);
event->button.axes = translate_axes (event->button.device,
event->button.x,
event->button.y,
stage_x11,
&xev->valuators);
break;
}
CLUTTER_NOTE (EVENT,
"%s: win:0x%x, device:%s (button:%d, x:%.2f, y:%.2f)",
event->any.type == CLUTTER_BUTTON_PRESS
? "button press "
: "button release",
(unsigned int) stage_x11->xwin,
event->button.device->device_name,
event->button.button,
event->button.x,
event->button.y);
if (xi_event->evtype == XI_ButtonPress)
_clutter_stage_x11_set_user_time (stage_x11, event->button.time);
retval = CLUTTER_TRANSLATE_QUEUE;
}
break;
case XI_Motion:
{
XIDeviceEvent *xev = (XIDeviceEvent *) xi_event;
event->motion.type = event->type = CLUTTER_MOTION;
event->motion.stage = stage;
event->motion.time = xev->time;
event->motion.x = xev->event_x;
event->motion.y = xev->event_y;
event->motion.device =
g_hash_table_lookup (manager_xi2->devices_by_id,
GINT_TO_POINTER (xev->deviceid));
event->motion.modifier_state =
_clutter_input_device_xi2_translate_state (&xev->mods,
&xev->buttons);
CLUTTER_NOTE (EVENT, "motion: win:0x%x device:%s (x:%.2f, y:%.2f)",
(unsigned int) stage_x11->xwin,
event->motion.device->device_name,
event->motion.x,
event->motion.y);
retval = CLUTTER_TRANSLATE_QUEUE;
}
break;
case XI_Enter:
case XI_Leave:
{
XIEnterEvent *xev = (XIEnterEvent *) xi_event;
device = g_hash_table_lookup (manager_xi2->devices_by_id,
GINT_TO_POINTER (xev->deviceid));
if (xi_event->evtype == XI_Enter)
{
_clutter_input_device_set_stage (device, stage);
event->motion.type = event->type = CLUTTER_MOTION;
event->motion.stage = stage;
event->motion.source = CLUTTER_ACTOR (stage);
event->motion.time = xev->time;
event->motion.x = xev->event_x;
event->motion.y = xev->event_y;
event->motion.device = device;
event->motion.modifier_state =
_clutter_input_device_xi2_translate_state (&xev->mods,
&xev->buttons);
}
else
{
if (device->stage == NULL)
{
CLUTTER_NOTE (EVENT,
"Discarding Leave for ButtonRelease "
"event off-stage");
retval = CLUTTER_TRANSLATE_REMOVE;
break;
}
_clutter_input_device_set_stage (device, NULL);
event->crossing.type = event->type = CLUTTER_LEAVE;
event->crossing.source = CLUTTER_ACTOR (stage);
event->crossing.time = xev->time;
event->crossing.x = xev->event_x;
event->crossing.y = xev->event_y;
event->crossing.device = device;
}
retval = CLUTTER_TRANSLATE_QUEUE;
}
break;
case XI_FocusIn:
case XI_FocusOut:
retval = CLUTTER_TRANSLATE_CONTINUE;
break;
}
XFreeEventData (backend_x11->xdpy, cookie);
return retval;
}
static void
clutter_event_translator_iface_init (ClutterEventTranslatorIface *iface)
{
iface->translate_event = clutter_device_manager_xi2_translate_event;
}
static void
clutter_device_manager_xi2_add_device (ClutterDeviceManager *manager,
ClutterInputDevice *device)
{
}
static void
clutter_device_manager_xi2_remove_device (ClutterDeviceManager *manager,
ClutterInputDevice *device)
{
ClutterDeviceManagerXI2 *manager_xi2 = CLUTTER_DEVICE_MANAGER_XI2 (manager);
gint device_id;
device_id = clutter_input_device_get_device_id (device);
manager_xi2->master_devices =
g_list_remove (manager_xi2->master_devices, device);
manager_xi2->slave_devices =
g_list_remove (manager_xi2->slave_devices, device);
g_signal_emit_by_name (manager_xi2, "device-removed", device);
g_object_run_dispose (G_OBJECT (device));
g_hash_table_remove (manager_xi2->devices_by_id,
GINT_TO_POINTER (device_id));
}
static const GSList *
clutter_device_manager_xi2_get_devices (ClutterDeviceManager *manager)
{
ClutterDeviceManagerXI2 *manager_xi2 = CLUTTER_DEVICE_MANAGER_XI2 (manager);
GSList *all_devices = NULL;
GList *l;
if (manager_xi2->all_devices != NULL)
return manager_xi2->all_devices;
for (l = manager_xi2->master_devices; l != NULL; l = l->next)
all_devices = g_slist_prepend (all_devices, l->data);
for (l = manager_xi2->slave_devices; l != NULL; l = l->next)
all_devices = g_slist_prepend (all_devices, l->data);
manager_xi2->all_devices = g_slist_reverse (all_devices);
return manager_xi2->all_devices;
}
static ClutterInputDevice *
clutter_device_manager_xi2_get_device (ClutterDeviceManager *manager,
gint id)
{
ClutterDeviceManagerXI2 *manager_xi2 = CLUTTER_DEVICE_MANAGER_XI2 (manager);
return g_hash_table_lookup (manager_xi2->devices_by_id,
GINT_TO_POINTER (id));
}
static ClutterInputDevice *
clutter_device_manager_xi2_get_core_device (ClutterDeviceManager *manager,
ClutterInputDeviceType device_type)
{
return NULL;
}
static void
relate_masters (gpointer key,
gpointer value,
gpointer data)
{
ClutterDeviceManagerXI2 *manager_xi2 = data;
ClutterInputDevice *device, *relative;
device = g_hash_table_lookup (manager_xi2->devices_by_id, key);
relative = g_hash_table_lookup (manager_xi2->devices_by_id, value);
_clutter_input_device_set_associated_device (device, relative);
_clutter_input_device_set_associated_device (relative, device);
}
static void
relate_slaves (gpointer key,
gpointer value,
gpointer data)
{
ClutterDeviceManagerXI2 *manager_xi2 = data;
ClutterInputDevice *master, *slave;
master = g_hash_table_lookup (manager_xi2->devices_by_id, key);
slave = g_hash_table_lookup (manager_xi2->devices_by_id, value);
_clutter_input_device_set_associated_device (slave, master);
_clutter_input_device_add_slave (master, slave);
}
static void
clutter_device_manager_xi2_constructed (GObject *gobject)
{
ClutterDeviceManagerXI2 *manager_xi2 = CLUTTER_DEVICE_MANAGER_XI2 (gobject);
ClutterDeviceManager *manager = CLUTTER_DEVICE_MANAGER (gobject);
ClutterBackendX11 *backend_x11;
GHashTable *masters, *slaves;
XIDeviceInfo *info;
XIEventMask event_mask;
unsigned char mask[2] = { 0, };
int n_devices, i;
backend_x11 =
CLUTTER_BACKEND_X11 (_clutter_device_manager_get_backend (manager));
masters = g_hash_table_new (NULL, NULL);
slaves = g_hash_table_new (NULL, NULL);
info = XIQueryDevice (backend_x11->xdpy, XIAllDevices, &n_devices);
for (i = 0; i < n_devices; i++)
{
XIDeviceInfo *xi_device = &info[i];
ClutterInputDevice *device;
device = add_device (manager_xi2, backend_x11, xi_device, TRUE);
if (xi_device->use == XIMasterPointer ||
xi_device->use == XIMasterKeyboard)
{
g_hash_table_insert (masters,
GINT_TO_POINTER (xi_device->deviceid),
GINT_TO_POINTER (xi_device->attachment));
}
else if (xi_device->use == XISlavePointer ||
xi_device->use == XISlaveKeyboard)
{
g_hash_table_insert (slaves,
GINT_TO_POINTER (xi_device->deviceid),
GINT_TO_POINTER (xi_device->attachment));
}
}
XIFreeDeviceInfo (info);
g_hash_table_foreach (masters, relate_masters, manager_xi2);
g_hash_table_destroy (masters);
g_hash_table_foreach (slaves, relate_slaves, manager_xi2);
g_hash_table_destroy (slaves);
XISetMask (mask, XI_HierarchyChanged);
XISetMask (mask, XI_DeviceChanged);
event_mask.deviceid = XIAllDevices;
event_mask.mask_len = sizeof (mask);
event_mask.mask = mask;
clutter_device_manager_xi2_select_events (manager,
clutter_x11_get_root_window (),
&event_mask);
if (G_OBJECT_CLASS (clutter_device_manager_xi2_parent_class)->constructed)
G_OBJECT_CLASS (clutter_device_manager_xi2_parent_class)->constructed (gobject);
}
static void
clutter_device_manager_xi2_set_property (GObject *gobject,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
ClutterDeviceManagerXI2 *manager_xi2 = CLUTTER_DEVICE_MANAGER_XI2 (gobject);
switch (prop_id)
{
case PROP_OPCODE:
manager_xi2->opcode = g_value_get_int (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
break;
}
}
static void
clutter_device_manager_xi2_class_init (ClutterDeviceManagerXI2Class *klass)
{
ClutterDeviceManagerClass *manager_class;
GObjectClass *gobject_class;
obj_props[PROP_OPCODE] =
g_param_spec_int ("opcode",
"Opcode",
"The XI2 opcode",
-1, G_MAXINT,
-1,
CLUTTER_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY);
gobject_class = G_OBJECT_CLASS (klass);
gobject_class->constructed = clutter_device_manager_xi2_constructed;
gobject_class->set_property = clutter_device_manager_xi2_set_property;
g_object_class_install_properties (gobject_class, PROP_LAST, obj_props);
manager_class = CLUTTER_DEVICE_MANAGER_CLASS (klass);
manager_class->add_device = clutter_device_manager_xi2_add_device;
manager_class->remove_device = clutter_device_manager_xi2_remove_device;
manager_class->get_devices = clutter_device_manager_xi2_get_devices;
manager_class->get_core_device = clutter_device_manager_xi2_get_core_device;
manager_class->get_device = clutter_device_manager_xi2_get_device;
}
static void
clutter_device_manager_xi2_init (ClutterDeviceManagerXI2 *self)
{
self->devices_by_id = g_hash_table_new_full (NULL, NULL,
NULL,
(GDestroyNotify) g_object_unref);
}