state: apply control transformation on utf8/utf32 keysym strings
This is required by the specification: http://www.x.org/releases/current/doc/kbproto/xkbproto.html#Interpreting_the_Control_Modifier and clients expect this to happen. https://bugs.freedesktop.org/show_bug.cgi?id=75892 Reported-by: Gatis Paeglis <gatis.paeglis@digia.com> Signed-off-by: Ran Benita <ran234@gmail.com>master
parent
b973d71e82
commit
3cfa7fdac8
121
src/state.c
121
src/state.c
|
@ -842,6 +842,53 @@ err:
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* http://www.x.org/releases/current/doc/kbproto/xkbproto.html#Interpreting_the_Lock_Modifier
|
||||
*/
|
||||
static bool
|
||||
should_do_caps_transformation(struct xkb_state *state, xkb_keycode_t kc)
|
||||
{
|
||||
xkb_mod_index_t caps =
|
||||
xkb_keymap_mod_get_index(state->keymap, XKB_MOD_NAME_CAPS);
|
||||
|
||||
return
|
||||
xkb_state_mod_index_is_active(state, caps, XKB_STATE_MODS_EFFECTIVE) > 0 &&
|
||||
xkb_state_mod_index_is_consumed(state, kc, caps) == 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* http://www.x.org/releases/current/doc/kbproto/xkbproto.html#Interpreting_the_Control_Modifier
|
||||
*/
|
||||
static bool
|
||||
should_do_ctrl_transformation(struct xkb_state *state, xkb_keycode_t kc)
|
||||
{
|
||||
xkb_mod_index_t ctrl =
|
||||
xkb_keymap_mod_get_index(state->keymap, XKB_MOD_NAME_CTRL);
|
||||
|
||||
return
|
||||
xkb_state_mod_index_is_active(state, ctrl, XKB_STATE_MODS_EFFECTIVE) > 0 &&
|
||||
xkb_state_mod_index_is_consumed(state, kc, ctrl) == 0;
|
||||
}
|
||||
|
||||
/* Verbatim from libX11:src/xkb/XKBBind.c */
|
||||
static char
|
||||
XkbToControl(char ch)
|
||||
{
|
||||
char c = ch;
|
||||
|
||||
if ((c >= '@' && c < '\177') || c == ' ')
|
||||
c &= 0x1F;
|
||||
else if (c == '2')
|
||||
c = '\000';
|
||||
else if (c >= '3' && c <= '7')
|
||||
c -= ('3' - '\033');
|
||||
else if (c == '8')
|
||||
c = '\177';
|
||||
else if (c == '/')
|
||||
c = '_' & 0x1F;
|
||||
return c;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides either exactly one symbol, or XKB_KEY_NoSymbol.
|
||||
*/
|
||||
|
@ -851,7 +898,6 @@ 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)
|
||||
|
@ -859,18 +905,65 @@ xkb_state_key_get_one_sym(struct xkb_state *state, xkb_keycode_t kc)
|
|||
|
||||
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)
|
||||
if (should_do_caps_transformation(state, kc))
|
||||
sym = xkb_keysym_to_upper(sym);
|
||||
|
||||
return sym;
|
||||
}
|
||||
|
||||
/*
|
||||
* The caps and ctrl transformations require some special handling,
|
||||
* so we cannot simply use xkb_state_get_one_sym() for them.
|
||||
* In particular, if Control is set, we must try very hard to find
|
||||
* some layout in which the keysym is ASCII and thus can be (maybe)
|
||||
* converted to a control character. libX11 allows to disable this
|
||||
* behavior with the XkbLC_ControlFallback (see XkbSetXlibControls(3)),
|
||||
* but it is enabled by default, yippee.
|
||||
*/
|
||||
static xkb_keysym_t
|
||||
get_one_sym_for_string(struct xkb_state *state, xkb_keycode_t kc)
|
||||
{
|
||||
xkb_level_index_t level;
|
||||
xkb_layout_index_t layout, num_layouts;
|
||||
const xkb_keysym_t *syms;
|
||||
int nsyms;
|
||||
xkb_keysym_t sym;
|
||||
|
||||
layout = xkb_state_key_get_layout(state, kc);
|
||||
num_layouts = xkb_keymap_num_layouts_for_key(state->keymap, kc);
|
||||
level = xkb_state_key_get_level(state, kc, layout);
|
||||
if (layout == XKB_LAYOUT_INVALID || num_layouts == 0 ||
|
||||
level == XKB_LEVEL_INVALID)
|
||||
return XKB_KEY_NoSymbol;
|
||||
|
||||
nsyms = xkb_keymap_key_get_syms_by_level(state->keymap, kc,
|
||||
layout, level, &syms);
|
||||
if (nsyms != 1)
|
||||
return XKB_KEY_NoSymbol;
|
||||
sym = syms[0];
|
||||
|
||||
if (should_do_ctrl_transformation(state, kc) && sym > 127u) {
|
||||
for (xkb_layout_index_t i = 0; i < num_layouts; i++) {
|
||||
level = xkb_state_key_get_level(state, kc, i);
|
||||
if (level == XKB_LEVEL_INVALID)
|
||||
continue;
|
||||
|
||||
nsyms = xkb_keymap_key_get_syms_by_level(state->keymap, kc,
|
||||
i, level, &syms);
|
||||
if (nsyms == 1 && syms[0] <= 127u) {
|
||||
sym = syms[0];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (should_do_caps_transformation(state, kc)) {
|
||||
sym = xkb_keysym_to_upper(sym);
|
||||
}
|
||||
|
||||
return sym;
|
||||
}
|
||||
|
||||
XKB_EXPORT int
|
||||
xkb_state_key_get_utf8(struct xkb_state *state, xkb_keycode_t kc,
|
||||
char *buffer, size_t size)
|
||||
|
@ -881,8 +974,7 @@ xkb_state_key_get_utf8(struct xkb_state *state, xkb_keycode_t kc,
|
|||
int offset;
|
||||
char tmp[7];
|
||||
|
||||
/* Make sure the keysym transformations are applied. */
|
||||
sym = xkb_state_key_get_one_sym(state, kc);
|
||||
sym = get_one_sym_for_string(state, kc);
|
||||
if (sym != XKB_KEY_NoSymbol) {
|
||||
nsyms = 1; syms = &sym;
|
||||
}
|
||||
|
@ -910,6 +1002,10 @@ xkb_state_key_get_utf8(struct xkb_state *state, xkb_keycode_t kc,
|
|||
if (!is_valid_utf8(buffer, offset))
|
||||
goto err_bad;
|
||||
|
||||
if (offset == 1 && (unsigned int) buffer[0] <= 127u &&
|
||||
should_do_ctrl_transformation(state, kc))
|
||||
buffer[0] = XkbToControl(buffer[0]);
|
||||
|
||||
return offset;
|
||||
|
||||
err_trunc:
|
||||
|
@ -929,9 +1025,12 @@ xkb_state_key_get_utf32(struct xkb_state *state, xkb_keycode_t kc)
|
|||
xkb_keysym_t sym;
|
||||
uint32_t cp;
|
||||
|
||||
sym = xkb_state_key_get_one_sym(state, kc);
|
||||
sym = get_one_sym_for_string(state, kc);
|
||||
cp = xkb_keysym_to_utf32(sym);
|
||||
|
||||
if (cp <= 127u && should_do_ctrl_transformation(state, kc))
|
||||
cp = (uint32_t) XkbToControl((char) cp);
|
||||
|
||||
return cp;
|
||||
}
|
||||
|
||||
|
|
47
test/state.c
47
test/state.c
|
@ -503,6 +503,52 @@ test_get_utf8_utf32(struct xkb_keymap *keymap)
|
|||
xkb_state_unref(state);
|
||||
}
|
||||
|
||||
static void
|
||||
test_ctrl_string_transformation(struct xkb_keymap *keymap)
|
||||
{
|
||||
char buf[256];
|
||||
struct xkb_state *state = xkb_state_new(keymap);
|
||||
xkb_mod_index_t ctrl;
|
||||
|
||||
assert(state);
|
||||
|
||||
/* See xkb_state_key_get_utf8() for what's this all about. */
|
||||
|
||||
ctrl = xkb_keymap_mod_get_index(keymap, XKB_MOD_NAME_CTRL);
|
||||
assert(ctrl != XKB_MOD_INVALID);
|
||||
|
||||
/* First without. */
|
||||
TEST_KEY(KEY_A, "a", 0x61);
|
||||
TEST_KEY(KEY_B, "b", 0x62);
|
||||
TEST_KEY(KEY_C, "c", 0x63);
|
||||
TEST_KEY(KEY_ESC, "\x1B", 0x1B);
|
||||
TEST_KEY(KEY_1, "1", 0x31);
|
||||
|
||||
/* And with. */
|
||||
xkb_state_update_key(state, KEY_RIGHTCTRL + EVDEV_OFFSET, XKB_KEY_DOWN);
|
||||
assert(xkb_state_mod_index_is_active(state, ctrl, XKB_STATE_MODS_EFFECTIVE) > 0);
|
||||
TEST_KEY(KEY_A, "\x01", 0x01);
|
||||
TEST_KEY(KEY_B, "\x02", 0x02);
|
||||
TEST_KEY(KEY_C, "\x03", 0x03);
|
||||
TEST_KEY(KEY_ESC, "\x1B", 0x1B);
|
||||
TEST_KEY(KEY_1, "1", 0x31);
|
||||
xkb_state_update_key(state, KEY_RIGHTCTRL + EVDEV_OFFSET, XKB_KEY_UP);
|
||||
|
||||
/* Switch to ru layout */
|
||||
xkb_state_update_key(state, KEY_COMPOSE + EVDEV_OFFSET, XKB_KEY_DOWN);
|
||||
xkb_state_update_key(state, KEY_COMPOSE + EVDEV_OFFSET, XKB_KEY_UP);
|
||||
assert(xkb_state_key_get_layout(state, KEY_A + 8) == 1);
|
||||
|
||||
/* Non ASCII. */
|
||||
xkb_state_update_key(state, KEY_RIGHTCTRL + EVDEV_OFFSET, XKB_KEY_DOWN);
|
||||
assert(xkb_state_mod_index_is_active(state, ctrl, XKB_STATE_MODS_EFFECTIVE) > 0);
|
||||
TEST_KEY(KEY_A, "\x01", 0x01);
|
||||
TEST_KEY(KEY_B, "\x02", 0x02);
|
||||
xkb_state_update_key(state, KEY_RIGHTCTRL + EVDEV_OFFSET, XKB_KEY_UP);
|
||||
|
||||
xkb_state_unref(state);
|
||||
}
|
||||
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
|
@ -525,6 +571,7 @@ main(void)
|
|||
test_consume(keymap);
|
||||
test_range(keymap);
|
||||
test_get_utf8_utf32(keymap);
|
||||
test_ctrl_string_transformation(keymap);
|
||||
|
||||
xkb_keymap_unref(keymap);
|
||||
keymap = test_compile_rules(context, "evdev", NULL, "ch", "fr", NULL);
|
||||
|
|
Loading…
Reference in New Issue