diff --git a/src/state.c b/src/state.c index bfcf1d4..e13d1fd 100644 --- a/src/state.c +++ b/src/state.c @@ -60,6 +60,7 @@ */ #include "keymap.h" +#include "keysym.h" struct xkb_filter { 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) { const xkb_keysym_t *syms; + xkb_keysym_t sym; int num_syms; + xkb_mod_index_t caps; num_syms = xkb_state_key_get_syms(state, kc, &syms); if (num_syms != 1) 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; } /** diff --git a/test/state.c b/test/state.c index 3521d66..d4e5aba 100644 --- a/test/state.c +++ b/test/state.c @@ -353,6 +353,71 @@ test_range(struct xkb_keymap *keymap) 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 main(void) { @@ -375,6 +440,12 @@ main(void) test_consume(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_context_unref(context); } diff --git a/xkbcommon/xkbcommon.h b/xkbcommon/xkbcommon.h index de64dbe..e5c8f05 100644 --- a/xkbcommon/xkbcommon.h +++ b/xkbcommon/xkbcommon.h @@ -1205,8 +1205,9 @@ xkb_state_update_mask(struct xkb_state *state, * key in the given keyboard state. * * 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 - * xkb_state_key_get_one_sym(). + * If you do not want to handle this case, you should use + * 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 * 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 * given keyboard state. * - * This function is similar to xkb_state_key_get_syms(), but with a - * simplified interface for users which cannot or do not want to handle - * the case where multiple keysyms are returned. + * This function is similar to xkb_state_key_get_syms(), but intended + * for users which cannot or do not want to handle the case where + * multiple keysyms are returned (in which case this function is + * preferred). * * @returns The keysym. If the key does not have exactly one keysym, * returns XKB_KEY_NoSymbol