diff --git a/README b/README index 9d132fd77..a1666dff4 100644 --- a/README +++ b/README @@ -28,6 +28,7 @@ On X11, Clutter depends on the following extensions • XExt • XFixes • XInput 1.x (if --enable-xinput is passed to configure) + • XKB When running the OpenGL flavor, Clutter requires at least version 1.3 or 1.2 with the multitexturing extension. However to build Clutter diff --git a/clutter/clutter-backend.c b/clutter/clutter-backend.c index 1a92267d0..7e32ac55f 100644 --- a/clutter/clutter-backend.c +++ b/clutter/clutter-backend.c @@ -518,6 +518,36 @@ _clutter_backend_get_units_per_em (ClutterBackend *backend, return priv->units_per_em; } +void +_clutter_backend_copy_event_data (ClutterBackend *backend, + ClutterEvent *src, + ClutterEvent *dest) +{ + ClutterBackendClass *klass; + + g_return_if_fail (CLUTTER_IS_BACKEND (backend)); + g_return_if_fail (src != NULL); + g_return_if_fail (dest != NULL); + + klass = CLUTTER_BACKEND_GET_CLASS (backend); + if (klass->copy_event_data != NULL) + klass->copy_event_data (backend, src, dest); +} + +void +_clutter_backend_free_event_data (ClutterBackend *backend, + ClutterEvent *event) +{ + ClutterBackendClass *klass; + + g_return_if_fail (CLUTTER_IS_BACKEND (backend)); + g_return_if_fail (event != NULL); + + klass = CLUTTER_BACKEND_GET_CLASS (backend); + if (klass->free_event_data != NULL) + klass->free_event_data (backend, event); +} + /** * clutter_get_default_backend: * diff --git a/clutter/clutter-backend.h b/clutter/clutter-backend.h index 1012664d1..21d2ac39f 100644 --- a/clutter/clutter-backend.h +++ b/clutter/clutter-backend.h @@ -85,6 +85,12 @@ struct _ClutterBackendClass ClutterStage *stage); ClutterDeviceManager *(* get_device_manager) (ClutterBackend *backend); + void (* copy_event_data) (ClutterBackend *backend, + ClutterEvent *src, + ClutterEvent *dest); + void (* free_event_data) (ClutterBackend *backend, + ClutterEvent *event); + /* signals */ void (* resolution_changed) (ClutterBackend *backend); void (* font_changed) (ClutterBackend *backend); diff --git a/clutter/clutter-event.c b/clutter/clutter-event.c index d6755cb22..f71b21312 100644 --- a/clutter/clutter-event.c +++ b/clutter/clutter-event.c @@ -43,6 +43,52 @@ * be synthesized by Clutter itself or by the application code. */ +typedef struct _ClutterEventPrivate { + ClutterEvent base; + + gpointer platform_data; +} ClutterEventPrivate; + +static GHashTable *all_events = NULL; + +static gboolean +is_event_allocated (const ClutterEvent *event) +{ + if (all_events == NULL) + return FALSE; + + return g_hash_table_lookup (all_events, event) != NULL; +} + +/* + * _clutter_event_get_platform_data: + * @event: a #ClutterEvent + * + * Retrieves the pointer to platform-specific data inside an event + * + * Return value: a pointer to platform-specific data + * + * Since: 1.4 + */ +gpointer +_clutter_event_get_platform_data (const ClutterEvent *event) +{ + if (!is_event_allocated (event)) + return NULL; + + return ((ClutterEventPrivate *) event)->platform_data; +} + +void +_clutter_event_set_platform_data (ClutterEvent *event, + gpointer data) +{ + if (!is_event_allocated (event)) + return; + + ((ClutterEventPrivate *) event)->platform_data = data; +} + /** * clutter_event_type: * @event: a #ClutterEvent @@ -606,10 +652,18 @@ ClutterEvent * clutter_event_new (ClutterEventType type) { ClutterEvent *new_event; + ClutterEventPrivate *priv; - new_event = g_slice_new0 (ClutterEvent); + priv = g_slice_new0 (ClutterEventPrivate); + + new_event = (ClutterEvent *) priv; new_event->type = new_event->any.type = type; + if (all_events == NULL) + all_events = g_hash_table_new (NULL, NULL); + + g_hash_table_replace (all_events, priv, GUINT_TO_POINTER (1)); + return new_event; } @@ -631,6 +685,11 @@ clutter_event_copy (ClutterEvent *event) new_event = clutter_event_new (CLUTTER_NOTHING); *new_event = *event; + if (is_event_allocated (event)) + _clutter_backend_copy_event_data (clutter_get_default_backend (), + event, + new_event); + return new_event; } @@ -644,7 +703,12 @@ void clutter_event_free (ClutterEvent *event) { if (G_LIKELY (event != NULL)) - g_slice_free (ClutterEvent, event); + { + _clutter_backend_free_event_data (clutter_get_default_backend (), event); + + g_hash_table_remove (all_events, event); + g_slice_free (ClutterEventPrivate, (ClutterEventPrivate *) event); + } } /** diff --git a/clutter/clutter-private.h b/clutter/clutter-private.h index 40d717c74..666428b4d 100644 --- a/clutter/clutter-private.h +++ b/clutter/clutter-private.h @@ -245,7 +245,6 @@ int _clutter_stage_get_pending_swaps (ClutterStage *stage); gboolean _clutter_stage_has_full_redraw_queued (ClutterStage *stage); - /* vfuncs implemented by backend */ GType _clutter_backend_impl_get_type (void); @@ -270,6 +269,12 @@ gboolean _clutter_backend_post_parse (ClutterBackend *backend, GError **error); void _clutter_backend_init_events (ClutterBackend *backend); +void _clutter_backend_copy_event_data (ClutterBackend *backend, + ClutterEvent *src, + ClutterEvent *dest); +void _clutter_backend_free_event_data (ClutterBackend *backend, + ClutterEvent *event); + ClutterFeatureFlags _clutter_backend_get_features (ClutterBackend *backend); gfloat _clutter_backend_get_units_per_em (ClutterBackend *backend, @@ -341,6 +346,10 @@ void _clutter_effect_post_paint (ClutterEffect *effect); GType _clutter_layout_manager_get_child_meta_type (ClutterLayoutManager *manager); +void _clutter_event_set_platform_data (ClutterEvent *event, + gpointer data); +gpointer _clutter_event_get_platform_data (const ClutterEvent *event); + G_END_DECLS #endif /* _HAVE_CLUTTER_PRIVATE_H */ diff --git a/clutter/x11/Makefile.am b/clutter/x11/Makefile.am index 6e227bb71..b8c7c03da 100644 --- a/clutter/x11/Makefile.am +++ b/clutter/x11/Makefile.am @@ -46,6 +46,8 @@ libclutter_x11_la_SOURCES = \ $(srcdir)/clutter-event-x11.c \ $(srcdir)/clutter-input-device-x11.h \ $(srcdir)/clutter-input-device-x11.c \ + $(srcdir)/clutter-keymap-x11.h \ + $(srcdir)/clutter-keymap-x11.c \ $(srcdir)/clutter-settings-x11.h \ $(srcdir)/clutter-stage-x11.h \ $(srcdir)/clutter-stage-x11.c \ diff --git a/clutter/x11/clutter-backend-x11.c b/clutter/x11/clutter-backend-x11.c index 81ce4d817..8a78c1937 100644 --- a/clutter/x11/clutter-backend-x11.c +++ b/clutter/x11/clutter-backend-x11.c @@ -319,6 +319,12 @@ clutter_backend_x11_post_parse (ClutterBackend *backend, "backend", backend_x11, NULL); + /* register keymap */ + backend_x11->keymap = + g_object_new (CLUTTER_TYPE_KEYMAP_X11, + "backend", backend_x11, + NULL); + /* create XSETTINGS client */ backend_x11->xsettings = _clutter_xsettings_client_new (backend_x11->xdpy, @@ -475,6 +481,29 @@ clutter_backend_x11_get_features (ClutterBackend *backend) return CLUTTER_FEATURE_STAGE_USER_RESIZE | CLUTTER_FEATURE_STAGE_CURSOR; } +static void +clutter_backend_x11_copy_event_data (ClutterBackend *backend, + ClutterEvent *src, + ClutterEvent *dest) +{ + gpointer event_x11; + + event_x11 = _clutter_event_get_platform_data (src); + if (event_x11 != NULL) + _clutter_event_set_platform_data (dest, _clutter_event_x11_copy (event_x11)); +} + +static void +clutter_backend_x11_free_event_data (ClutterBackend *backend, + ClutterEvent *event) +{ + gpointer event_x11; + + event_x11 = _clutter_event_get_platform_data (event); + if (event_x11 != NULL) + _clutter_event_x11_free (event_x11); +} + gboolean clutter_backend_x11_handle_event (ClutterBackendX11 *backend_x11, XEvent *xevent) @@ -516,6 +545,8 @@ clutter_backend_x11_class_init (ClutterBackendX11Class *klass) backend_class->add_options = clutter_backend_x11_add_options; backend_class->get_features = clutter_backend_x11_get_features; backend_class->get_device_manager = clutter_backend_x11_get_device_manager; + backend_class->copy_event_data = clutter_backend_x11_copy_event_data; + backend_class->free_event_data = clutter_backend_x11_free_event_data; backendx11_class->handle_event = clutter_backend_x11_handle_event; } diff --git a/clutter/x11/clutter-backend-x11.h b/clutter/x11/clutter-backend-x11.h index eebdc4db9..e51cc135f 100644 --- a/clutter/x11/clutter-backend-x11.h +++ b/clutter/x11/clutter-backend-x11.h @@ -30,6 +30,7 @@ #include "clutter-x11.h" +#include "clutter-keymap-x11.h" #include "xsettings/xsettings-client.h" G_BEGIN_DECLS @@ -88,6 +89,11 @@ struct _ClutterBackendX11 XSettingsClient *xsettings; Window xsettings_xwin; + + ClutterKeymapX11 *keymap; + int xkb_event_base; + gboolean use_xkb; + gboolean have_xkb_autorepeat; }; struct _ClutterBackendX11Class @@ -109,6 +115,9 @@ struct _ClutterBackendX11Class XEvent *xevent); }; +/* platform-specific event data */ +typedef struct _ClutterEventX11 ClutterEventX11; + void _clutter_backend_x11_events_init (ClutterBackend *backend); void _clutter_backend_x11_events_uninit (ClutterBackend *backend); @@ -146,6 +155,15 @@ _clutter_x11_get_device_for_xid (XID id); void _clutter_x11_select_events (Window xwin); +ClutterEventX11 * +_clutter_event_x11_new (void); + +ClutterEventX11 * +_clutter_event_x11_copy (ClutterEventX11 *event_x11); + +void +_clutter_event_x11_free (ClutterEventX11 *event_x11); + G_END_DECLS #endif /* __CLUTTER_BACKEND_X11_H__ */ diff --git a/clutter/x11/clutter-event-x11.c b/clutter/x11/clutter-event-x11.c index 28bb4b3ab..acddb389f 100644 --- a/clutter/x11/clutter-event-x11.c +++ b/clutter/x11/clutter-event-x11.c @@ -30,6 +30,7 @@ #include "clutter-stage-x11.h" #include "clutter-backend-x11.h" +#include "clutter-keymap-x11.h" #include "clutter-x11.h" #include "../clutter-backend.h" @@ -54,6 +55,10 @@ #include #endif +#ifdef HAVE_XKB +#include +#endif + /* XEMBED protocol support for toolkit embedding */ #define XEMBED_MAPPED (1 << 0) #define MAX_SUPPORTED_XEMBED_VERSION 1 @@ -85,6 +90,38 @@ struct _ClutterEventSource GPollFD event_poll_fd; }; +struct _ClutterEventX11 +{ + /* additional fields for Key events */ + gint key_group; + + guint key_is_modifier : 1; + guint num_lock_set : 1; + guint caps_lock_set : 1; +}; + +ClutterEventX11 * +_clutter_event_x11_new (void) +{ + return g_slice_new0 (ClutterEventX11); +} + +ClutterEventX11 * +_clutter_event_x11_copy (ClutterEventX11 *event_x11) +{ + if (event_x11 != NULL) + return g_slice_dup (ClutterEventX11, event_x11); + + return NULL; +} + +void +_clutter_event_x11_free (ClutterEventX11 *event_x11) +{ + if (event_x11 != NULL) + g_slice_free (ClutterEventX11, event_x11); +} + static gboolean clutter_event_prepare (GSource *source, gint *timeout); static gboolean clutter_event_check (GSource *source); @@ -295,16 +332,18 @@ convert_xdevicekey_to_xkey (XDeviceKeyEvent *xkev, } #endif /* HAVE_XINPUT */ -static void -translate_key_event (ClutterBackend *backend, - ClutterEvent *event, - XEvent *xevent) +static inline void +translate_key_event (ClutterBackendX11 *backend_x11, + ClutterEvent *event, + XEvent *xevent) { - char buffer[256+1]; + ClutterEventX11 *event_x11; + char buffer[256 + 1]; int n; - - CLUTTER_NOTE (EVENT, "Translating key %s event", - xevent->xany.type == KeyPress ? "press" : "release"); + + /* KeyEvents have platform specific data associated to them */ + event_x11 = _clutter_event_x11_new (); + _clutter_event_set_platform_data (event, event_x11); event->key.time = xevent->xkey.time; event->key.modifier_state = (ClutterModifierType) xevent->xkey.state; @@ -312,9 +351,21 @@ translate_key_event (ClutterBackend *backend, /* keyval is the key ignoring all modifiers ('1' vs. '!') */ event->key.keyval = - XKeycodeToKeysym (xevent->xkey.display, - xevent->xkey.keycode, - 0); + _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); @@ -324,10 +375,13 @@ translate_key_event (ClutterBackend *backend, event->key.unicode_value = g_utf8_get_char_validated (buffer, n); if ((event->key.unicode_value != -1) && (event->key.unicode_value != -2)) - return; + goto out; } - - event->key.unicode_value = (gunichar)'\0'; + else + event->key.unicode_value = (gunichar)'\0'; + +out: + return; } static gboolean @@ -509,7 +563,9 @@ event_translate (ClutterBackend *backend, if ((stage_x11->state & CLUTTER_STAGE_STATE_FULLSCREEN) || (stage_x11->xwin_width != xevent->xconfigure.width) || (stage_x11->xwin_height != xevent->xconfigure.height)) - clutter_actor_queue_relayout (CLUTTER_ACTOR (stage)); + { + clutter_actor_queue_relayout (CLUTTER_ACTOR (stage)); + } /* If we're fullscreened, we want these variables to * represent the size of the window before it was set @@ -669,7 +725,7 @@ event_translate (ClutterBackend *backend, clutter_device_manager_get_core_device (manager, CLUTTER_KEYBOARD_DEVICE); - translate_key_event (backend, event, xevent); + translate_key_event (backend_x11, event, xevent); set_user_time (backend_x11, &xwindow, xevent->xkey.time); break; @@ -685,8 +741,11 @@ event_translate (ClutterBackend *backend, * 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 (XPending (xevent->xkey.display)) + if (!backend_x11->have_xkb_autorepeat && XPending (xevent->xkey.display)) { XEvent next_event; @@ -706,7 +765,7 @@ event_translate (ClutterBackend *backend, clutter_device_manager_get_core_device (manager, CLUTTER_KEYBOARD_DEVICE); - translate_key_event (backend, event, xevent); + translate_key_event (backend_x11, event, xevent); break; default: @@ -986,7 +1045,7 @@ event_translate (ClutterBackend *backend, ? CLUTTER_KEY_PRESS : CLUTTER_KEY_RELEASE; - translate_key_event (backend, event, &xevent_converted); + translate_key_event (backend_x11, event, &xevent_converted); if (xevent->type == key_press) set_user_time (backend_x11, &xwindow, xkev->time); @@ -1031,9 +1090,7 @@ events_queue (ClutterBackend *backend) g_queue_push_head (clutter_context->events_queue, event); } else - { - clutter_event_free (event); - } + clutter_event_free (event); } } @@ -1209,3 +1266,29 @@ clutter_x11_get_current_event_time (void) return CLUTTER_BACKEND_X11 (backend)->last_event_time; } + +/** + * clutter_x11_event_get_key_group: + * @event: a #ClutterEvent of type %CLUTTER_KEY_PRESS or %CLUTTER_KEY_RELEASE + * + * Retrieves the group for the modifiers set in @event + * + * Return value: the group id + * + * Since: 1.4 + */ +gint +clutter_x11_event_get_key_group (const ClutterEvent *event) +{ + ClutterEventX11 *event_x11; + + g_return_val_if_fail (event != NULL, 0); + g_return_val_if_fail (event->type == CLUTTER_KEY_PRESS || + event->type == CLUTTER_KEY_RELEASE, 0); + + event_x11 = _clutter_event_get_platform_data (event); + if (event_x11 == NULL) + return 0; + + return event_x11->key_group; +} diff --git a/clutter/x11/clutter-keymap-x11.c b/clutter/x11/clutter-keymap-x11.c new file mode 100644 index 000000000..abd2cb53e --- /dev/null +++ b/clutter/x11/clutter-keymap-x11.c @@ -0,0 +1,422 @@ +/* + * Clutter. + * + * An OpenGL based 'interactive canvas' library. + * + * Copyright (C) 2010 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 . + * + * Author: Emmanuele Bassi + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "clutter-keymap-x11.h" +#include "clutter-backend-x11.h" + +#include "clutter-debug.h" +#include "clutter-private.h" + +#include + +#ifdef HAVE_XINPUT +#include +#endif + +#ifdef HAVE_XKB +#include +#endif + +typedef struct _ClutterKeymapX11Class ClutterKeymapX11Class; + +struct _ClutterKeymapX11 +{ + GObject parent_instance; + + ClutterBackend *backend; + + gint min_keycode; + gint max_keycode; + + ClutterModifierType modmap[8]; + + ClutterModifierType num_lock_mask; + +#ifdef HAVE_XKB + XkbDescPtr xkb_desc; +#endif + + guint caps_lock_state : 1; + guint num_lock_state : 1; +}; + +struct _ClutterKeymapX11Class +{ + GObjectClass parent_class; +}; + +enum +{ + PROP_0, + + PROP_BACKEND +}; + +G_DEFINE_TYPE (ClutterKeymapX11, clutter_keymap_x11, G_TYPE_OBJECT); + +#ifdef HAVE_XKB + +/* code adapted from gdk/x11/gdkkeys-x11.c - update_modmap */ +static void +update_modmap (Display *display, + ClutterKeymapX11 *keymap_x11) +{ + static struct { + const gchar *name; + Atom atom; + ClutterModifierType mask; + } vmods[] = { + { "Meta", 0, CLUTTER_META_MASK }, + { "Super", 0, CLUTTER_SUPER_MASK }, + { "Hyper", 0, CLUTTER_HYPER_MASK }, + { NULL, 0, 0 } + }; + + int i, j, k; + + if (vmods[0].atom == 0) + for (i = 0; vmods[i].name; i++) + vmods[i].atom = XInternAtom (display, vmods[i].name, FALSE); + + for (i = 0; i < 8; i++) + keymap_x11->modmap[i] = 1 << i; + + for (i = 0; i < XkbNumVirtualMods; i++) + { + for (j = 0; vmods[j].atom; j++) + { + if (keymap_x11->xkb_desc->names->vmods[i] == vmods[j].atom) + { + for (k = 0; k < 8; k++) + { + if (keymap_x11->xkb_desc->server->vmods[i] & (1 << k)) + keymap_x11->modmap[k] |= vmods[j].mask; + } + } + } + } +} + +static XkbDescPtr +get_xkb (ClutterKeymapX11 *keymap_x11) +{ + ClutterBackendX11 *backend_x11 = CLUTTER_BACKEND_X11 (keymap_x11->backend); + + if (keymap_x11->max_keycode == 0) + XDisplayKeycodes (backend_x11->xdpy, + &keymap_x11->min_keycode, + &keymap_x11->max_keycode); + + if (keymap_x11->xkb_desc == NULL) + { + int flags = XkbKeySymsMask + | XkbKeyTypesMask + | XkbModifierMapMask + | XkbVirtualModsMask; + + keymap_x11->xkb_desc = XkbGetMap (backend_x11->xdpy, flags, XkbUseCoreKbd); + if (G_UNLIKELY (keymap_x11->xkb_desc == NULL)) + { + g_error ("Failed to get the keymap from XKB"); + return NULL; + } + + flags = XkbGroupNamesMask | XkbVirtualModNamesMask; + XkbGetNames (backend_x11->xdpy, flags, keymap_x11->xkb_desc); + + update_modmap (backend_x11->xdpy, keymap_x11); + } + + if (keymap_x11->num_lock_mask == 0) + keymap_x11->num_lock_mask = XkbKeysymToModifiers (backend_x11->xdpy, + XK_Num_Lock); + + return keymap_x11->xkb_desc; +} +#endif /* HAVE_XKB */ + +#ifdef HAVE_XKB +static void +update_locked_mods (ClutterKeymapX11 *keymap_x11, + gint locked_mods) +{ + gboolean old_caps_lock_state, old_num_lock_state; + + old_caps_lock_state = keymap_x11->caps_lock_state; + old_num_lock_state = keymap_x11->num_lock_state; + + keymap_x11->caps_lock_state = (locked_mods & CLUTTER_LOCK_MASK) != 0; + keymap_x11->num_lock_state = (locked_mods & keymap_x11->num_lock_mask) != 0; + + CLUTTER_NOTE (BACKEND, "Locks state changed - Num: %s, Caps: %s", + keymap_x11->num_lock_state ? "set" : "unset", + keymap_x11->caps_lock_state ? "set" : "unset"); + +#if 0 + /* Add signal to ClutterBackend? */ + if ((keymap_x11->caps_lock_state != old_caps_lock_state) || + (keymap_x11->num_lock_state != old_num_lock_state)) + g_signal_emit_by_name (keymap_x11->backend, "key-lock-changed"); +#endif +} + +static ClutterX11FilterReturn +xkb_filter (XEvent *xevent, + ClutterEvent *event, + gpointer data) +{ + ClutterBackendX11 *backend_x11 = data; + ClutterKeymapX11 *keymap_x11 = backend_x11->keymap; + + g_assert (keymap_x11 != NULL); + + if (!backend_x11->use_xkb) + return CLUTTER_X11_FILTER_CONTINUE; + + if (xevent->type == backend_x11->xkb_event_base) + { + XkbEvent *xkb_event = (XkbEvent *) xevent; + + CLUTTER_NOTE (BACKEND, "Received XKB event [%d]", + xkb_event->any.xkb_type); + + switch (xkb_event->any.xkb_type) + { + case XkbStateNotify: + update_locked_mods (keymap_x11, xkb_event->state.locked_mods); + break; + + default: + break; + } + } + + return CLUTTER_X11_FILTER_CONTINUE; +} +#endif /* HAVE_XKB */ + +static void +clutter_keymap_x11_constructed (GObject *gobject) +{ + ClutterKeymapX11 *keymap_x11 = CLUTTER_KEYMAP_X11 (gobject); + ClutterBackendX11 *backend_x11; + + g_assert (keymap_x11->backend != NULL); + backend_x11 = CLUTTER_BACKEND_X11 (keymap_x11->backend); + +#if HAVE_XKB + { + gint xkb_major = XkbMajorVersion; + gint xkb_minor = XkbMinorVersion; + + if (XkbLibraryVersion (&xkb_major, &xkb_minor)) + { + xkb_major = XkbMajorVersion; + xkb_minor = XkbMinorVersion; + + if (XkbQueryExtension (backend_x11->xdpy, + NULL, &backend_x11->xkb_event_base, NULL, + &xkb_major, &xkb_minor)) + { + Bool detectable_autorepeat_supported; + + backend_x11->use_xkb = TRUE; + + XkbSelectEvents (backend_x11->xdpy, + XkbUseCoreKbd, + XkbNewKeyboardNotifyMask | XkbMapNotifyMask | XkbStateNotifyMask, + XkbNewKeyboardNotifyMask | XkbMapNotifyMask | XkbStateNotifyMask); + + XkbSelectEventDetails (backend_x11->xdpy, + XkbUseCoreKbd, XkbStateNotify, + XkbAllStateComponentsMask, + XkbGroupLockMask|XkbModifierLockMask); + + clutter_x11_add_filter (xkb_filter, backend_x11); + + /* enable XKB autorepeat */ + XkbSetDetectableAutoRepeat (backend_x11->xdpy, + True, + &detectable_autorepeat_supported); + + backend_x11->have_xkb_autorepeat = detectable_autorepeat_supported; + + CLUTTER_NOTE (BACKEND, "Detectable autorepeat: %s", + backend_x11->have_xkb_autorepeat ? "supported" + : "not supported"); + } + } + } +#endif /* HAVE_XKB */ +} + +static void +clutter_keymap_x11_set_property (GObject *gobject, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + ClutterKeymapX11 *keymap = CLUTTER_KEYMAP_X11 (gobject); + + switch (prop_id) + { + case PROP_BACKEND: + keymap->backend = g_value_get_object (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); + break; + } +} + +static void +clutter_keymap_x11_finalize (GObject *gobject) +{ + ClutterKeymapX11 *keymap; + + keymap = CLUTTER_KEYMAP_X11 (gobject); + +#ifdef HAVE_XKB + if (keymap->xkb_desc != NULL) + XkbFreeKeyboard (keymap->xkb_desc, XkbAllComponentsMask, True); +#endif + + G_OBJECT_CLASS (clutter_keymap_x11_parent_class)->finalize (gobject); +} + +static void +clutter_keymap_x11_class_init (ClutterKeymapX11Class *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + GParamSpec *pspec; + + gobject_class->constructed = clutter_keymap_x11_constructed; + gobject_class->set_property = clutter_keymap_x11_set_property; + gobject_class->finalize = clutter_keymap_x11_finalize; + + pspec = g_param_spec_object ("backend", + "Backend", + "The Clutter backend", + CLUTTER_TYPE_BACKEND, + CLUTTER_PARAM_WRITABLE | + G_PARAM_CONSTRUCT_ONLY); + g_object_class_install_property (gobject_class, PROP_BACKEND, pspec); +} + +static void +clutter_keymap_x11_init (ClutterKeymapX11 *keymap) +{ +} + +gint +_clutter_keymap_x11_get_key_group (ClutterKeymapX11 *keymap, + ClutterModifierType state) +{ +#ifdef HAVE_XKB + return XkbGroupForCoreState (state); +#else + return 0; +#endif /* HAVE_XKB */ +} + +gboolean +_clutter_keymap_x11_get_num_lock_state (ClutterKeymapX11 *keymap) +{ + g_return_val_if_fail (CLUTTER_IS_KEYMAP_X11 (keymap), FALSE); + + return keymap->num_lock_state; +} + +gboolean +_clutter_keymap_x11_get_caps_lock_state (ClutterKeymapX11 *keymap) +{ + g_return_val_if_fail (CLUTTER_IS_KEYMAP_X11 (keymap), FALSE); + + return keymap->caps_lock_state; +} + +gint +_clutter_keymap_x11_translate_key_state (ClutterKeymapX11 *keymap, + guint hardware_keycode, + ClutterModifierType modifier_state, + ClutterModifierType *mods_p) +{ + ClutterBackendX11 *backend_x11; + ClutterModifierType unconsumed_modifiers = 0; + gint retval; + + g_return_val_if_fail (CLUTTER_IS_KEYMAP_X11 (keymap), 0); + + backend_x11 = CLUTTER_BACKEND_X11 (keymap->backend); + +#ifdef HAVE_XKB + if (backend_x11->use_xkb) + { + XkbDescRec *xkb = get_xkb (keymap); + KeySym tmp_keysym; + + if (XkbTranslateKeyCode (xkb, hardware_keycode, modifier_state, + &unconsumed_modifiers, + &tmp_keysym)) + { + retval = tmp_keysym; + } + else + retval = 0; + } + else +#endif /* HAVE_XKB */ + retval = XKeycodeToKeysym (backend_x11->xdpy, hardware_keycode, 0); + + if (mods_p) + *mods_p = unconsumed_modifiers; + + return retval; +} + +gboolean +_clutter_keymap_x11_get_is_modifier (ClutterKeymapX11 *keymap, + guint keycode) +{ + g_return_val_if_fail (CLUTTER_IS_KEYMAP_X11 (keymap), FALSE); + + if (keycode < keymap->min_keycode || keycode > keymap->max_keycode) + return FALSE; + +#ifdef HAVE_XKB + if (CLUTTER_BACKEND_X11 (keymap->backend)->use_xkb) + { + XkbDescRec *xkb = get_xkb (keymap); + + if (xkb->map->modmap && xkb->map->modmap[keycode] != 0) + return TRUE; + } +#endif /* HAVE_XKB */ + + return FALSE; +} diff --git a/clutter/x11/clutter-keymap-x11.h b/clutter/x11/clutter-keymap-x11.h new file mode 100644 index 000000000..34abf3997 --- /dev/null +++ b/clutter/x11/clutter-keymap-x11.h @@ -0,0 +1,53 @@ +/* + * 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 . + * + * Author: Emmanuele Bassi + */ + +#ifndef __CLUTTER_KEYMAP_X11_H__ +#define __CLUTTER_KEYMAP_X11_H__ + +#include +#include + +G_BEGIN_DECLS + +#define CLUTTER_TYPE_KEYMAP_X11 (clutter_keymap_x11_get_type ()) +#define CLUTTER_KEYMAP_X11(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_KEYMAP_X11, ClutterKeymapX11)) +#define CLUTTER_IS_KEYMAP_X11(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_KEYMAP_X11)) + +typedef struct _ClutterKeymapX11 ClutterKeymapX11; + +GType clutter_keymap_x11_get_type (void) G_GNUC_CONST; + +gint _clutter_keymap_x11_get_key_group (ClutterKeymapX11 *keymap, + ClutterModifierType state); +gboolean _clutter_keymap_x11_get_num_lock_state (ClutterKeymapX11 *keymap); +gboolean _clutter_keymap_x11_get_caps_lock_state (ClutterKeymapX11 *keymap); +gint _clutter_keymap_x11_translate_key_state (ClutterKeymapX11 *keymap, + guint hardware_keycode, + ClutterModifierType modifier_state, + ClutterModifierType *mods_p); +gboolean _clutter_keymap_x11_get_is_modifier (ClutterKeymapX11 *keymap, + guint keycode); + +G_END_DECLS + +#endif /* __CLUTTER_KEYMAP_X11_H__ */ diff --git a/clutter/x11/clutter-x11.h b/clutter/x11/clutter-x11.h index 630e63af4..70e3324bb 100644 --- a/clutter/x11/clutter-x11.h +++ b/clutter/x11/clutter-x11.h @@ -141,6 +141,8 @@ gboolean clutter_x11_get_use_argb_visual (void); Time clutter_x11_get_current_event_time (void); +gint clutter_x11_event_get_key_group (const ClutterEvent *event); + G_END_DECLS #endif /* __CLUTTER_X11_H__ */ diff --git a/configure.ac b/configure.ac index fd8badcd7..86d146f0f 100644 --- a/configure.ac +++ b/configure.ac @@ -692,7 +692,7 @@ AS_IF([test "x$SUPPORT_XLIB" = "x1"], [yes], [ - AC_DEFINE(HAVE_XINPUT, 1, Use the XINPUT X extension) + AC_DEFINE(HAVE_XINPUT, 1, [Use the XINPUT X extension]) X11_LIBS="$X11_LIBS -lXi" X11_PC_FILES="$X11_PC_FILES xi" @@ -702,6 +702,23 @@ AS_IF([test "x$SUPPORT_XLIB" = "x1"], [], ) + # XKB + clutter_save_CPPFLAGS="$CPPFLAGS" + CPPFLAGS="$CPPFLAGS $X11_CFLAGS" + + clutter_save_LIBS="$LIBS" + LIBS="$LIBS $X11_LIBS" + + have_xkb=no + AC_CHECK_FUNC([XkbQueryExtension], + [ + AC_DEFINE(HAVE_XKB, 1, [Define to use XKB extension]) + have_xkb=yes + ]) + + CPPFLAGS="$clutter_save_CPPFLAGS" + LIBS="$clutter_save_LIBS" + x11_tests=yes BACKEND_PC_FILES="$BACKEND_PC_FILES $X11_PC_FILES" FLAVOUR_LIBS="$FLAVOUR_LIBS $X11_LIBS" @@ -1144,6 +1161,7 @@ fi if test "x$SUPPORT_XLIB" = "x1"; then echo " Enable XInput 1.0: ${xinput}" +echo " Enable XKB: ${have_xkb}" echo " Enable X11 tests: ${x11_tests}" fi