mirror of
https://github.com/brl/mutter.git
synced 2024-11-30 20:01:00 -05:00
f7fbd6ea6f
Simplify the handling of numlock state, so it can be entirely handled within the input thread. Since the saving/restoring is triggered inside each backend code, there's no need anymore for meta_backend_set_numlock(). Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1403>
972 lines
26 KiB
C
972 lines
26 KiB
C
/*
|
|
* 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 <http://www.gnu.org/licenses/>.
|
|
*
|
|
* Author: Emmanuele Bassi <ebassi@linux.intel.com>
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include <X11/Xatom.h>
|
|
#include <X11/XKBlib.h>
|
|
|
|
#include "backends/meta-backend-private.h"
|
|
#include "backends/meta-input-settings-private.h"
|
|
#include "backends/x11/meta-keymap-x11.h"
|
|
#include "clutter/clutter.h"
|
|
#include "clutter/clutter-mutter.h"
|
|
#include "clutter/x11/clutter-x11.h"
|
|
|
|
typedef struct _DirectionCacheEntry DirectionCacheEntry;
|
|
typedef struct _ClutterKeymapKey ClutterKeymapKey;
|
|
|
|
struct _ClutterKeymapKey
|
|
{
|
|
uint32_t keycode;
|
|
uint32_t group;
|
|
uint32_t level;
|
|
};
|
|
|
|
struct _DirectionCacheEntry
|
|
{
|
|
uint32_t serial;
|
|
Atom group_atom;
|
|
PangoDirection direction;
|
|
};
|
|
|
|
struct _MetaKeymapX11
|
|
{
|
|
ClutterKeymap parent_instance;
|
|
|
|
ClutterBackend *backend;
|
|
|
|
int min_keycode;
|
|
int max_keycode;
|
|
|
|
ClutterModifierType modmap[8];
|
|
|
|
ClutterModifierType num_lock_mask;
|
|
ClutterModifierType scroll_lock_mask;
|
|
ClutterModifierType level3_shift_mask;
|
|
|
|
PangoDirection current_direction;
|
|
|
|
XkbDescPtr xkb_desc;
|
|
int xkb_event_base;
|
|
uint32_t xkb_map_serial;
|
|
Atom current_group_atom;
|
|
uint32_t current_cache_serial;
|
|
DirectionCacheEntry group_direction_cache[4];
|
|
int current_group;
|
|
|
|
GHashTable *reserved_keycodes;
|
|
GQueue *available_keycodes;
|
|
|
|
uint32_t keymap_serial;
|
|
|
|
uint32_t caps_lock_state : 1;
|
|
uint32_t num_lock_state : 1;
|
|
uint32_t has_direction : 1;
|
|
|
|
uint32_t use_xkb : 1;
|
|
uint32_t have_xkb_autorepeat : 1;
|
|
};
|
|
|
|
enum
|
|
{
|
|
PROP_0,
|
|
|
|
PROP_BACKEND,
|
|
|
|
PROP_LAST
|
|
};
|
|
|
|
static GParamSpec *obj_props[PROP_LAST] = { NULL, };
|
|
|
|
G_DEFINE_TYPE (MetaKeymapX11, meta_keymap_x11, CLUTTER_TYPE_KEYMAP)
|
|
|
|
/* code adapted from gdk/x11/gdkkeys-x11.c - update_modmap */
|
|
static void
|
|
update_modmap (Display *display,
|
|
MetaKeymapX11 *keymap_x11)
|
|
{
|
|
static struct {
|
|
const char *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 (MetaKeymapX11 *keymap_x11)
|
|
{
|
|
Display *xdisplay = clutter_x11_get_default_display ();
|
|
|
|
if (keymap_x11->max_keycode == 0)
|
|
XDisplayKeycodes (xdisplay,
|
|
&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 (xdisplay, 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 (xdisplay, flags, keymap_x11->xkb_desc);
|
|
|
|
update_modmap (xdisplay, keymap_x11);
|
|
}
|
|
else if (keymap_x11->xkb_map_serial != keymap_x11->keymap_serial)
|
|
{
|
|
int flags = XkbKeySymsMask
|
|
| XkbKeyTypesMask
|
|
| XkbModifierMapMask
|
|
| XkbVirtualModsMask;
|
|
|
|
XkbGetUpdatedMap (xdisplay, flags, keymap_x11->xkb_desc);
|
|
|
|
flags = XkbGroupNamesMask | XkbVirtualModNamesMask;
|
|
XkbGetNames (xdisplay, flags, keymap_x11->xkb_desc);
|
|
|
|
update_modmap (xdisplay, keymap_x11);
|
|
|
|
keymap_x11->xkb_map_serial = keymap_x11->keymap_serial;
|
|
}
|
|
|
|
if (keymap_x11->num_lock_mask == 0)
|
|
keymap_x11->num_lock_mask = XkbKeysymToModifiers (xdisplay, XK_Num_Lock);
|
|
|
|
if (keymap_x11->scroll_lock_mask == 0)
|
|
keymap_x11->scroll_lock_mask = XkbKeysymToModifiers (xdisplay,
|
|
XK_Scroll_Lock);
|
|
if (keymap_x11->level3_shift_mask == 0)
|
|
keymap_x11->level3_shift_mask = XkbKeysymToModifiers (xdisplay,
|
|
XK_ISO_Level3_Shift);
|
|
|
|
return keymap_x11->xkb_desc;
|
|
}
|
|
|
|
static void
|
|
update_locked_mods (MetaKeymapX11 *keymap_x11,
|
|
int 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;
|
|
|
|
g_debug ("Locks state changed - Num: %s, Caps: %s",
|
|
keymap_x11->num_lock_state ? "set" : "unset",
|
|
keymap_x11->caps_lock_state ? "set" : "unset");
|
|
|
|
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, "state-changed");
|
|
|
|
if (keymap_x11->num_lock_state != old_num_lock_state)
|
|
{
|
|
MetaBackend *backend;
|
|
MetaInputSettings *input_settings;
|
|
|
|
backend = meta_get_backend ();
|
|
input_settings = meta_backend_get_input_settings (backend);
|
|
|
|
if (input_settings)
|
|
{
|
|
meta_input_settings_maybe_save_numlock_state (input_settings,
|
|
keymap_x11->num_lock_state);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* the code to retrieve the keymap direction and cache it
|
|
* is taken from GDK:
|
|
* gdk/x11/gdkkeys-x11.c
|
|
*/
|
|
static PangoDirection
|
|
get_direction (XkbDescPtr xkb,
|
|
int group)
|
|
{
|
|
int rtl_minus_ltr = 0; /* total number of RTL keysyms minus LTR ones */
|
|
int code;
|
|
|
|
for (code = xkb->min_key_code;
|
|
code <= xkb->max_key_code;
|
|
code += 1)
|
|
{
|
|
int level = 0;
|
|
KeySym sym = XkbKeySymEntry (xkb, code, level, group);
|
|
PangoDirection dir =
|
|
_clutter_pango_unichar_direction (clutter_keysym_to_unicode (sym));
|
|
|
|
switch (dir)
|
|
{
|
|
case PANGO_DIRECTION_RTL:
|
|
rtl_minus_ltr++;
|
|
break;
|
|
|
|
case PANGO_DIRECTION_LTR:
|
|
rtl_minus_ltr--;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (rtl_minus_ltr > 0)
|
|
return PANGO_DIRECTION_RTL;
|
|
|
|
return PANGO_DIRECTION_LTR;
|
|
}
|
|
|
|
static PangoDirection
|
|
get_direction_from_cache (MetaKeymapX11 *keymap_x11,
|
|
XkbDescPtr xkb,
|
|
int group)
|
|
{
|
|
Atom group_atom = xkb->names->groups[group];
|
|
gboolean cache_hit = FALSE;
|
|
DirectionCacheEntry *cache = keymap_x11->group_direction_cache;
|
|
PangoDirection direction = PANGO_DIRECTION_NEUTRAL;
|
|
int i;
|
|
|
|
if (keymap_x11->has_direction)
|
|
{
|
|
/* look up in the cache */
|
|
for (i = 0; i < G_N_ELEMENTS (keymap_x11->group_direction_cache); i++)
|
|
{
|
|
if (cache[i].group_atom == group_atom)
|
|
{
|
|
cache_hit = TRUE;
|
|
cache[i].serial = keymap_x11->current_cache_serial++;
|
|
direction = cache[i].direction;
|
|
group_atom = cache[i].group_atom;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* initialize the cache */
|
|
for (i = 0; i < G_N_ELEMENTS (keymap_x11->group_direction_cache); i++)
|
|
{
|
|
cache[i].group_atom = 0;
|
|
cache[i].direction = PANGO_DIRECTION_NEUTRAL;
|
|
cache[i].serial = keymap_x11->current_cache_serial;
|
|
}
|
|
|
|
keymap_x11->current_cache_serial += 1;
|
|
}
|
|
|
|
/* insert the new entry in the cache */
|
|
if (!cache_hit)
|
|
{
|
|
int oldest = 0;
|
|
|
|
direction = get_direction (xkb, group);
|
|
|
|
/* replace the oldest entry */
|
|
for (i = 0; i < G_N_ELEMENTS (keymap_x11->group_direction_cache); i++)
|
|
{
|
|
if (cache[i].serial < cache[oldest].serial)
|
|
oldest = i;
|
|
}
|
|
|
|
cache[oldest].group_atom = group_atom;
|
|
cache[oldest].direction = direction;
|
|
cache[oldest].serial = keymap_x11->current_cache_serial++;
|
|
}
|
|
|
|
return direction;
|
|
}
|
|
|
|
static void
|
|
update_direction (MetaKeymapX11 *keymap_x11,
|
|
int group)
|
|
{
|
|
XkbDescPtr xkb = get_xkb (keymap_x11);
|
|
Atom group_atom;
|
|
|
|
group_atom = xkb->names->groups[group];
|
|
|
|
if (!keymap_x11->has_direction || keymap_x11->current_group_atom != group_atom)
|
|
{
|
|
keymap_x11->current_direction = get_direction_from_cache (keymap_x11, xkb, group);
|
|
keymap_x11->current_group_atom = group_atom;
|
|
keymap_x11->has_direction = TRUE;
|
|
}
|
|
}
|
|
|
|
static void
|
|
meta_keymap_x11_constructed (GObject *object)
|
|
{
|
|
MetaKeymapX11 *keymap_x11 = META_KEYMAP_X11 (object);
|
|
Display *xdisplay = clutter_x11_get_default_display ();
|
|
int xkb_major = XkbMajorVersion;
|
|
int xkb_minor = XkbMinorVersion;
|
|
|
|
g_assert (keymap_x11->backend != NULL);
|
|
|
|
if (XkbLibraryVersion (&xkb_major, &xkb_minor))
|
|
{
|
|
xkb_major = XkbMajorVersion;
|
|
xkb_minor = XkbMinorVersion;
|
|
|
|
if (XkbQueryExtension (xdisplay,
|
|
NULL,
|
|
&keymap_x11->xkb_event_base,
|
|
NULL,
|
|
&xkb_major, &xkb_minor))
|
|
{
|
|
Bool detectable_autorepeat_supported;
|
|
|
|
keymap_x11->use_xkb = TRUE;
|
|
|
|
XkbSelectEvents (xdisplay,
|
|
XkbUseCoreKbd,
|
|
XkbNewKeyboardNotifyMask | XkbMapNotifyMask | XkbStateNotifyMask,
|
|
XkbNewKeyboardNotifyMask | XkbMapNotifyMask | XkbStateNotifyMask);
|
|
|
|
XkbSelectEventDetails (xdisplay,
|
|
XkbUseCoreKbd, XkbStateNotify,
|
|
XkbAllStateComponentsMask,
|
|
XkbGroupLockMask | XkbModifierLockMask);
|
|
|
|
/* enable XKB autorepeat */
|
|
XkbSetDetectableAutoRepeat (xdisplay,
|
|
True,
|
|
&detectable_autorepeat_supported);
|
|
|
|
keymap_x11->have_xkb_autorepeat = detectable_autorepeat_supported;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
meta_keymap_x11_set_property (GObject *object,
|
|
guint prop_id,
|
|
const GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
MetaKeymapX11 *keymap = META_KEYMAP_X11 (object);
|
|
|
|
switch (prop_id)
|
|
{
|
|
case PROP_BACKEND:
|
|
keymap->backend = g_value_get_object (value);
|
|
break;
|
|
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
meta_keymap_x11_refresh_reserved_keycodes (MetaKeymapX11 *keymap_x11)
|
|
{
|
|
Display *dpy = clutter_x11_get_default_display ();
|
|
GHashTableIter iter;
|
|
gpointer key, value;
|
|
|
|
g_hash_table_iter_init (&iter, keymap_x11->reserved_keycodes);
|
|
while (g_hash_table_iter_next (&iter, &key, &value))
|
|
{
|
|
uint32_t reserved_keycode = GPOINTER_TO_UINT (key);
|
|
uint32_t reserved_keysym = GPOINTER_TO_UINT (value);
|
|
uint32_t actual_keysym = XkbKeycodeToKeysym (dpy, reserved_keycode, 0, 0);
|
|
|
|
/* If an available keycode is no longer mapped to the stored keysym, then
|
|
* the keycode should not be considered available anymore and should be
|
|
* removed both from the list of available and reserved keycodes.
|
|
*/
|
|
if (reserved_keysym != actual_keysym)
|
|
{
|
|
g_hash_table_iter_remove (&iter);
|
|
g_queue_remove (keymap_x11->available_keycodes, key);
|
|
}
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
meta_keymap_x11_replace_keycode (MetaKeymapX11 *keymap_x11,
|
|
KeyCode keycode,
|
|
KeySym keysym)
|
|
{
|
|
if (keymap_x11->use_xkb)
|
|
{
|
|
Display *dpy = clutter_x11_get_default_display ();
|
|
XkbDescPtr xkb = get_xkb (keymap_x11);
|
|
XkbMapChangesRec changes;
|
|
|
|
XFlush (dpy);
|
|
|
|
xkb->device_spec = XkbUseCoreKbd;
|
|
memset (&changes, 0, sizeof(changes));
|
|
|
|
if (keysym != NoSymbol)
|
|
{
|
|
int types[XkbNumKbdGroups] = { XkbOneLevelIndex };
|
|
XkbChangeTypesOfKey (xkb, keycode, 1, XkbGroup1Mask, types, &changes);
|
|
XkbKeySymEntry (xkb, keycode, 0, 0) = keysym;
|
|
}
|
|
else
|
|
{
|
|
/* Reset to NoSymbol */
|
|
XkbChangeTypesOfKey (xkb, keycode, 0, XkbGroup1Mask, NULL, &changes);
|
|
}
|
|
|
|
changes.changed = XkbKeySymsMask | XkbKeyTypesMask;
|
|
changes.first_key_sym = keycode;
|
|
changes.num_key_syms = 1;
|
|
changes.first_type = 0;
|
|
changes.num_types = xkb->map->num_types;
|
|
XkbChangeMap (dpy, xkb, &changes);
|
|
|
|
XFlush (dpy);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static void
|
|
meta_keymap_x11_finalize (GObject *object)
|
|
{
|
|
MetaKeymapX11 *keymap;
|
|
GHashTableIter iter;
|
|
gpointer key, value;
|
|
|
|
keymap = META_KEYMAP_X11 (object);
|
|
|
|
meta_keymap_x11_refresh_reserved_keycodes (keymap);
|
|
g_hash_table_iter_init (&iter, keymap->reserved_keycodes);
|
|
while (g_hash_table_iter_next (&iter, &key, &value))
|
|
{
|
|
uint32_t keycode = GPOINTER_TO_UINT (key);
|
|
meta_keymap_x11_replace_keycode (keymap, keycode, NoSymbol);
|
|
}
|
|
|
|
g_hash_table_destroy (keymap->reserved_keycodes);
|
|
g_queue_free (keymap->available_keycodes);
|
|
|
|
if (keymap->xkb_desc != NULL)
|
|
XkbFreeKeyboard (keymap->xkb_desc, XkbAllComponentsMask, True);
|
|
|
|
G_OBJECT_CLASS (meta_keymap_x11_parent_class)->finalize (object);
|
|
}
|
|
|
|
static gboolean
|
|
meta_keymap_x11_get_num_lock_state (ClutterKeymap *keymap)
|
|
{
|
|
MetaKeymapX11 *keymap_x11 = META_KEYMAP_X11 (keymap);
|
|
|
|
return keymap_x11->num_lock_state;
|
|
}
|
|
|
|
static gboolean
|
|
meta_keymap_x11_get_caps_lock_state (ClutterKeymap *keymap)
|
|
{
|
|
MetaKeymapX11 *keymap_x11 = META_KEYMAP_X11 (keymap);
|
|
|
|
return keymap_x11->caps_lock_state;
|
|
}
|
|
|
|
static PangoDirection
|
|
meta_keymap_x11_get_direction (ClutterKeymap *keymap)
|
|
{
|
|
MetaKeymapX11 *keymap_x11;
|
|
|
|
g_return_val_if_fail (META_IS_KEYMAP_X11 (keymap), PANGO_DIRECTION_NEUTRAL);
|
|
|
|
keymap_x11 = META_KEYMAP_X11 (keymap);
|
|
|
|
if (keymap_x11->use_xkb)
|
|
{
|
|
if (!keymap_x11->has_direction)
|
|
{
|
|
XkbStateRec state_rec;
|
|
|
|
XkbGetState (clutter_x11_get_default_display (),
|
|
XkbUseCoreKbd, &state_rec);
|
|
update_direction (keymap_x11, XkbStateGroup (&state_rec));
|
|
}
|
|
|
|
return keymap_x11->current_direction;
|
|
}
|
|
else
|
|
{
|
|
return PANGO_DIRECTION_NEUTRAL;
|
|
}
|
|
}
|
|
|
|
static void
|
|
meta_keymap_x11_class_init (MetaKeymapX11Class *klass)
|
|
{
|
|
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
|
ClutterKeymapClass *keymap_class = CLUTTER_KEYMAP_CLASS (klass);
|
|
|
|
obj_props[PROP_BACKEND] =
|
|
g_param_spec_object ("backend",
|
|
"Backend",
|
|
"The Clutter backend",
|
|
CLUTTER_TYPE_BACKEND,
|
|
G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY);
|
|
|
|
gobject_class->constructed = meta_keymap_x11_constructed;
|
|
gobject_class->set_property = meta_keymap_x11_set_property;
|
|
gobject_class->finalize = meta_keymap_x11_finalize;
|
|
|
|
keymap_class->get_num_lock_state = meta_keymap_x11_get_num_lock_state;
|
|
keymap_class->get_caps_lock_state = meta_keymap_x11_get_caps_lock_state;
|
|
keymap_class->get_direction = meta_keymap_x11_get_direction;
|
|
|
|
g_object_class_install_properties (gobject_class, PROP_LAST, obj_props);
|
|
}
|
|
|
|
static void
|
|
meta_keymap_x11_init (MetaKeymapX11 *keymap)
|
|
{
|
|
keymap->current_direction = PANGO_DIRECTION_NEUTRAL;
|
|
keymap->current_group = -1;
|
|
keymap->reserved_keycodes = g_hash_table_new (NULL, NULL);
|
|
keymap->available_keycodes = g_queue_new ();
|
|
}
|
|
|
|
gboolean
|
|
meta_keymap_x11_handle_event (MetaKeymapX11 *keymap_x11,
|
|
XEvent *xevent)
|
|
{
|
|
gboolean retval;
|
|
|
|
if (!keymap_x11->use_xkb)
|
|
return FALSE;
|
|
|
|
retval = FALSE;
|
|
|
|
if (xevent->type == keymap_x11->xkb_event_base)
|
|
{
|
|
XkbEvent *xkb_event = (XkbEvent *) xevent;
|
|
|
|
switch (xkb_event->any.xkb_type)
|
|
{
|
|
case XkbStateNotify:
|
|
g_debug ("Updating keyboard state");
|
|
keymap_x11->current_group = XkbStateGroup (&xkb_event->state);
|
|
update_direction (keymap_x11, keymap_x11->current_group);
|
|
update_locked_mods (keymap_x11, xkb_event->state.locked_mods);
|
|
retval = TRUE;
|
|
break;
|
|
|
|
case XkbNewKeyboardNotify:
|
|
case XkbMapNotify:
|
|
g_debug ("Updating keyboard mapping");
|
|
XkbRefreshKeyboardMapping (&xkb_event->map);
|
|
keymap_x11->keymap_serial += 1;
|
|
retval = TRUE;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
else if (xevent->type == MappingNotify)
|
|
{
|
|
XRefreshKeyboardMapping (&xevent->xmapping);
|
|
keymap_x11->keymap_serial += 1;
|
|
retval = TRUE;
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
int
|
|
meta_keymap_x11_get_key_group (MetaKeymapX11 *keymap,
|
|
ClutterModifierType state)
|
|
{
|
|
return XkbGroupForCoreState (state);
|
|
}
|
|
|
|
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
|
|
|
|
/* XXX - yes, I know that XKeycodeToKeysym() has been deprecated; hopefully,
|
|
* this code will never get run on any decent system that is also able to
|
|
* run Clutter. I just don't want to copy the implementation inside GDK for
|
|
* a fallback path.
|
|
*/
|
|
static int
|
|
translate_keysym (MetaKeymapX11 *keymap,
|
|
uint32_t hardware_keycode)
|
|
{
|
|
int retval;
|
|
|
|
retval = XKeycodeToKeysym (clutter_x11_get_default_display (),
|
|
hardware_keycode, 0);
|
|
return retval;
|
|
}
|
|
|
|
G_GNUC_END_IGNORE_DEPRECATIONS
|
|
|
|
int
|
|
meta_keymap_x11_translate_key_state (MetaKeymapX11 *keymap,
|
|
uint32_t hardware_keycode,
|
|
ClutterModifierType *modifier_state_p,
|
|
ClutterModifierType *mods_p)
|
|
{
|
|
ClutterModifierType unconsumed_modifiers = 0;
|
|
ClutterModifierType modifier_state = *modifier_state_p;
|
|
int retval;
|
|
|
|
g_return_val_if_fail (META_IS_KEYMAP_X11 (keymap), 0);
|
|
|
|
if (keymap->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
|
|
retval = translate_keysym (keymap, hardware_keycode);
|
|
|
|
if (mods_p)
|
|
*mods_p = unconsumed_modifiers;
|
|
|
|
*modifier_state_p = modifier_state & ~(keymap->num_lock_mask |
|
|
keymap->scroll_lock_mask |
|
|
LockMask);
|
|
|
|
return retval;
|
|
}
|
|
|
|
gboolean
|
|
meta_keymap_x11_get_is_modifier (MetaKeymapX11 *keymap,
|
|
int keycode)
|
|
{
|
|
g_return_val_if_fail (META_IS_KEYMAP_X11 (keymap), FALSE);
|
|
|
|
if (keycode < keymap->min_keycode || keycode > keymap->max_keycode)
|
|
return FALSE;
|
|
|
|
if (keymap->use_xkb)
|
|
{
|
|
XkbDescRec *xkb = get_xkb (keymap);
|
|
|
|
if (xkb->map->modmap && xkb->map->modmap[keycode] != 0)
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static gboolean
|
|
meta_keymap_x11_get_entries_for_keyval (MetaKeymapX11 *keymap_x11,
|
|
uint32_t keyval,
|
|
ClutterKeymapKey **keys,
|
|
int *n_keys)
|
|
{
|
|
if (keymap_x11->use_xkb)
|
|
{
|
|
XkbDescRec *xkb = get_xkb (keymap_x11);
|
|
GArray *retval;
|
|
int keycode;
|
|
|
|
keycode = keymap_x11->min_keycode;
|
|
retval = g_array_new (FALSE, FALSE, sizeof (ClutterKeymapKey));
|
|
|
|
while (keycode <= keymap_x11->max_keycode)
|
|
{
|
|
int max_shift_levels = XkbKeyGroupsWidth (xkb, keycode);
|
|
int group = 0;
|
|
int level = 0;
|
|
int total_syms = XkbKeyNumSyms (xkb, keycode);
|
|
int i = 0;
|
|
KeySym *entry;
|
|
|
|
/* entry is an array with all syms for group 0, all
|
|
* syms for group 1, etc. and for each group the
|
|
* shift level syms are in order
|
|
*/
|
|
entry = XkbKeySymsPtr (xkb, keycode);
|
|
|
|
while (i < total_syms)
|
|
{
|
|
g_assert (i == (group * max_shift_levels + level));
|
|
|
|
if (entry[i] == keyval)
|
|
{
|
|
ClutterKeymapKey key;
|
|
|
|
key.keycode = keycode;
|
|
key.group = group;
|
|
key.level = level;
|
|
|
|
g_array_append_val (retval, key);
|
|
|
|
g_assert (XkbKeySymEntry (xkb, keycode, level, group) ==
|
|
keyval);
|
|
}
|
|
|
|
++level;
|
|
|
|
if (level == max_shift_levels)
|
|
{
|
|
level = 0;
|
|
++group;
|
|
}
|
|
|
|
++i;
|
|
}
|
|
|
|
++keycode;
|
|
}
|
|
|
|
if (retval->len > 0)
|
|
{
|
|
*keys = (ClutterKeymapKey*) retval->data;
|
|
*n_keys = retval->len;
|
|
}
|
|
else
|
|
{
|
|
*keys = NULL;
|
|
*n_keys = 0;
|
|
}
|
|
|
|
g_array_free (retval, retval->len > 0 ? FALSE : TRUE);
|
|
|
|
return *n_keys > 0;
|
|
}
|
|
else
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
static uint32_t
|
|
meta_keymap_x11_get_available_keycode (MetaKeymapX11 *keymap_x11)
|
|
{
|
|
if (keymap_x11->use_xkb)
|
|
{
|
|
meta_keymap_x11_refresh_reserved_keycodes (keymap_x11);
|
|
|
|
if (g_hash_table_size (keymap_x11->reserved_keycodes) < 5)
|
|
{
|
|
Display *dpy = clutter_x11_get_default_display ();
|
|
XkbDescPtr xkb = get_xkb (keymap_x11);
|
|
uint32_t i;
|
|
|
|
for (i = xkb->max_key_code; i >= xkb->min_key_code; --i)
|
|
{
|
|
if (XkbKeycodeToKeysym (dpy, i, 0, 0) == NoSymbol)
|
|
return i;
|
|
}
|
|
}
|
|
|
|
return GPOINTER_TO_UINT (g_queue_pop_head (keymap_x11->available_keycodes));
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
gboolean
|
|
meta_keymap_x11_reserve_keycode (MetaKeymapX11 *keymap_x11,
|
|
uint32_t keyval,
|
|
uint32_t *keycode_out)
|
|
{
|
|
g_return_val_if_fail (META_IS_KEYMAP_X11 (keymap_x11), FALSE);
|
|
g_return_val_if_fail (keyval != 0, FALSE);
|
|
g_return_val_if_fail (keycode_out != NULL, FALSE);
|
|
|
|
*keycode_out = meta_keymap_x11_get_available_keycode (keymap_x11);
|
|
|
|
if (*keycode_out == NoSymbol)
|
|
{
|
|
g_warning ("Cannot reserve a keycode for keyval %d: no available keycode", keyval);
|
|
return FALSE;
|
|
}
|
|
|
|
if (!meta_keymap_x11_replace_keycode (keymap_x11, *keycode_out, keyval))
|
|
{
|
|
g_warning ("Failed to remap keycode %d to keyval %d", *keycode_out, keyval);
|
|
return FALSE;
|
|
}
|
|
|
|
g_hash_table_insert (keymap_x11->reserved_keycodes, GUINT_TO_POINTER (*keycode_out), GUINT_TO_POINTER (keyval));
|
|
g_queue_remove (keymap_x11->available_keycodes, GUINT_TO_POINTER (*keycode_out));
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void
|
|
meta_keymap_x11_release_keycode_if_needed (MetaKeymapX11 *keymap_x11,
|
|
uint32_t keycode)
|
|
{
|
|
g_return_if_fail (META_IS_KEYMAP_X11 (keymap_x11));
|
|
|
|
if (!g_hash_table_contains (keymap_x11->reserved_keycodes, GUINT_TO_POINTER (keycode)) ||
|
|
g_queue_index (keymap_x11->available_keycodes, GUINT_TO_POINTER (keycode)) != -1)
|
|
return;
|
|
|
|
g_queue_push_tail (keymap_x11->available_keycodes, GUINT_TO_POINTER (keycode));
|
|
}
|
|
|
|
void
|
|
meta_keymap_x11_latch_modifiers (MetaKeymapX11 *keymap_x11,
|
|
uint32_t level,
|
|
gboolean enable)
|
|
{
|
|
uint32_t modifiers[] = {
|
|
0,
|
|
ShiftMask,
|
|
keymap_x11->level3_shift_mask,
|
|
keymap_x11->level3_shift_mask | ShiftMask,
|
|
};
|
|
uint32_t value = 0;
|
|
|
|
if (!keymap_x11->use_xkb)
|
|
return;
|
|
|
|
level = CLAMP (level, 0, G_N_ELEMENTS (modifiers) - 1);
|
|
|
|
if (enable)
|
|
value = modifiers[level];
|
|
else
|
|
value = 0;
|
|
|
|
XkbLatchModifiers (clutter_x11_get_default_display (),
|
|
XkbUseCoreKbd, modifiers[level],
|
|
value);
|
|
}
|
|
|
|
static uint32_t
|
|
meta_keymap_x11_get_current_group (MetaKeymapX11 *keymap_x11)
|
|
{
|
|
XkbStateRec state_rec;
|
|
|
|
if (keymap_x11->current_group >= 0)
|
|
return keymap_x11->current_group;
|
|
|
|
XkbGetState (clutter_x11_get_default_display (),
|
|
XkbUseCoreKbd, &state_rec);
|
|
return XkbStateGroup (&state_rec);
|
|
}
|
|
|
|
gboolean
|
|
meta_keymap_x11_keycode_for_keyval (MetaKeymapX11 *keymap_x11,
|
|
uint32_t keyval,
|
|
uint32_t *keycode_out,
|
|
uint32_t *level_out)
|
|
{
|
|
ClutterKeymapKey *keys;
|
|
int i, n_keys, group;
|
|
gboolean found = FALSE;
|
|
|
|
g_return_val_if_fail (keycode_out != NULL, FALSE);
|
|
g_return_val_if_fail (level_out != NULL, FALSE);
|
|
|
|
group = meta_keymap_x11_get_current_group (keymap_x11);
|
|
|
|
if (!meta_keymap_x11_get_entries_for_keyval (keymap_x11, keyval, &keys, &n_keys))
|
|
return FALSE;
|
|
|
|
for (i = 0; i < n_keys && !found; i++)
|
|
{
|
|
if (keys[i].group == group)
|
|
{
|
|
*keycode_out = keys[i].keycode;
|
|
*level_out = keys[i].level;
|
|
found = TRUE;
|
|
}
|
|
}
|
|
|
|
if (!found)
|
|
{
|
|
GHashTableIter iter;
|
|
gpointer key, value;
|
|
|
|
g_hash_table_iter_init (&iter, keymap_x11->reserved_keycodes);
|
|
while (!found && g_hash_table_iter_next (&iter, &key, &value))
|
|
{
|
|
uint32_t reserved_keycode = GPOINTER_TO_UINT (key);
|
|
uint32_t reserved_keysym = GPOINTER_TO_UINT (value);
|
|
|
|
if (keyval == reserved_keysym)
|
|
{
|
|
*keycode_out = reserved_keycode;
|
|
*level_out = 0;
|
|
found = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
g_free (keys);
|
|
return found;
|
|
}
|