mirror of
https://github.com/brl/mutter.git
synced 2024-12-25 04:22:05 +00:00
e38ea7a20f
Now we either use core X11 or XInput 2. https://bugzilla.gnome.org/show_bug.cgi?id=703969
533 lines
18 KiB
C
533 lines
18 KiB
C
/*
|
|
* Clutter.
|
|
*
|
|
* An OpenGL based 'interactive canvas' library.
|
|
*
|
|
* Copyright © 2009, 2010, 2011 Intel Corp.
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
* Author: Emmanuele Bassi <ebassi@linux.intel.com>
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include "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-private.h"
|
|
#include "clutter-event-translator.h"
|
|
#include "clutter-stage-private.h"
|
|
#include "clutter-private.h"
|
|
|
|
enum
|
|
{
|
|
PROP_0,
|
|
|
|
PROP_EVENT_BASE,
|
|
|
|
PROP_LAST
|
|
};
|
|
|
|
static GParamSpec *obj_props[PROP_LAST] = { NULL, };
|
|
|
|
static void clutter_event_translator_iface_init (ClutterEventTranslatorIface *iface);
|
|
|
|
#define clutter_device_manager_x11_get_type _clutter_device_manager_x11_get_type
|
|
|
|
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));
|
|
|
|
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;
|
|
|
|
clutter_event_set_device (event, 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 != (gunichar) -1) &&
|
|
(event->key.unicode_value != (gunichar) -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;
|
|
}
|
|
|
|
static ClutterTranslateReturn
|
|
clutter_device_manager_x11_translate_event (ClutterEventTranslator *translator,
|
|
gpointer native,
|
|
ClutterEvent *event)
|
|
{
|
|
ClutterDeviceManagerX11 *manager_x11;
|
|
ClutterBackendX11 *backend_x11;
|
|
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 ());
|
|
|
|
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;
|
|
|
|
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.axes = NULL;
|
|
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.axes = NULL;
|
|
break;
|
|
}
|
|
|
|
clutter_event_set_device (event, manager_x11->core_pointer);
|
|
|
|
_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;
|
|
clutter_event_set_device (event, 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.axes = NULL;
|
|
clutter_event_set_device (event, manager_x11->core_pointer);
|
|
res = CLUTTER_TRANSLATE_QUEUE;
|
|
break;
|
|
|
|
case EnterNotify:
|
|
CLUTTER_NOTE (EVENT, "Entering the stage (time:%u)",
|
|
(unsigned int) xevent->xcrossing.time);
|
|
|
|
event->crossing.type = CLUTTER_ENTER;
|
|
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.related = NULL;
|
|
clutter_event_set_device (event, manager_x11->core_pointer);
|
|
|
|
_clutter_input_device_set_stage (manager_x11->core_pointer, stage);
|
|
|
|
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_NOTE (EVENT, "Leaving the stage (time:%u)",
|
|
(unsigned int) xevent->xcrossing.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.related = NULL;
|
|
clutter_event_set_device (event, manager_x11->core_pointer);
|
|
|
|
_clutter_input_device_set_stage (manager_x11->core_pointer, NULL);
|
|
|
|
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;
|
|
|
|
manager_x11 = CLUTTER_DEVICE_MANAGER_X11 (gobject);
|
|
|
|
g_object_get (gobject, "backend", &backend_x11, NULL);
|
|
g_assert (backend_x11 != NULL);
|
|
|
|
manager_x11->core_pointer =
|
|
g_object_new (CLUTTER_TYPE_INPUT_DEVICE_X11,
|
|
"name", "Core Pointer",
|
|
"has-cursor", TRUE,
|
|
"device-type", CLUTTER_POINTER_DEVICE,
|
|
"device-manager", manager_x11,
|
|
"device-mode", CLUTTER_INPUT_MODE_MASTER,
|
|
"backend", backend_x11,
|
|
"enabled", TRUE,
|
|
NULL);
|
|
CLUTTER_NOTE (BACKEND, "Added core pointer device");
|
|
|
|
manager_x11->core_keyboard =
|
|
g_object_new (CLUTTER_TYPE_INPUT_DEVICE_X11,
|
|
"name", "Core Keyboard",
|
|
"has-cursor", FALSE,
|
|
"device-type", CLUTTER_KEYBOARD_DEVICE,
|
|
"device-manager", manager_x11,
|
|
"device-mode", CLUTTER_INPUT_MODE_MASTER,
|
|
"backend", backend_x11,
|
|
"enabled", TRUE,
|
|
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);
|
|
}
|