state: apply capitalization transformation on keysyms

The xkbproto spec says:
http://www.x.org/releases/current/doc/kbproto/xkbproto.html#Interpreting_the_Lock_Modifier

    If the Lock modifier is not consumed by the symbol lookup process,
    routines that determine the symbol and string that correspond to
    an event should capitalize the result.

This was not an issue until now, because most xkeyboard-config keymaps
do not utilize this "feature", and specify the keysyms for the Lock
modifier explicitly instead. However, some keymaps do depend on it, e.g.
ch(fr) for eacute and others.

The spec goes on to describe two options for doing this transformation:
locale-sensitive and locale-insensitive. We opt for the latter; it is
less desirable but we don't want *that* headache.

Also, only xkb_state_key_get_one_sym() is changed;
xkb_state_key_get_syms() is left as-is, and always reports the
untransformed keysyms. This is for the following reasons:

- The API doesn't allow it, since we return a const pointer directly to
  the keymap keysyms table and we can't transform that.

- The transformation doesn't make sense for multiple-keysyms.

- It can be useful for an application to get the "raw" keysyms if it
  wants to (e.g. maybe it wants to do the transformation itself).

Finally, note that xkb_state_mod_index_is_consumed() does *not*
report Lock as consumed even if it was used in the transformation. This
is what Xlib does.

This definitely doesn't fall under the "hard to misuse" API rule but
it's the best we can do.

https://bugs.freedesktop.org/show_bug.cgi?id=67167

Reported-By: Gatis Paeglis <gatis.paeglis@digia.com>
Signed-off-by: Ran Benita <ran234@gmail.com>
master
Ran Benita 2013-08-13 18:57:43 +03:00
parent 9e92319db4
commit 2a2a8d7da1
3 changed files with 93 additions and 6 deletions

View File

@ -60,6 +60,7 @@
*/ */
#include "keymap.h" #include "keymap.h"
#include "keysym.h"
struct xkb_filter { struct xkb_filter {
union xkb_action action; union xkb_action action;
@ -832,13 +833,26 @@ XKB_EXPORT xkb_keysym_t
xkb_state_key_get_one_sym(struct xkb_state *state, xkb_keycode_t kc) xkb_state_key_get_one_sym(struct xkb_state *state, xkb_keycode_t kc)
{ {
const xkb_keysym_t *syms; const xkb_keysym_t *syms;
xkb_keysym_t sym;
int num_syms; int num_syms;
xkb_mod_index_t caps;
num_syms = xkb_state_key_get_syms(state, kc, &syms); num_syms = xkb_state_key_get_syms(state, kc, &syms);
if (num_syms != 1) if (num_syms != 1)
return XKB_KEY_NoSymbol; return XKB_KEY_NoSymbol;
return syms[0]; sym = syms[0];
/*
* Perform capitalization transformation, see:
* http://www.x.org/releases/current/doc/kbproto/xkbproto.html#Interpreting_the_Lock_Modifier
*/
caps = xkb_keymap_mod_get_index(state->keymap, XKB_MOD_NAME_CAPS);
if (xkb_state_mod_index_is_active(state, caps, XKB_STATE_MODS_EFFECTIVE) > 0 &&
xkb_state_mod_index_is_consumed(state, kc, caps) == 0)
sym = xkb_keysym_to_upper(sym);
return sym;
} }
/** /**

View File

@ -353,6 +353,71 @@ test_range(struct xkb_keymap *keymap)
assert(counter == xkb_keymap_max_keycode(keymap) + 1); assert(counter == xkb_keymap_max_keycode(keymap) + 1);
} }
static void
test_caps_keysym_transformation(struct xkb_keymap *keymap)
{
struct xkb_state *state = xkb_state_new(keymap);
xkb_mod_index_t caps, shift;
int nsyms;
xkb_keysym_t sym;
const xkb_keysym_t *syms;
assert(state);
/* See xkb_state_key_get_one_sym() for what's this all about. */
caps = xkb_keymap_mod_get_index(keymap, XKB_MOD_NAME_CAPS);
shift = xkb_keymap_mod_get_index(keymap, XKB_MOD_NAME_SHIFT);
assert(caps >= 0 && shift >= 0);
assert(xkb_state_key_get_layout(state, KEY_A + 8) == 0);
assert(xkb_state_key_get_layout(state, KEY_SEMICOLON + 8) == 0);
/* Without caps, no transformation. */
assert(xkb_state_mod_index_is_active(state, caps, XKB_STATE_MODS_EFFECTIVE) == 0);
assert(xkb_state_mod_index_is_active(state, shift, XKB_STATE_MODS_EFFECTIVE) == 0);
assert(xkb_state_key_get_level(state, KEY_A + 8, 0) == 0);
sym = xkb_state_key_get_one_sym(state, KEY_A + 8);
assert(sym == XKB_KEY_a);
assert(xkb_state_key_get_level(state, KEY_SEMICOLON + 8, 0) == 0);
sym = xkb_state_key_get_one_sym(state, KEY_SEMICOLON + 8);
assert(sym == XKB_KEY_eacute);
nsyms = xkb_state_key_get_syms(state, KEY_SEMICOLON + 8, &syms);
assert(nsyms == 1 && syms[0] == XKB_KEY_eacute);
/* With shift, no transformation (only different level). */
xkb_state_update_key(state, KEY_LEFTSHIFT + 8, XKB_KEY_DOWN);
assert(xkb_state_mod_index_is_active(state, caps, XKB_STATE_MODS_EFFECTIVE) == 0);
assert(xkb_state_mod_index_is_active(state, shift, XKB_STATE_MODS_EFFECTIVE) > 0);
assert(xkb_state_key_get_level(state, KEY_A + 8, 0) == 1);
sym = xkb_state_key_get_one_sym(state, KEY_A + 8);
assert(sym == XKB_KEY_A);
sym = xkb_state_key_get_one_sym(state, KEY_SEMICOLON + 8);
assert(sym == XKB_KEY_odiaeresis);
nsyms = xkb_state_key_get_syms(state, KEY_SEMICOLON + 8, &syms);
assert(nsyms == 1 && syms[0] == XKB_KEY_odiaeresis);
xkb_state_update_key(state, KEY_LEFTSHIFT + 8, XKB_KEY_UP);
assert(xkb_state_mod_index_is_active(state, shift, XKB_STATE_MODS_EFFECTIVE) == 0);
/* With caps, transform in same level, only with _get_one_sym(). */
xkb_state_update_key(state, KEY_CAPSLOCK + 8, XKB_KEY_DOWN);
xkb_state_update_key(state, KEY_CAPSLOCK + 8, XKB_KEY_UP);
assert(xkb_state_mod_index_is_active(state, caps, XKB_STATE_MODS_EFFECTIVE) > 0);
assert(xkb_state_mod_index_is_active(state, shift, XKB_STATE_MODS_EFFECTIVE) == 0);
assert(xkb_state_key_get_level(state, KEY_A + 8, 0) == 1);
sym = xkb_state_key_get_one_sym(state, KEY_A + 8);
assert(sym == XKB_KEY_A);
assert(xkb_state_key_get_level(state, KEY_SEMICOLON + 8, 0) == 0);
sym = xkb_state_key_get_one_sym(state, KEY_SEMICOLON + 8);
assert(sym == XKB_KEY_Eacute);
nsyms = xkb_state_key_get_syms(state, KEY_SEMICOLON + 8, &syms);
assert(nsyms == 1 && syms[0] == XKB_KEY_eacute);
xkb_state_update_key(state, KEY_LEFTSHIFT + 8, XKB_KEY_UP);
assert(xkb_state_mod_index_is_active(state, shift, XKB_STATE_MODS_EFFECTIVE) == 0);
xkb_state_update_key(state, KEY_CAPSLOCK + 8, XKB_KEY_DOWN);
xkb_state_update_key(state, KEY_CAPSLOCK + 8, XKB_KEY_UP);
}
int int
main(void) main(void)
{ {
@ -375,6 +440,12 @@ main(void)
test_consume(keymap); test_consume(keymap);
test_range(keymap); test_range(keymap);
xkb_keymap_unref(keymap);
keymap = test_compile_rules(context, "evdev", NULL, "ch", "fr", NULL);
assert(keymap);
test_caps_keysym_transformation(keymap);
xkb_keymap_unref(keymap); xkb_keymap_unref(keymap);
xkb_context_unref(context); xkb_context_unref(context);
} }

View File

@ -1205,8 +1205,9 @@ xkb_state_update_mask(struct xkb_state *state,
* key in the given keyboard state. * key in the given keyboard state.
* *
* As an extension to XKB, this function can return more than one keysym. * As an extension to XKB, this function can return more than one keysym.
* If you do not want to handle this case, you can use * If you do not want to handle this case, you should use
* xkb_state_key_get_one_sym(). * xkb_state_key_get_one_sym(), which additionally performs transformations
* which are specific to the one-keysym case.
* *
* @returns The number of keysyms in the syms_out array. If no keysyms * @returns The number of keysyms in the syms_out array. If no keysyms
* are produced by the key in the given keyboard state, returns 0 and sets * are produced by the key in the given keyboard state, returns 0 and sets
@ -1222,9 +1223,10 @@ xkb_state_key_get_syms(struct xkb_state *state, xkb_keycode_t key,
* Get the single keysym obtained from pressing a particular key in a * Get the single keysym obtained from pressing a particular key in a
* given keyboard state. * given keyboard state.
* *
* This function is similar to xkb_state_key_get_syms(), but with a * This function is similar to xkb_state_key_get_syms(), but intended
* simplified interface for users which cannot or do not want to handle * for users which cannot or do not want to handle the case where
* the case where multiple keysyms are returned. * multiple keysyms are returned (in which case this function is
* preferred).
* *
* @returns The keysym. If the key does not have exactly one keysym, * @returns The keysym. If the key does not have exactly one keysym,
* returns XKB_KEY_NoSymbol * returns XKB_KEY_NoSymbol