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.
*/
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)
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;
}
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;
}
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:
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;
}
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)
{
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);