forked from AuroraMiddleware/gtk
99c3928cec
We currently calling gdk_display_map_keyval up to once per key event per shortcut trigger, and that function does an expensive loop over the entire keymap and allocates an array. Avoid this by caching the entries in a single array, and have a lookup table for finding the entries for a keyval. To do this, change the GdkKeymap.get_entries_for_keyval signature, and change the ::keys-changed signal to be RUN_FIRST, since we want to clear the cache in the class handler before running signal handlers. These changes are possible now, since keymaps are no longer public API.
928 lines
32 KiB
C
928 lines
32 KiB
C
/* GDK - The GIMP Drawing Kit
|
||
* Copyright (C) 2000 Red Hat, Inc.
|
||
*
|
||
* 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/>.
|
||
*/
|
||
|
||
/*
|
||
* Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
|
||
* file for a list of people on the GTK+ Team. See the ChangeLog
|
||
* files for a list of changes. These files are distributed with
|
||
* GTK+ at ftp://ftp.gtk.org/pub/gtk/.
|
||
*/
|
||
|
||
#include "config.h"
|
||
|
||
#include "gdkkeysyms.h"
|
||
#include "gdkkeysprivate.h"
|
||
#include "gdkdisplay.h"
|
||
#include "gdkdisplaymanagerprivate.h"
|
||
|
||
|
||
/**
|
||
* SECTION:keys
|
||
* @Short_description: Functions for manipulating keyboard codes
|
||
* @Title: Key Values
|
||
*
|
||
* Key values are the codes which are sent whenever a key is pressed or released.
|
||
* They are included in the data contained in a key press or release #GdkEvent.
|
||
* The complete list of key values can be found in the `gdk/gdkkeysyms.h` header
|
||
* file.
|
||
*
|
||
* Key values are regularly updated from the upstream X.org X11 implementation,
|
||
* so new values are added regularly. They will be prefixed with GDK_KEY_ rather
|
||
* than XF86XK_ or XK_ (for older symbols).
|
||
*
|
||
* Key values can be converted into a string representation using
|
||
* gdk_keyval_name(). The reverse function, converting a string to a key value,
|
||
* is provided by gdk_keyval_from_name().
|
||
*
|
||
* The case of key values can be determined using gdk_keyval_is_upper() and
|
||
* gdk_keyval_is_lower(). Key values can be converted to upper or lower case
|
||
* using gdk_keyval_to_upper() and gdk_keyval_to_lower().
|
||
*
|
||
* When it makes sense, key values can be converted to and from
|
||
* Unicode characters with gdk_keyval_to_unicode() and gdk_unicode_to_keyval().
|
||
*
|
||
* # Groups # {#key-group-explanation}
|
||
*
|
||
* At the lowest level, physical keys on the keyboard are represented by
|
||
* numeric keycodes, and GDK knows how to translate these keycodes into
|
||
* key values according to the configured keyboard layout and the current
|
||
* state of the keyboard. In the GDK api, the mapping from keycodes to key
|
||
* values is available via gdk_display_map_keycode(), and the reverse mapping
|
||
* is available via gdk_display_map_keyval(). The results of these functions
|
||
* are returned in #GdkKeymapKey structs.
|
||
*
|
||
* You can think of a #GdkKeymapKey as a representation of a symbol printed on
|
||
* a physical keyboard key. That is, it contains three pieces of information.
|
||
* First, it contains the hardware keycode; this is an identifying number for
|
||
* a physical key. Second, it contains the “level” of the key. The level indicates
|
||
* which symbol on the key will be used, in a vertical direction. So on a standard
|
||
* US keyboard, the key with the number “1“ on it also has the exclamation point
|
||
* (”!”) character on it. The level indicates whether to use the “1” or the “!”
|
||
* symbol. The letter keys are considered to have a lowercase letter at level 0,
|
||
* and an uppercase letter at level 1, though normally only the uppercase letter
|
||
* is printed on the key. Third, the #GdkKeymapKey contains a group; groups are
|
||
* not used on standard US keyboards, but are used in many other countries. On a
|
||
* keyboard with groups, there can be 3 or 4 symbols printed on a single key.
|
||
* The group indicates movement in a horizontal direction. Usually groups are
|
||
* used for two different languages. In group 0, a key might have two English
|
||
* characters, and in group 1 it might have two Hebrew characters. The Hebrew
|
||
* characters will be printed on the key next to the English characters.
|
||
*
|
||
* When GDK creates a key event in order to deliver a key press or release,
|
||
* it first converts the current keyboard state into an effective group and
|
||
* level. This is done via a set of rules that varies widely according to
|
||
* type of keyboard and user configuration. The input to this translation
|
||
* consists of the hardware keycode pressed, the active modifiers, and the
|
||
* active group. It then applies the appropriate rules, and returns the
|
||
* group/level to be used to index the keymap, along with the modifiers
|
||
* which did not affect the group and level. i.e. it returns “unconsumed
|
||
* modifiers.” The keyboard group may differ from the effective group used
|
||
* for lookups because some keys don't have multiple groups - e.g. the Enter
|
||
* key is always in group 0 regardless of keyboard state.
|
||
*
|
||
* The results of the translation, including the keyval, are all included
|
||
* in the key event and can be obtained via #GdkEvent getters.
|
||
*
|
||
* # Consumed modifiers
|
||
*
|
||
* The @consumed_modifiers in a key event are modifiers that should be masked
|
||
* out from @state when comparing this key press to a hot key. For instance,
|
||
* on a US keyboard, the `plus` symbol is shifted, so when comparing a key
|
||
* press to a `<Control>plus` accelerator `<Shift>` should be masked out.
|
||
*
|
||
* |[<!-- language="C" -->
|
||
* // We want to ignore irrelevant modifiers like ScrollLock
|
||
* #define ALL_ACCELS_MASK (GDK_CONTROL_MASK | GDK_SHIFT_MASK | GDK_ALT_MASK)
|
||
* state = gdk_event_get_modifier_state (event);
|
||
* gdk_keymap_translate_keyboard_state (keymap,
|
||
* gdk_key_event_get_keycode (event),
|
||
* state,
|
||
* gdk_key_event_get_group (event),
|
||
* &keyval, NULL, NULL, &consumed);
|
||
* if (keyval == GDK_PLUS &&
|
||
* (state & ~consumed & ALL_ACCELS_MASK) == GDK_CONTROL_MASK)
|
||
* // Control was pressed
|
||
* ]|
|
||
*
|
||
* An older interpretation @consumed_modifiers was that it contained
|
||
* all modifiers that might affect the translation of the key;
|
||
* this allowed accelerators to be stored with irrelevant consumed
|
||
* modifiers, by doing:
|
||
* |[<!-- language="C" -->
|
||
* // XXX Don’t do this XXX
|
||
* if (keyval == accel_keyval &&
|
||
* (state & ~consumed & ALL_ACCELS_MASK) == (accel_mods & ~consumed))
|
||
* // Accelerator was pressed
|
||
* ]|
|
||
*
|
||
* However, this did not work if multi-modifier combinations were
|
||
* used in the keymap, since, for instance, `<Control>` would be
|
||
* masked out even if only `<Control><Alt>` was used in the keymap.
|
||
* To support this usage as well as well as possible, all single
|
||
* modifier combinations that could affect the key for any combination
|
||
* of modifiers will be returned in @consumed_modifiers; multi-modifier
|
||
* combinations are returned only when actually found in @state. When
|
||
* you store accelerators, you should always store them with consumed
|
||
* modifiers removed. Store `<Control>plus`, not `<Control><Shift>plus`.
|
||
*/
|
||
|
||
enum {
|
||
PROP_0,
|
||
PROP_DISPLAY,
|
||
LAST_PROP
|
||
};
|
||
|
||
enum {
|
||
DIRECTION_CHANGED,
|
||
KEYS_CHANGED,
|
||
STATE_CHANGED,
|
||
LAST_SIGNAL
|
||
};
|
||
|
||
static GParamSpec *props[LAST_PROP] = { NULL, };
|
||
static guint signals[LAST_SIGNAL] = { 0 };
|
||
|
||
G_DEFINE_TYPE (GdkKeymap, gdk_keymap, G_TYPE_OBJECT)
|
||
|
||
static void
|
||
gdk_keymap_get_property (GObject *object,
|
||
guint prop_id,
|
||
GValue *value,
|
||
GParamSpec *pspec)
|
||
{
|
||
GdkKeymap *keymap = GDK_KEYMAP (object);
|
||
|
||
switch (prop_id)
|
||
{
|
||
case PROP_DISPLAY:
|
||
g_value_set_object (value, keymap->display);
|
||
break;
|
||
default:
|
||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||
}
|
||
}
|
||
|
||
static void
|
||
gdk_keymap_set_property (GObject *object,
|
||
guint prop_id,
|
||
const GValue *value,
|
||
GParamSpec *pspec)
|
||
{
|
||
GdkKeymap *keymap = GDK_KEYMAP (object);
|
||
|
||
switch (prop_id)
|
||
{
|
||
case PROP_DISPLAY:
|
||
keymap->display = g_value_get_object (value);
|
||
break;
|
||
|
||
default:
|
||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||
}
|
||
}
|
||
|
||
static void
|
||
gdk_keymap_finalize (GObject *object)
|
||
{
|
||
GdkKeymap *keymap = GDK_KEYMAP (object);
|
||
|
||
g_array_free (keymap->cached_keys, TRUE);
|
||
g_hash_table_unref (keymap->cache);
|
||
|
||
G_OBJECT_CLASS (gdk_keymap_parent_class)->finalize (object);
|
||
}
|
||
|
||
static void
|
||
gdk_keymap_keys_changed (GdkKeymap *keymap)
|
||
{
|
||
GdkKeymapKey key;
|
||
|
||
g_array_set_size (keymap->cached_keys, 0);
|
||
|
||
key.keycode = 0;
|
||
key.group = 0;
|
||
key.level = 0;
|
||
|
||
g_array_append_val (keymap->cached_keys, key);
|
||
|
||
g_hash_table_remove_all (keymap->cache);
|
||
}
|
||
|
||
static void
|
||
gdk_keymap_class_init (GdkKeymapClass *klass)
|
||
{
|
||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||
|
||
object_class->finalize = gdk_keymap_finalize;
|
||
object_class->get_property = gdk_keymap_get_property;
|
||
object_class->set_property = gdk_keymap_set_property;
|
||
|
||
klass->keys_changed = gdk_keymap_keys_changed;
|
||
|
||
props[PROP_DISPLAY] =
|
||
g_param_spec_object ("display",
|
||
"Display",
|
||
"The display of the keymap",
|
||
GDK_TYPE_DISPLAY,
|
||
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
|
||
|
||
g_object_class_install_properties (object_class, LAST_PROP, props);
|
||
|
||
/**
|
||
* GdkKeymap::direction-changed:
|
||
* @keymap: the object on which the signal is emitted
|
||
*
|
||
* The ::direction-changed signal gets emitted when the direction
|
||
* of the keymap changes. See gdk_keymap_get_direction().
|
||
*/
|
||
signals[DIRECTION_CHANGED] =
|
||
g_signal_new (g_intern_static_string ("direction-changed"),
|
||
G_OBJECT_CLASS_TYPE (object_class),
|
||
G_SIGNAL_RUN_LAST,
|
||
G_STRUCT_OFFSET (GdkKeymapClass, direction_changed),
|
||
NULL, NULL,
|
||
NULL,
|
||
G_TYPE_NONE,
|
||
0);
|
||
/**
|
||
* GdkKeymap::keys-changed:
|
||
* @keymap: the object on which the signal is emitted
|
||
*
|
||
* The ::keys-changed signal is emitted when the mapping represented by
|
||
* @keymap changes.
|
||
*/
|
||
signals[KEYS_CHANGED] =
|
||
g_signal_new (g_intern_static_string ("keys-changed"),
|
||
G_OBJECT_CLASS_TYPE (object_class),
|
||
G_SIGNAL_RUN_FIRST,
|
||
G_STRUCT_OFFSET (GdkKeymapClass, keys_changed),
|
||
NULL, NULL,
|
||
NULL,
|
||
G_TYPE_NONE,
|
||
0);
|
||
|
||
/**
|
||
* GdkKeymap::state-changed:
|
||
* @keymap: the object on which the signal is emitted
|
||
*
|
||
* The ::state-changed signal is emitted when the state of the
|
||
* keyboard changes, e.g when Caps Lock is turned on or off.
|
||
* See gdk_keymap_get_caps_lock_state().
|
||
*/
|
||
signals[STATE_CHANGED] =
|
||
g_signal_new (g_intern_static_string ("state-changed"),
|
||
G_OBJECT_CLASS_TYPE (object_class),
|
||
G_SIGNAL_RUN_LAST,
|
||
G_STRUCT_OFFSET (GdkKeymapClass, state_changed),
|
||
NULL, NULL,
|
||
NULL,
|
||
G_TYPE_NONE,
|
||
0);
|
||
}
|
||
|
||
static void
|
||
gdk_keymap_init (GdkKeymap *keymap)
|
||
{
|
||
GdkKeymapKey key;
|
||
|
||
keymap->cached_keys = g_array_new (FALSE, FALSE, sizeof (GdkKeymapKey));
|
||
|
||
key.keycode = 0;
|
||
key.group = 0;
|
||
key.level = 0;
|
||
|
||
g_array_append_val (keymap->cached_keys, key);
|
||
|
||
keymap->cache = g_hash_table_new (g_direct_hash, g_direct_equal);
|
||
}
|
||
|
||
/**
|
||
* gdk_keymap_get_display:
|
||
* @keymap: a #GdkKeymap
|
||
*
|
||
* Retrieves the #GdkDisplay associated to the @keymap.
|
||
*
|
||
* Returns: (transfer none): a #GdkDisplay
|
||
*/
|
||
GdkDisplay *
|
||
gdk_keymap_get_display (GdkKeymap *keymap)
|
||
{
|
||
g_return_val_if_fail (GDK_IS_KEYMAP (keymap), NULL);
|
||
|
||
return keymap->display;
|
||
}
|
||
|
||
/* Other key-handling stuff
|
||
*/
|
||
|
||
/**
|
||
* gdk_keyval_to_upper:
|
||
* @keyval: a key value.
|
||
*
|
||
* Converts a key value to upper case, if applicable.
|
||
*
|
||
* Returns: the upper case form of @keyval, or @keyval itself if it is already
|
||
* in upper case or it is not subject to case conversion.
|
||
*/
|
||
guint
|
||
gdk_keyval_to_upper (guint keyval)
|
||
{
|
||
guint result;
|
||
|
||
gdk_keyval_convert_case (keyval, NULL, &result);
|
||
|
||
return result;
|
||
}
|
||
|
||
/**
|
||
* gdk_keyval_to_lower:
|
||
* @keyval: a key value.
|
||
*
|
||
* Converts a key value to lower case, if applicable.
|
||
*
|
||
* Returns: the lower case form of @keyval, or @keyval itself if it is already
|
||
* in lower case or it is not subject to case conversion.
|
||
*/
|
||
guint
|
||
gdk_keyval_to_lower (guint keyval)
|
||
{
|
||
guint result;
|
||
|
||
gdk_keyval_convert_case (keyval, &result, NULL);
|
||
|
||
return result;
|
||
}
|
||
|
||
/**
|
||
* gdk_keyval_is_upper:
|
||
* @keyval: a key value.
|
||
*
|
||
* Returns %TRUE if the given key value is in upper case.
|
||
*
|
||
* Returns: %TRUE if @keyval is in upper case, or if @keyval is not subject to
|
||
* case conversion.
|
||
*/
|
||
gboolean
|
||
gdk_keyval_is_upper (guint keyval)
|
||
{
|
||
if (keyval)
|
||
{
|
||
guint upper_val = 0;
|
||
|
||
gdk_keyval_convert_case (keyval, NULL, &upper_val);
|
||
return upper_val == keyval;
|
||
}
|
||
return FALSE;
|
||
}
|
||
|
||
/**
|
||
* gdk_keyval_is_lower:
|
||
* @keyval: a key value.
|
||
*
|
||
* Returns %TRUE if the given key value is in lower case.
|
||
*
|
||
* Returns: %TRUE if @keyval is in lower case, or if @keyval is not
|
||
* subject to case conversion.
|
||
*/
|
||
gboolean
|
||
gdk_keyval_is_lower (guint keyval)
|
||
{
|
||
if (keyval)
|
||
{
|
||
guint lower_val = 0;
|
||
|
||
gdk_keyval_convert_case (keyval, &lower_val, NULL);
|
||
return lower_val == keyval;
|
||
}
|
||
return FALSE;
|
||
}
|
||
|
||
/**
|
||
* gdk_keymap_get_direction:
|
||
* @keymap: a #GdkKeymap
|
||
*
|
||
* Returns the direction of effective layout of the keymap.
|
||
* The direction of a layout is the direction of the majority of its
|
||
* symbols. See pango_unichar_direction().
|
||
*
|
||
* Returns: %PANGO_DIRECTION_LTR or %PANGO_DIRECTION_RTL
|
||
* if it can determine the direction. %PANGO_DIRECTION_NEUTRAL
|
||
* otherwise.
|
||
**/
|
||
PangoDirection
|
||
gdk_keymap_get_direction (GdkKeymap *keymap)
|
||
{
|
||
g_return_val_if_fail (GDK_IS_KEYMAP (keymap), PANGO_DIRECTION_LTR);
|
||
|
||
return GDK_KEYMAP_GET_CLASS (keymap)->get_direction (keymap);
|
||
}
|
||
|
||
/**
|
||
* gdk_keymap_have_bidi_layouts:
|
||
* @keymap: a #GdkKeymap
|
||
*
|
||
* Determines if keyboard layouts for both right-to-left and left-to-right
|
||
* languages are in use.
|
||
*
|
||
* Returns: %TRUE if there are layouts in both directions, %FALSE otherwise
|
||
**/
|
||
gboolean
|
||
gdk_keymap_have_bidi_layouts (GdkKeymap *keymap)
|
||
{
|
||
g_return_val_if_fail (GDK_IS_KEYMAP (keymap), FALSE);
|
||
|
||
return GDK_KEYMAP_GET_CLASS (keymap)->have_bidi_layouts (keymap);
|
||
}
|
||
|
||
/**
|
||
* gdk_keymap_get_caps_lock_state:
|
||
* @keymap: a #GdkKeymap
|
||
*
|
||
* Returns whether the Caps Lock modifer is locked.
|
||
*
|
||
* Returns: %TRUE if Caps Lock is on
|
||
*/
|
||
gboolean
|
||
gdk_keymap_get_caps_lock_state (GdkKeymap *keymap)
|
||
{
|
||
g_return_val_if_fail (GDK_IS_KEYMAP (keymap), FALSE);
|
||
|
||
return GDK_KEYMAP_GET_CLASS (keymap)->get_caps_lock_state (keymap);
|
||
}
|
||
|
||
/**
|
||
* gdk_keymap_get_num_lock_state:
|
||
* @keymap: a #GdkKeymap
|
||
*
|
||
* Returns whether the Num Lock modifer is locked.
|
||
*
|
||
* Returns: %TRUE if Num Lock is on
|
||
*/
|
||
gboolean
|
||
gdk_keymap_get_num_lock_state (GdkKeymap *keymap)
|
||
{
|
||
g_return_val_if_fail (GDK_IS_KEYMAP (keymap), FALSE);
|
||
|
||
return GDK_KEYMAP_GET_CLASS (keymap)->get_num_lock_state (keymap);
|
||
}
|
||
|
||
/**
|
||
* gdk_keymap_get_scroll_lock_state:
|
||
* @keymap: a #GdkKeymap
|
||
*
|
||
* Returns whether the Scroll Lock modifer is locked.
|
||
*
|
||
* Returns: %TRUE if Scroll Lock is on
|
||
*/
|
||
gboolean
|
||
gdk_keymap_get_scroll_lock_state (GdkKeymap *keymap)
|
||
{
|
||
g_return_val_if_fail (GDK_IS_KEYMAP (keymap), FALSE);
|
||
|
||
return GDK_KEYMAP_GET_CLASS (keymap)->get_scroll_lock_state (keymap);
|
||
}
|
||
|
||
/**
|
||
* gdk_keymap_get_modifier_state:
|
||
* @keymap: a #GdkKeymap
|
||
*
|
||
* Returns the current modifier state.
|
||
*
|
||
* Returns: the current modifier state.
|
||
*/
|
||
guint
|
||
gdk_keymap_get_modifier_state (GdkKeymap *keymap)
|
||
{
|
||
g_return_val_if_fail (GDK_IS_KEYMAP (keymap), FALSE);
|
||
|
||
if (GDK_KEYMAP_GET_CLASS (keymap)->get_modifier_state)
|
||
return GDK_KEYMAP_GET_CLASS (keymap)->get_modifier_state (keymap);
|
||
|
||
return 0;
|
||
}
|
||
|
||
/**
|
||
* gdk_keymap_get_entries_for_keyval:
|
||
* @keymap: a #GdkKeymap
|
||
* @keyval: a keyval, such as %GDK_KEY_a, %GDK_KEY_Up, %GDK_KEY_Return, etc.
|
||
* @keys: (out) (array length=n_keys) (transfer full): return location
|
||
* for an array of #GdkKeymapKey
|
||
* @n_keys: return location for number of elements in returned array
|
||
*
|
||
* Obtains a list of keycode/group/level combinations that will
|
||
* generate @keyval. Groups and levels are two kinds of keyboard mode;
|
||
* in general, the level determines whether the top or bottom symbol
|
||
* on a key is used, and the group determines whether the left or
|
||
* right symbol is used. On US keyboards, the shift key changes the
|
||
* keyboard level, and there are no groups. A group switch key might
|
||
* convert a keyboard between Hebrew to English modes, for example.
|
||
* #GdkEventKey contains a %group field that indicates the active
|
||
* keyboard group. The level is computed from the modifier mask.
|
||
* The returned array should be freed
|
||
* with g_free().
|
||
*
|
||
* Returns: %TRUE if keys were found and returned
|
||
**/
|
||
gboolean
|
||
gdk_keymap_get_entries_for_keyval (GdkKeymap *keymap,
|
||
guint keyval,
|
||
GdkKeymapKey **keys,
|
||
gint *n_keys)
|
||
{
|
||
GArray *array;
|
||
|
||
g_return_val_if_fail (GDK_IS_KEYMAP (keymap), FALSE);
|
||
g_return_val_if_fail (keys != NULL, FALSE);
|
||
g_return_val_if_fail (n_keys != NULL, FALSE);
|
||
g_return_val_if_fail (keyval != 0, FALSE);
|
||
|
||
array = g_array_new (FALSE, FALSE, sizeof (GdkKeymapKey));
|
||
|
||
GDK_KEYMAP_GET_CLASS (keymap)->get_entries_for_keyval (keymap, keyval, array);
|
||
|
||
*n_keys = array->len;
|
||
*keys = (GdkKeymapKey *)g_array_free (array, FALSE);
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
void
|
||
gdk_keymap_get_cached_entries_for_keyval (GdkKeymap *keymap,
|
||
guint keyval,
|
||
GdkKeymapKey **keys,
|
||
guint *n_keys)
|
||
{
|
||
guint cached;
|
||
guint offset;
|
||
guint len;
|
||
|
||
/* avoid using the first entry in cached_keys, so we can
|
||
* use 0 to mean 'not cached'
|
||
*/
|
||
cached = GPOINTER_TO_UINT (g_hash_table_lookup (keymap->cache, GUINT_TO_POINTER (keyval)));
|
||
if (cached == 0)
|
||
{
|
||
GdkKeymapKey key;
|
||
|
||
offset = keymap->cached_keys->len;
|
||
|
||
GDK_KEYMAP_GET_CLASS (keymap)->get_entries_for_keyval (keymap, keyval, keymap->cached_keys);
|
||
|
||
g_array_append_val (keymap->cached_keys, key);
|
||
|
||
len = keymap->cached_keys->len - offset;
|
||
g_assert (len <= 255);
|
||
|
||
cached = (offset << 8) | len;
|
||
|
||
g_hash_table_insert (keymap->cache, GUINT_TO_POINTER (keyval), GUINT_TO_POINTER (cached));
|
||
}
|
||
else
|
||
{
|
||
len = cached & 255;
|
||
offset = cached >> 8;
|
||
}
|
||
|
||
*n_keys = len;
|
||
*keys = (GdkKeymapKey *)&g_array_index (keymap->cached_keys, GdkKeymapKey, offset);
|
||
}
|
||
|
||
/**
|
||
* gdk_keymap_get_entries_for_keycode:
|
||
* @keymap: a #GdkKeymap
|
||
* @hardware_keycode: a keycode
|
||
* @keys: (out) (array length=n_entries) (transfer full) (optional): return
|
||
* location for array of #GdkKeymapKey, or %NULL
|
||
* @keyvals: (out) (array length=n_entries) (transfer full) (optional): return
|
||
* location for array of keyvals, or %NULL
|
||
* @n_entries: length of @keys and @keyvals
|
||
*
|
||
* Returns the keyvals bound to @hardware_keycode.
|
||
* The Nth #GdkKeymapKey in @keys is bound to the Nth
|
||
* keyval in @keyvals. Free the returned arrays with g_free().
|
||
* When a keycode is pressed by the user, the keyval from
|
||
* this list of entries is selected by considering the effective
|
||
* keyboard group and level. See gdk_keymap_translate_keyboard_state().
|
||
*
|
||
* Returns: %TRUE if there were any entries
|
||
**/
|
||
gboolean
|
||
gdk_keymap_get_entries_for_keycode (GdkKeymap *keymap,
|
||
guint hardware_keycode,
|
||
GdkKeymapKey **keys,
|
||
guint **keyvals,
|
||
gint *n_entries)
|
||
{
|
||
g_return_val_if_fail (GDK_IS_KEYMAP (keymap), FALSE);
|
||
g_return_val_if_fail (n_entries != NULL, FALSE);
|
||
|
||
return GDK_KEYMAP_GET_CLASS (keymap)->get_entries_for_keycode (keymap, hardware_keycode,
|
||
keys, keyvals, n_entries);
|
||
}
|
||
|
||
/**
|
||
* gdk_keymap_lookup_key:
|
||
* @keymap: a #GdkKeymap
|
||
* @key: a #GdkKeymapKey with keycode, group, and level initialized
|
||
*
|
||
* Looks up the keyval mapped to a keycode/group/level triplet.
|
||
* If no keyval is bound to @key, returns 0. For normal user input,
|
||
* you want to use gdk_keymap_translate_keyboard_state() instead of
|
||
* this function, since the effective group/level may not be
|
||
* the same as the current keyboard state.
|
||
*
|
||
* Returns: a keyval, or 0 if none was mapped to the given @key
|
||
**/
|
||
guint
|
||
gdk_keymap_lookup_key (GdkKeymap *keymap,
|
||
const GdkKeymapKey *key)
|
||
{
|
||
g_return_val_if_fail (GDK_IS_KEYMAP (keymap), 0);
|
||
g_return_val_if_fail (key != NULL, 0);
|
||
|
||
return GDK_KEYMAP_GET_CLASS (keymap)->lookup_key (keymap, key);
|
||
}
|
||
|
||
/**
|
||
* gdk_keymap_translate_keyboard_state:
|
||
* @keymap: a #GdkKeymap
|
||
* @hardware_keycode: a keycode
|
||
* @state: a modifier state
|
||
* @group: active keyboard group
|
||
* @keyval: (out) (allow-none): return location for keyval, or %NULL
|
||
* @effective_group: (out) (allow-none): return location for effective
|
||
* group, or %NULL
|
||
* @level: (out) (allow-none): return location for level, or %NULL
|
||
* @consumed_modifiers: (out) (allow-none): return location for modifiers
|
||
* that were used to determine the group or level, or %NULL
|
||
*
|
||
* Translates the contents of a #GdkEventKey into a keyval, effective
|
||
* group, and level. Modifiers that affected the translation and
|
||
* are thus unavailable for application use are returned in
|
||
* @consumed_modifiers.
|
||
* See [Groups][key-group-explanation] for an explanation of
|
||
* groups and levels. The @effective_group is the group that was
|
||
* actually used for the translation; some keys such as Enter are not
|
||
* affected by the active keyboard group. The @level is derived from
|
||
* @state. For convenience, #GdkEventKey already contains the translated
|
||
* keyval, so this function isn’t as useful as you might think.
|
||
*
|
||
* @consumed_modifiers gives modifiers that should be masked outfrom @state
|
||
* when comparing this key press to a hot key. For instance, on a US keyboard,
|
||
* the `plus` symbol is shifted, so when comparing a key press to a
|
||
* `<Control>plus` accelerator `<Shift>` should be masked out.
|
||
*
|
||
* |[<!-- language="C" -->
|
||
* // We want to ignore irrelevant modifiers like ScrollLock
|
||
* #define ALL_ACCELS_MASK (GDK_CONTROL_MASK | GDK_SHIFT_MASK | GDK_ALT_MASK)
|
||
* state = gdk_event_get_modifier_state (event);
|
||
* gdk_keymap_translate_keyboard_state (keymap,
|
||
* gdk_key_event_get_keycode (event),
|
||
* state,
|
||
* gdk_key_event_get_group (event),
|
||
* &keyval, NULL, NULL, &consumed);
|
||
* if (keyval == GDK_PLUS &&
|
||
* (state & ~consumed & ALL_ACCELS_MASK) == GDK_CONTROL_MASK)
|
||
* // Control was pressed
|
||
* ]|
|
||
*
|
||
* An older interpretation @consumed_modifiers was that it contained
|
||
* all modifiers that might affect the translation of the key;
|
||
* this allowed accelerators to be stored with irrelevant consumed
|
||
* modifiers, by doing:
|
||
* |[<!-- language="C" -->
|
||
* // XXX Don’t do this XXX
|
||
* if (keyval == accel_keyval &&
|
||
* (state & ~consumed & ALL_ACCELS_MASK) == (accel_mods & ~consumed))
|
||
* // Accelerator was pressed
|
||
* ]|
|
||
*
|
||
* However, this did not work if multi-modifier combinations were
|
||
* used in the keymap, since, for instance, `<Control>` would be
|
||
* masked out even if only `<Control><Alt>` was used in the keymap.
|
||
* To support this usage as well as well as possible, all single
|
||
* modifier combinations that could affect the key for any combination
|
||
* of modifiers will be returned in @consumed_modifiers; multi-modifier
|
||
* combinations are returned only when actually found in @state. When
|
||
* you store accelerators, you should always store them with consumed
|
||
* modifiers removed. Store `<Control>plus`, not `<Control><Shift>plus`,
|
||
*
|
||
* Returns: %TRUE if there was a keyval bound to the keycode/state/group
|
||
**/
|
||
gboolean
|
||
gdk_keymap_translate_keyboard_state (GdkKeymap *keymap,
|
||
guint hardware_keycode,
|
||
GdkModifierType state,
|
||
gint group,
|
||
guint *keyval,
|
||
gint *effective_group,
|
||
gint *level,
|
||
GdkModifierType *consumed_modifiers)
|
||
{
|
||
g_return_val_if_fail (GDK_IS_KEYMAP (keymap), FALSE);
|
||
|
||
return GDK_KEYMAP_GET_CLASS (keymap)->translate_keyboard_state (keymap,
|
||
hardware_keycode,
|
||
state,
|
||
group,
|
||
keyval,
|
||
effective_group,
|
||
level,
|
||
consumed_modifiers);
|
||
}
|
||
|
||
#include "gdkkeynames.c"
|
||
|
||
/**
|
||
* gdk_keyval_name:
|
||
* @keyval: a key value
|
||
*
|
||
* Converts a key value into a symbolic name.
|
||
*
|
||
* The names are the same as those in the
|
||
* `gdk/gdkkeysyms.h` header file
|
||
* but without the leading “GDK_KEY_”.
|
||
*
|
||
* Returns: (nullable) (transfer none): a string containing the name
|
||
* of the key, or %NULL if @keyval is not a valid key. The string
|
||
* should not be modified.
|
||
*/
|
||
const gchar *
|
||
gdk_keyval_name (guint keyval)
|
||
{
|
||
return _gdk_keyval_name (keyval);
|
||
}
|
||
|
||
/**
|
||
* gdk_keyval_from_name:
|
||
* @keyval_name: a key name
|
||
*
|
||
* Converts a key name to a key value.
|
||
*
|
||
* The names are the same as those in the
|
||
* `gdk/gdkkeysyms.h` header file
|
||
* but without the leading “GDK_KEY_”.
|
||
*
|
||
* Returns: the corresponding key value, or %GDK_KEY_VoidSymbol
|
||
* if the key name is not a valid key
|
||
*/
|
||
guint
|
||
gdk_keyval_from_name (const gchar *keyval_name)
|
||
{
|
||
return _gdk_keyval_from_name (keyval_name);
|
||
}
|
||
|
||
/**
|
||
* gdk_keyval_convert_case:
|
||
* @symbol: a keyval
|
||
* @lower: (out): return location for lowercase version of @symbol
|
||
* @upper: (out): return location for uppercase version of @symbol
|
||
*
|
||
* Obtains the upper- and lower-case versions of the keyval @symbol.
|
||
* Examples of keyvals are #GDK_KEY_a, #GDK_KEY_Enter, #GDK_KEY_F1, etc.
|
||
*/
|
||
void
|
||
gdk_keyval_convert_case (guint symbol,
|
||
guint *lower,
|
||
guint *upper)
|
||
{
|
||
guint xlower, xupper;
|
||
|
||
xlower = symbol;
|
||
xupper = symbol;
|
||
|
||
/* Check for directly encoded 24-bit UCS characters: */
|
||
if ((symbol & 0xff000000) == 0x01000000)
|
||
{
|
||
if (lower)
|
||
*lower = gdk_unicode_to_keyval (g_unichar_tolower (symbol & 0x00ffffff));
|
||
if (upper)
|
||
*upper = gdk_unicode_to_keyval (g_unichar_toupper (symbol & 0x00ffffff));
|
||
return;
|
||
}
|
||
|
||
switch (symbol >> 8)
|
||
{
|
||
case 0: /* Latin 1 */
|
||
if ((symbol >= GDK_KEY_A) && (symbol <= GDK_KEY_Z))
|
||
xlower += (GDK_KEY_a - GDK_KEY_A);
|
||
else if ((symbol >= GDK_KEY_a) && (symbol <= GDK_KEY_z))
|
||
xupper -= (GDK_KEY_a - GDK_KEY_A);
|
||
else if ((symbol >= GDK_KEY_Agrave) && (symbol <= GDK_KEY_Odiaeresis))
|
||
xlower += (GDK_KEY_agrave - GDK_KEY_Agrave);
|
||
else if ((symbol >= GDK_KEY_agrave) && (symbol <= GDK_KEY_odiaeresis))
|
||
xupper -= (GDK_KEY_agrave - GDK_KEY_Agrave);
|
||
else if ((symbol >= GDK_KEY_Ooblique) && (symbol <= GDK_KEY_Thorn))
|
||
xlower += (GDK_KEY_oslash - GDK_KEY_Ooblique);
|
||
else if ((symbol >= GDK_KEY_oslash) && (symbol <= GDK_KEY_thorn))
|
||
xupper -= (GDK_KEY_oslash - GDK_KEY_Ooblique);
|
||
break;
|
||
|
||
case 1: /* Latin 2 */
|
||
/* Assume the KeySym is a legal value (ignore discontinuities) */
|
||
if (symbol == GDK_KEY_Aogonek)
|
||
xlower = GDK_KEY_aogonek;
|
||
else if (symbol >= GDK_KEY_Lstroke && symbol <= GDK_KEY_Sacute)
|
||
xlower += (GDK_KEY_lstroke - GDK_KEY_Lstroke);
|
||
else if (symbol >= GDK_KEY_Scaron && symbol <= GDK_KEY_Zacute)
|
||
xlower += (GDK_KEY_scaron - GDK_KEY_Scaron);
|
||
else if (symbol >= GDK_KEY_Zcaron && symbol <= GDK_KEY_Zabovedot)
|
||
xlower += (GDK_KEY_zcaron - GDK_KEY_Zcaron);
|
||
else if (symbol == GDK_KEY_aogonek)
|
||
xupper = GDK_KEY_Aogonek;
|
||
else if (symbol >= GDK_KEY_lstroke && symbol <= GDK_KEY_sacute)
|
||
xupper -= (GDK_KEY_lstroke - GDK_KEY_Lstroke);
|
||
else if (symbol >= GDK_KEY_scaron && symbol <= GDK_KEY_zacute)
|
||
xupper -= (GDK_KEY_scaron - GDK_KEY_Scaron);
|
||
else if (symbol >= GDK_KEY_zcaron && symbol <= GDK_KEY_zabovedot)
|
||
xupper -= (GDK_KEY_zcaron - GDK_KEY_Zcaron);
|
||
else if (symbol >= GDK_KEY_Racute && symbol <= GDK_KEY_Tcedilla)
|
||
xlower += (GDK_KEY_racute - GDK_KEY_Racute);
|
||
else if (symbol >= GDK_KEY_racute && symbol <= GDK_KEY_tcedilla)
|
||
xupper -= (GDK_KEY_racute - GDK_KEY_Racute);
|
||
break;
|
||
|
||
case 2: /* Latin 3 */
|
||
/* Assume the KeySym is a legal value (ignore discontinuities) */
|
||
if (symbol >= GDK_KEY_Hstroke && symbol <= GDK_KEY_Hcircumflex)
|
||
xlower += (GDK_KEY_hstroke - GDK_KEY_Hstroke);
|
||
else if (symbol >= GDK_KEY_Gbreve && symbol <= GDK_KEY_Jcircumflex)
|
||
xlower += (GDK_KEY_gbreve - GDK_KEY_Gbreve);
|
||
else if (symbol >= GDK_KEY_hstroke && symbol <= GDK_KEY_hcircumflex)
|
||
xupper -= (GDK_KEY_hstroke - GDK_KEY_Hstroke);
|
||
else if (symbol >= GDK_KEY_gbreve && symbol <= GDK_KEY_jcircumflex)
|
||
xupper -= (GDK_KEY_gbreve - GDK_KEY_Gbreve);
|
||
else if (symbol >= GDK_KEY_Cabovedot && symbol <= GDK_KEY_Scircumflex)
|
||
xlower += (GDK_KEY_cabovedot - GDK_KEY_Cabovedot);
|
||
else if (symbol >= GDK_KEY_cabovedot && symbol <= GDK_KEY_scircumflex)
|
||
xupper -= (GDK_KEY_cabovedot - GDK_KEY_Cabovedot);
|
||
break;
|
||
|
||
case 3: /* Latin 4 */
|
||
/* Assume the KeySym is a legal value (ignore discontinuities) */
|
||
if (symbol >= GDK_KEY_Rcedilla && symbol <= GDK_KEY_Tslash)
|
||
xlower += (GDK_KEY_rcedilla - GDK_KEY_Rcedilla);
|
||
else if (symbol >= GDK_KEY_rcedilla && symbol <= GDK_KEY_tslash)
|
||
xupper -= (GDK_KEY_rcedilla - GDK_KEY_Rcedilla);
|
||
else if (symbol == GDK_KEY_ENG)
|
||
xlower = GDK_KEY_eng;
|
||
else if (symbol == GDK_KEY_eng)
|
||
xupper = GDK_KEY_ENG;
|
||
else if (symbol >= GDK_KEY_Amacron && symbol <= GDK_KEY_Umacron)
|
||
xlower += (GDK_KEY_amacron - GDK_KEY_Amacron);
|
||
else if (symbol >= GDK_KEY_amacron && symbol <= GDK_KEY_umacron)
|
||
xupper -= (GDK_KEY_amacron - GDK_KEY_Amacron);
|
||
break;
|
||
|
||
case 6: /* Cyrillic */
|
||
/* Assume the KeySym is a legal value (ignore discontinuities) */
|
||
if (symbol >= GDK_KEY_Serbian_DJE && symbol <= GDK_KEY_Serbian_DZE)
|
||
xlower -= (GDK_KEY_Serbian_DJE - GDK_KEY_Serbian_dje);
|
||
else if (symbol >= GDK_KEY_Serbian_dje && symbol <= GDK_KEY_Serbian_dze)
|
||
xupper += (GDK_KEY_Serbian_DJE - GDK_KEY_Serbian_dje);
|
||
else if (symbol >= GDK_KEY_Cyrillic_YU && symbol <= GDK_KEY_Cyrillic_HARDSIGN)
|
||
xlower -= (GDK_KEY_Cyrillic_YU - GDK_KEY_Cyrillic_yu);
|
||
else if (symbol >= GDK_KEY_Cyrillic_yu && symbol <= GDK_KEY_Cyrillic_hardsign)
|
||
xupper += (GDK_KEY_Cyrillic_YU - GDK_KEY_Cyrillic_yu);
|
||
break;
|
||
|
||
case 7: /* Greek */
|
||
/* Assume the KeySym is a legal value (ignore discontinuities) */
|
||
if (symbol >= GDK_KEY_Greek_ALPHAaccent && symbol <= GDK_KEY_Greek_OMEGAaccent)
|
||
xlower += (GDK_KEY_Greek_alphaaccent - GDK_KEY_Greek_ALPHAaccent);
|
||
else if (symbol >= GDK_KEY_Greek_alphaaccent && symbol <= GDK_KEY_Greek_omegaaccent &&
|
||
symbol != GDK_KEY_Greek_iotaaccentdieresis &&
|
||
symbol != GDK_KEY_Greek_upsilonaccentdieresis)
|
||
xupper -= (GDK_KEY_Greek_alphaaccent - GDK_KEY_Greek_ALPHAaccent);
|
||
else if (symbol >= GDK_KEY_Greek_ALPHA && symbol <= GDK_KEY_Greek_OMEGA)
|
||
xlower += (GDK_KEY_Greek_alpha - GDK_KEY_Greek_ALPHA);
|
||
else if (symbol == GDK_KEY_Greek_finalsmallsigma)
|
||
xupper = GDK_KEY_Greek_SIGMA;
|
||
else if (symbol >= GDK_KEY_Greek_alpha && symbol <= GDK_KEY_Greek_omega)
|
||
xupper -= (GDK_KEY_Greek_alpha - GDK_KEY_Greek_ALPHA);
|
||
break;
|
||
|
||
default:
|
||
break;
|
||
}
|
||
|
||
if (lower)
|
||
*lower = xlower;
|
||
if (upper)
|
||
*upper = xupper;
|
||
}
|