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-04 07:32:04 -05:00
|
|
|
/*
|
|
|
|
* Clutter.
|
|
|
|
*
|
|
|
|
* An OpenGL based 'interactive canvas' library.
|
|
|
|
*
|
2011-01-18 07:59:49 -05:00
|
|
|
* Copyright © 2009, 2010, 2011 Intel Corp.
|
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-04 07:32:04 -05:00
|
|
|
*
|
|
|
|
* 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-core-x11.h"
|
|
|
|
|
|
|
|
#include "clutter-backend-x11.h"
|
|
|
|
#include "clutter-input-device-core-x11.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"
|
|
|
|
|
|
|
|
#ifdef HAVE_XINPUT
|
|
|
|
#include <X11/extensions/XInput.h>
|
|
|
|
|
|
|
|
/* old versions of XI.h don't define these */
|
|
|
|
#ifndef IsXExtensionKeyboard
|
|
|
|
#define IsXExtensionKeyboard 3
|
|
|
|
#define IsXExtensionPointer 4
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#endif /* HAVE_XINPUT */
|
|
|
|
|
|
|
|
enum
|
|
|
|
{
|
|
|
|
PROP_0,
|
|
|
|
|
|
|
|
PROP_EVENT_BASE,
|
|
|
|
|
|
|
|
PROP_LAST
|
|
|
|
};
|
|
|
|
|
|
|
|
static GParamSpec *obj_props[PROP_LAST] = { NULL, };
|
|
|
|
|
|
|
|
static void clutter_event_translator_iface_init (ClutterEventTranslatorIface *iface);
|
|
|
|
|
|
|
|
G_DEFINE_TYPE_WITH_CODE (ClutterDeviceManagerX11,
|
|
|
|
clutter_device_manager_x11,
|
|
|
|
CLUTTER_TYPE_DEVICE_MANAGER,
|
|
|
|
G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_EVENT_TRANSLATOR,
|
|
|
|
clutter_event_translator_iface_init));
|
|
|
|
|
|
|
|
#ifdef HAVE_XINPUT
|
|
|
|
static void
|
|
|
|
translate_class_info (ClutterInputDevice *device,
|
|
|
|
XDeviceInfo *info)
|
|
|
|
{
|
|
|
|
XAnyClassPtr any_class;
|
|
|
|
gint i;
|
|
|
|
|
|
|
|
any_class = info->inputclassinfo;
|
|
|
|
|
|
|
|
for (i = 0; i < info->num_classes; i++)
|
|
|
|
{
|
|
|
|
switch (any_class->class)
|
|
|
|
{
|
|
|
|
case ButtonClass:
|
|
|
|
break;
|
|
|
|
|
|
|
|
case KeyClass:
|
|
|
|
{
|
|
|
|
XKeyInfo *xk_info = (XKeyInfo *) any_class;
|
|
|
|
guint n_keys;
|
|
|
|
|
|
|
|
n_keys = xk_info->max_keycode - xk_info->min_keycode + 1;
|
|
|
|
_clutter_input_device_set_keys (device, n_keys,
|
|
|
|
xk_info->min_keycode,
|
|
|
|
xk_info->max_keycode);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ValuatorClass:
|
|
|
|
{
|
|
|
|
XValuatorInfo *xv_info = (XValuatorInfo *) any_class;
|
|
|
|
gint j;
|
|
|
|
|
|
|
|
for (j = 0; j < xv_info->num_axes; j++)
|
|
|
|
{
|
|
|
|
ClutterInputAxis axis;
|
|
|
|
|
|
|
|
switch (j)
|
|
|
|
{
|
|
|
|
case 0:
|
|
|
|
axis = CLUTTER_INPUT_AXIS_X;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 1:
|
|
|
|
axis = CLUTTER_INPUT_AXIS_Y;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 2:
|
|
|
|
axis = CLUTTER_INPUT_AXIS_PRESSURE;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 3:
|
|
|
|
axis = CLUTTER_INPUT_AXIS_XTILT;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 4:
|
|
|
|
axis = CLUTTER_INPUT_AXIS_YTILT;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 5:
|
|
|
|
axis = CLUTTER_INPUT_AXIS_WHEEL;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
axis = CLUTTER_INPUT_AXIS_IGNORE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
_clutter_input_device_add_axis (device, axis,
|
|
|
|
xv_info->axes[j].min_value,
|
|
|
|
xv_info->axes[j].max_value,
|
|
|
|
xv_info->axes[j].resolution);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
any_class = (XAnyClassPtr) (((char *) any_class) + any_class->length);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static ClutterInputDevice *
|
|
|
|
create_device (ClutterDeviceManagerX11 *manager_x11,
|
|
|
|
ClutterBackendX11 *backend_x11,
|
|
|
|
XDeviceInfo *info)
|
|
|
|
{
|
|
|
|
ClutterInputDeviceType source;
|
|
|
|
ClutterInputDevice *retval;
|
|
|
|
|
|
|
|
if (info->use != IsXExtensionPointer &&
|
|
|
|
info->use != IsXExtensionKeyboard)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if (info->use == IsXExtensionKeyboard)
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
retval = g_object_new (CLUTTER_TYPE_INPUT_DEVICE_X11,
|
|
|
|
"name", info->name,
|
|
|
|
"id", info->id,
|
|
|
|
"has-cursor", FALSE,
|
|
|
|
"device-manager", manager_x11,
|
|
|
|
"device-type", source,
|
|
|
|
"device-mode", CLUTTER_INPUT_MODE_FLOATING,
|
|
|
|
"backend", backend_x11,
|
|
|
|
NULL);
|
|
|
|
translate_class_info (retval, info);
|
|
|
|
|
|
|
|
CLUTTER_NOTE (BACKEND,
|
|
|
|
"XI Device '%s' (id: %d) created",
|
|
|
|
info->name,
|
|
|
|
(int) info->id);
|
|
|
|
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
#endif /* HAVE_XINPUT */
|
|
|
|
|
|
|
|
static inline void
|
|
|
|
translate_key_event (ClutterBackendX11 *backend_x11,
|
|
|
|
ClutterDeviceManagerX11 *manager_x11,
|
|
|
|
ClutterEvent *event,
|
|
|
|
XEvent *xevent)
|
|
|
|
{
|
|
|
|
ClutterEventX11 *event_x11;
|
|
|
|
char buffer[256 + 1];
|
|
|
|
int n;
|
|
|
|
|
|
|
|
event->key.type = xevent->xany.type == KeyPress ? CLUTTER_KEY_PRESS
|
|
|
|
: CLUTTER_KEY_RELEASE;
|
|
|
|
event->key.time = xevent->xkey.time;
|
|
|
|
event->key.device = manager_x11->core_keyboard;
|
|
|
|
|
|
|
|
/* KeyEvents have platform specific data associated to them */
|
|
|
|
event_x11 = _clutter_event_x11_new ();
|
|
|
|
_clutter_event_set_platform_data (event, event_x11);
|
|
|
|
|
|
|
|
event->key.modifier_state = (ClutterModifierType) xevent->xkey.state;
|
|
|
|
event->key.hardware_keycode = xevent->xkey.keycode;
|
|
|
|
|
|
|
|
/* 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);
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
/* unicode_value is the printable representation */
|
|
|
|
n = XLookupString (&xevent->xkey, buffer, sizeof (buffer) - 1, NULL, NULL);
|
|
|
|
|
|
|
|
if (n != NoSymbol)
|
|
|
|
{
|
|
|
|
event->key.unicode_value = g_utf8_get_char_validated (buffer, n);
|
|
|
|
if ((event->key.unicode_value != -1) &&
|
|
|
|
(event->key.unicode_value != -2))
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
event->key.unicode_value = (gunichar)'\0';
|
|
|
|
|
|
|
|
out:
|
|
|
|
CLUTTER_NOTE (EVENT,
|
|
|
|
"%s: win:0x%x, key: %12s (%d)",
|
|
|
|
event->any.type == CLUTTER_KEY_PRESS
|
|
|
|
? "key press "
|
|
|
|
: "key release",
|
|
|
|
(unsigned int) xevent->xkey.window,
|
|
|
|
event->key.keyval ? buffer : "(none)",
|
|
|
|
event->key.keyval);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef HAVE_XINPUT
|
|
|
|
static ClutterInputDevice *
|
|
|
|
get_device_from_event (ClutterDeviceManagerX11 *manager_x11,
|
|
|
|
XEvent *xevent)
|
|
|
|
{
|
|
|
|
guint32 device_id;
|
|
|
|
|
|
|
|
device_id = ((XDeviceButtonEvent *) xevent)->deviceid;
|
|
|
|
|
|
|
|
return g_hash_table_lookup (manager_x11->devices_by_id,
|
|
|
|
GINT_TO_POINTER (device_id));
|
|
|
|
}
|
|
|
|
#endif /* HAVE_XINPUT */
|
|
|
|
|
|
|
|
static ClutterTranslateReturn
|
|
|
|
clutter_device_manager_x11_translate_event (ClutterEventTranslator *translator,
|
|
|
|
gpointer native,
|
|
|
|
ClutterEvent *event)
|
|
|
|
{
|
|
|
|
ClutterDeviceManagerX11 *manager_x11;
|
|
|
|
ClutterBackendX11 *backend_x11;
|
|
|
|
ClutterInputDevice *device;
|
|
|
|
ClutterStageX11 *stage_x11;
|
|
|
|
ClutterTranslateReturn res;
|
|
|
|
ClutterStage *stage;
|
|
|
|
XEvent *xevent;
|
|
|
|
|
|
|
|
manager_x11 = CLUTTER_DEVICE_MANAGER_X11 (translator);
|
|
|
|
backend_x11 = CLUTTER_BACKEND_X11 (clutter_get_default_backend ());
|
|
|
|
device = NULL;
|
|
|
|
|
|
|
|
xevent = native;
|
|
|
|
|
|
|
|
stage = clutter_x11_get_stage_from_window (xevent->xany.window);
|
|
|
|
if (stage == NULL)
|
|
|
|
return CLUTTER_TRANSLATE_CONTINUE;
|
|
|
|
|
|
|
|
if (CLUTTER_ACTOR_IN_DESTRUCTION (stage))
|
|
|
|
return CLUTTER_TRANSLATE_CONTINUE;
|
|
|
|
|
|
|
|
stage_x11 = CLUTTER_STAGE_X11 (_clutter_stage_get_window (stage));
|
|
|
|
|
|
|
|
event->any.stage = stage;
|
|
|
|
|
|
|
|
res = CLUTTER_TRANSLATE_CONTINUE;
|
|
|
|
|
|
|
|
#ifdef HAVE_XINPUT
|
|
|
|
device = get_device_from_event (manager_x11, xevent);
|
|
|
|
if (device != NULL)
|
|
|
|
{
|
|
|
|
ClutterInputDeviceX11 *device_x11;
|
|
|
|
gboolean retval;
|
|
|
|
|
|
|
|
device_x11 = CLUTTER_INPUT_DEVICE_X11 (device);
|
|
|
|
retval = _clutter_input_device_x11_translate_xi_event (device_x11,
|
|
|
|
stage_x11,
|
|
|
|
xevent,
|
|
|
|
event);
|
|
|
|
if (retval)
|
|
|
|
return CLUTTER_TRANSLATE_QUEUE;
|
|
|
|
}
|
|
|
|
#endif /* HAVE_XINPUT */
|
|
|
|
|
|
|
|
switch (xevent->type)
|
|
|
|
{
|
|
|
|
case KeyPress:
|
|
|
|
translate_key_event (backend_x11, manager_x11, event, xevent);
|
|
|
|
_clutter_stage_x11_set_user_time (stage_x11, xevent->xkey.time);
|
|
|
|
res = CLUTTER_TRANSLATE_QUEUE;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case KeyRelease:
|
|
|
|
/* old-style X11 terminals require that even modern X11 send
|
|
|
|
* KeyPress/KeyRelease pairs when auto-repeating. for this
|
|
|
|
* reason modern(-ish) API like XKB has a way to detect
|
|
|
|
* auto-repeat and do a single KeyRelease at the end of a
|
|
|
|
* KeyPress sequence.
|
|
|
|
*
|
|
|
|
* this check emulates XKB's detectable auto-repeat; we peek
|
|
|
|
* the next event and check if it's a KeyPress for the same key
|
|
|
|
* and timestamp - and then ignore it if it matches the
|
|
|
|
* KeyRelease
|
|
|
|
*
|
|
|
|
* if we have XKB, and autorepeat is enabled, then this becomes
|
|
|
|
* a no-op
|
|
|
|
*/
|
|
|
|
if (!backend_x11->have_xkb_autorepeat && XPending (xevent->xkey.display))
|
|
|
|
{
|
|
|
|
XEvent next_event;
|
|
|
|
|
|
|
|
XPeekEvent (xevent->xkey.display, &next_event);
|
|
|
|
|
|
|
|
if (next_event.type == KeyPress &&
|
|
|
|
next_event.xkey.keycode == xevent->xkey.keycode &&
|
|
|
|
next_event.xkey.time == xevent->xkey.time)
|
|
|
|
{
|
|
|
|
res = CLUTTER_TRANSLATE_REMOVE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
translate_key_event (backend_x11, manager_x11, event, xevent);
|
|
|
|
res = CLUTTER_TRANSLATE_QUEUE;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ButtonPress:
|
|
|
|
CLUTTER_NOTE (EVENT,
|
|
|
|
"button press: win: 0x%x, coords: %d, %d, button: %d",
|
|
|
|
(unsigned int) stage_x11->xwin,
|
|
|
|
xevent->xbutton.x,
|
|
|
|
xevent->xbutton.y,
|
|
|
|
xevent->xbutton.button);
|
|
|
|
|
|
|
|
switch (xevent->xbutton.button)
|
|
|
|
{
|
|
|
|
case 4: /* up */
|
|
|
|
case 5: /* down */
|
|
|
|
case 6: /* left */
|
|
|
|
case 7: /* right */
|
|
|
|
event->scroll.type = CLUTTER_SCROLL;
|
|
|
|
|
|
|
|
if (xevent->xbutton.button == 4)
|
|
|
|
event->scroll.direction = CLUTTER_SCROLL_UP;
|
|
|
|
else if (xevent->xbutton.button == 5)
|
|
|
|
event->scroll.direction = CLUTTER_SCROLL_DOWN;
|
|
|
|
else if (xevent->xbutton.button == 6)
|
|
|
|
event->scroll.direction = CLUTTER_SCROLL_LEFT;
|
|
|
|
else
|
|
|
|
event->scroll.direction = CLUTTER_SCROLL_RIGHT;
|
|
|
|
|
|
|
|
event->scroll.time = xevent->xbutton.time;
|
|
|
|
event->scroll.x = xevent->xbutton.x;
|
|
|
|
event->scroll.y = xevent->xbutton.y;
|
|
|
|
event->scroll.modifier_state = xevent->xbutton.state;
|
|
|
|
event->scroll.device = manager_x11->core_pointer;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
event->button.type = event->type = CLUTTER_BUTTON_PRESS;
|
|
|
|
event->button.time = xevent->xbutton.time;
|
|
|
|
event->button.x = xevent->xbutton.x;
|
|
|
|
event->button.y = xevent->xbutton.y;
|
|
|
|
event->button.modifier_state = xevent->xbutton.state;
|
|
|
|
event->button.button = xevent->xbutton.button;
|
|
|
|
event->button.device = manager_x11->core_pointer;
|
|
|
|
event->button.axes = NULL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
_clutter_stage_x11_set_user_time (stage_x11, xevent->xbutton.time);
|
|
|
|
res = CLUTTER_TRANSLATE_QUEUE;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ButtonRelease:
|
|
|
|
CLUTTER_NOTE (EVENT,
|
|
|
|
"button press: win: 0x%x, coords: %d, %d, button: %d",
|
|
|
|
(unsigned int) stage_x11->xwin,
|
|
|
|
xevent->xbutton.x,
|
|
|
|
xevent->xbutton.y,
|
|
|
|
xevent->xbutton.button);
|
|
|
|
|
|
|
|
/* scroll events don't have a corresponding release */
|
|
|
|
if (xevent->xbutton.button == 4 ||
|
|
|
|
xevent->xbutton.button == 5 ||
|
|
|
|
xevent->xbutton.button == 6 ||
|
|
|
|
xevent->xbutton.button == 7)
|
|
|
|
{
|
|
|
|
res = CLUTTER_TRANSLATE_REMOVE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
event->button.type = event->type = CLUTTER_BUTTON_RELEASE;
|
|
|
|
event->button.time = xevent->xbutton.time;
|
|
|
|
event->button.x = xevent->xbutton.x;
|
|
|
|
event->button.y = xevent->xbutton.y;
|
|
|
|
event->button.modifier_state = xevent->xbutton.state;
|
|
|
|
event->button.button = xevent->xbutton.button;
|
|
|
|
event->button.axes = NULL;
|
|
|
|
event->button.device = manager_x11->core_pointer;
|
|
|
|
res = CLUTTER_TRANSLATE_QUEUE;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case MotionNotify:
|
|
|
|
CLUTTER_NOTE (EVENT,
|
|
|
|
"motion: win: 0x%x, coords: %d, %d",
|
|
|
|
(unsigned int) stage_x11->xwin,
|
|
|
|
xevent->xmotion.x,
|
|
|
|
xevent->xmotion.y);
|
|
|
|
|
|
|
|
event->motion.type = event->type = CLUTTER_MOTION;
|
|
|
|
event->motion.time = xevent->xmotion.time;
|
|
|
|
event->motion.x = xevent->xmotion.x;
|
|
|
|
event->motion.y = xevent->xmotion.y;
|
|
|
|
event->motion.modifier_state = xevent->xmotion.state;
|
|
|
|
event->motion.device = manager_x11->core_pointer;
|
|
|
|
event->motion.axes = NULL;
|
|
|
|
res = CLUTTER_TRANSLATE_QUEUE;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case EnterNotify:
|
|
|
|
/* we know that we are entering the stage here */
|
|
|
|
_clutter_input_device_set_stage (manager_x11->core_pointer, stage);
|
|
|
|
CLUTTER_NOTE (EVENT, "Entering the stage");
|
|
|
|
|
|
|
|
/* Convert enter notifies to motion events because X
|
|
|
|
doesn't emit the corresponding motion notify */
|
|
|
|
event->motion.type = event->type = CLUTTER_MOTION;
|
|
|
|
event->motion.time = xevent->xcrossing.time;
|
|
|
|
event->motion.x = xevent->xcrossing.x;
|
|
|
|
event->motion.y = xevent->xcrossing.y;
|
|
|
|
event->motion.modifier_state = xevent->xcrossing.state;
|
|
|
|
event->motion.source = CLUTTER_ACTOR (stage);
|
|
|
|
event->motion.device = manager_x11->core_pointer;
|
|
|
|
res = CLUTTER_TRANSLATE_QUEUE;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case LeaveNotify:
|
|
|
|
if (manager_x11->core_pointer->stage == NULL)
|
|
|
|
{
|
|
|
|
CLUTTER_NOTE (EVENT, "Discarding LeaveNotify for "
|
|
|
|
"ButtonRelease event off-stage");
|
|
|
|
res = CLUTTER_TRANSLATE_REMOVE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* we know that we are leaving the stage here */
|
|
|
|
_clutter_input_device_set_stage (manager_x11->core_pointer, NULL);
|
|
|
|
CLUTTER_NOTE (EVENT, "Leaving the stage (time:%u)",
|
|
|
|
event->crossing.time);
|
|
|
|
|
|
|
|
event->crossing.type = CLUTTER_LEAVE;
|
|
|
|
event->crossing.time = xevent->xcrossing.time;
|
|
|
|
event->crossing.x = xevent->xcrossing.x;
|
|
|
|
event->crossing.y = xevent->xcrossing.y;
|
|
|
|
event->crossing.source = CLUTTER_ACTOR (stage);
|
|
|
|
event->crossing.device = manager_x11->core_pointer;
|
|
|
|
res = CLUTTER_TRANSLATE_QUEUE;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
clutter_event_translator_iface_init (ClutterEventTranslatorIface *iface)
|
|
|
|
{
|
|
|
|
iface->translate_event = clutter_device_manager_x11_translate_event;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
clutter_device_manager_x11_constructed (GObject *gobject)
|
|
|
|
{
|
|
|
|
ClutterDeviceManagerX11 *manager_x11;
|
|
|
|
ClutterBackendX11 *backend_x11;
|
|
|
|
ClutterDeviceManager *manager;
|
|
|
|
#ifdef HAVE_XINPUT
|
|
|
|
XDeviceInfo *x_devices = NULL;
|
|
|
|
int i, n_devices;
|
|
|
|
#endif /* HAVE_XINPUT */
|
|
|
|
|
|
|
|
manager = CLUTTER_DEVICE_MANAGER (gobject);
|
|
|
|
manager_x11 = CLUTTER_DEVICE_MANAGER_X11 (gobject);
|
|
|
|
|
|
|
|
g_object_get (gobject, "backend", &backend_x11, NULL);
|
|
|
|
g_assert (backend_x11 != NULL);
|
|
|
|
|
|
|
|
#ifdef HAVE_XINPUT
|
|
|
|
x_devices = XListInputDevices (backend_x11->xdpy, &n_devices);
|
|
|
|
if (n_devices == 0)
|
|
|
|
{
|
|
|
|
CLUTTER_NOTE (BACKEND, "No XInput devices found");
|
|
|
|
goto default_device;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < n_devices; i++)
|
|
|
|
{
|
|
|
|
XDeviceInfo *info = x_devices + i;
|
|
|
|
ClutterInputDevice *device;
|
|
|
|
|
|
|
|
CLUTTER_NOTE (BACKEND,
|
|
|
|
"Considering device %li with type %d, %d of %d",
|
|
|
|
info->id,
|
|
|
|
info->use,
|
|
|
|
i, n_devices);
|
|
|
|
|
|
|
|
device = create_device (manager_x11, backend_x11, info);
|
|
|
|
if (device != NULL)
|
|
|
|
_clutter_device_manager_add_device (manager, device);
|
|
|
|
}
|
|
|
|
|
|
|
|
XFreeDeviceList (x_devices);
|
|
|
|
|
|
|
|
default_device:
|
|
|
|
#endif /* HAVE_XINPUT */
|
|
|
|
|
|
|
|
/* fallback code in case:
|
|
|
|
*
|
|
|
|
* - we do not have XInput support compiled in
|
|
|
|
* - we do not have the XInput extension
|
|
|
|
*
|
|
|
|
* we register two default devices, one for the pointer
|
|
|
|
* and one for the keyboard. this block must also be
|
|
|
|
* executed for the XInput support because XI does not
|
|
|
|
* cover core devices
|
|
|
|
*/
|
|
|
|
manager_x11->core_pointer =
|
|
|
|
g_object_new (CLUTTER_TYPE_INPUT_DEVICE,
|
|
|
|
"name", "Core Pointer",
|
|
|
|
"has-cursor", TRUE,
|
|
|
|
"device-type", CLUTTER_POINTER_DEVICE,
|
|
|
|
"device-manager", manager_x11,
|
|
|
|
"device-mode", CLUTTER_INPUT_MODE_MASTER,
|
|
|
|
"backend", backend_x11,
|
|
|
|
NULL);
|
|
|
|
CLUTTER_NOTE (BACKEND, "Added core pointer device");
|
|
|
|
|
|
|
|
manager_x11->core_keyboard =
|
|
|
|
g_object_new (CLUTTER_TYPE_INPUT_DEVICE,
|
|
|
|
"name", "Core Keyboard",
|
|
|
|
"has-cursor", FALSE,
|
|
|
|
"device-type", CLUTTER_KEYBOARD_DEVICE,
|
|
|
|
"device-manager", manager_x11,
|
|
|
|
"device-mode", CLUTTER_INPUT_MODE_MASTER,
|
|
|
|
"backend", backend_x11,
|
|
|
|
NULL);
|
|
|
|
CLUTTER_NOTE (BACKEND, "Added core keyboard device");
|
|
|
|
|
|
|
|
/* associate core devices */
|
|
|
|
_clutter_input_device_set_associated_device (manager_x11->core_pointer,
|
|
|
|
manager_x11->core_keyboard);
|
|
|
|
_clutter_input_device_set_associated_device (manager_x11->core_keyboard,
|
|
|
|
manager_x11->core_pointer);
|
|
|
|
|
|
|
|
if (G_OBJECT_CLASS (clutter_device_manager_x11_parent_class)->constructed)
|
|
|
|
G_OBJECT_CLASS (clutter_device_manager_x11_parent_class)->constructed (gobject);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
clutter_device_manager_x11_add_device (ClutterDeviceManager *manager,
|
|
|
|
ClutterInputDevice *device)
|
|
|
|
{
|
|
|
|
ClutterDeviceManagerX11 *manager_x11 = CLUTTER_DEVICE_MANAGER_X11 (manager);
|
|
|
|
|
|
|
|
manager_x11->devices = g_slist_prepend (manager_x11->devices, device);
|
|
|
|
g_hash_table_replace (manager_x11->devices_by_id,
|
|
|
|
GINT_TO_POINTER (device->id),
|
|
|
|
device);
|
|
|
|
|
|
|
|
/* blow the cache */
|
|
|
|
g_slist_free (manager_x11->all_devices);
|
|
|
|
manager_x11->all_devices = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
clutter_device_manager_x11_remove_device (ClutterDeviceManager *manager,
|
|
|
|
ClutterInputDevice *device)
|
|
|
|
{
|
|
|
|
ClutterDeviceManagerX11 *manager_x11 = CLUTTER_DEVICE_MANAGER_X11 (manager);
|
|
|
|
|
|
|
|
g_hash_table_remove (manager_x11->devices_by_id,
|
|
|
|
GINT_TO_POINTER (device->id));
|
|
|
|
manager_x11->devices = g_slist_remove (manager_x11->devices, device);
|
|
|
|
|
|
|
|
/* blow the cache */
|
|
|
|
g_slist_free (manager_x11->all_devices);
|
|
|
|
manager_x11->all_devices = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const GSList *
|
|
|
|
clutter_device_manager_x11_get_devices (ClutterDeviceManager *manager)
|
|
|
|
{
|
|
|
|
ClutterDeviceManagerX11 *manager_x11 = CLUTTER_DEVICE_MANAGER_X11 (manager);
|
|
|
|
|
|
|
|
/* cache the devices list so that we can keep the core pointer
|
|
|
|
* and keyboard outside of the ManagerX11:devices list
|
|
|
|
*/
|
|
|
|
if (manager_x11->all_devices == NULL)
|
|
|
|
{
|
|
|
|
GSList *all_devices = manager_x11->devices;
|
|
|
|
|
|
|
|
all_devices = g_slist_prepend (all_devices, manager_x11->core_keyboard);
|
|
|
|
all_devices = g_slist_prepend (all_devices, manager_x11->core_pointer);
|
|
|
|
|
|
|
|
manager_x11->all_devices = all_devices;
|
|
|
|
}
|
|
|
|
|
|
|
|
return CLUTTER_DEVICE_MANAGER_X11 (manager)->all_devices;
|
|
|
|
}
|
|
|
|
|
|
|
|
static ClutterInputDevice *
|
|
|
|
clutter_device_manager_x11_get_core_device (ClutterDeviceManager *manager,
|
|
|
|
ClutterInputDeviceType type)
|
|
|
|
{
|
|
|
|
ClutterDeviceManagerX11 *manager_x11;
|
|
|
|
|
|
|
|
manager_x11 = CLUTTER_DEVICE_MANAGER_X11 (manager);
|
|
|
|
|
|
|
|
switch (type)
|
|
|
|
{
|
|
|
|
case CLUTTER_POINTER_DEVICE:
|
|
|
|
return manager_x11->core_pointer;
|
|
|
|
|
|
|
|
case CLUTTER_KEYBOARD_DEVICE:
|
|
|
|
return manager_x11->core_keyboard;
|
|
|
|
|
|
|
|
default:
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static ClutterInputDevice *
|
|
|
|
clutter_device_manager_x11_get_device (ClutterDeviceManager *manager,
|
|
|
|
gint id)
|
|
|
|
{
|
|
|
|
ClutterDeviceManagerX11 *manager_x11 = CLUTTER_DEVICE_MANAGER_X11 (manager);
|
|
|
|
|
|
|
|
return g_hash_table_lookup (manager_x11->devices_by_id,
|
|
|
|
GINT_TO_POINTER (id));
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
clutter_device_manager_x11_set_property (GObject *gobject,
|
|
|
|
guint prop_id,
|
|
|
|
const GValue *value,
|
|
|
|
GParamSpec *pspec)
|
|
|
|
{
|
|
|
|
ClutterDeviceManagerX11 *manager_x11 = CLUTTER_DEVICE_MANAGER_X11 (gobject);
|
|
|
|
|
|
|
|
switch (prop_id)
|
|
|
|
{
|
|
|
|
case PROP_EVENT_BASE:
|
|
|
|
manager_x11->xi_event_base = g_value_get_int (value);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
clutter_device_manager_x11_class_init (ClutterDeviceManagerX11Class *klass)
|
|
|
|
{
|
|
|
|
ClutterDeviceManagerClass *manager_class;
|
|
|
|
GObjectClass *gobject_class;
|
|
|
|
|
|
|
|
obj_props[PROP_EVENT_BASE] =
|
|
|
|
g_param_spec_int ("event-base",
|
|
|
|
"Event Base",
|
|
|
|
"The first XI event",
|
|
|
|
-1, G_MAXINT,
|
|
|
|
-1,
|
|
|
|
CLUTTER_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY);
|
|
|
|
|
|
|
|
gobject_class = G_OBJECT_CLASS (klass);
|
|
|
|
gobject_class->constructed = clutter_device_manager_x11_constructed;
|
|
|
|
gobject_class->set_property = clutter_device_manager_x11_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_x11_add_device;
|
|
|
|
manager_class->remove_device = clutter_device_manager_x11_remove_device;
|
|
|
|
manager_class->get_devices = clutter_device_manager_x11_get_devices;
|
|
|
|
manager_class->get_core_device = clutter_device_manager_x11_get_core_device;
|
|
|
|
manager_class->get_device = clutter_device_manager_x11_get_device;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
clutter_device_manager_x11_init (ClutterDeviceManagerX11 *self)
|
|
|
|
{
|
|
|
|
self->devices_by_id = g_hash_table_new (NULL, NULL);
|
|
|
|
}
|