summaryrefslogtreecommitdiff
path: root/src/state.c
diff options
context:
space:
mode:
authorRan Benita <ran234@gmail.com>2014-03-21 23:00:37 +0200
committerRan Benita <ran234@gmail.com>2014-03-22 20:07:30 +0200
commit3cfa7fdac829b9f51aa70ae38f38295265edb2d1 (patch)
tree7c7a56ae8ff8c73b199dd57a20ed2f21a51c7d76 /src/state.c
parentb973d71e82e9ec042d604226ccf0ba641773f38f (diff)
downloadxorg-lib-libxkbcommon-3cfa7fdac829b9f51aa70ae38f38295265edb2d1.tar.gz
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>
Diffstat (limited to 'src/state.c')
-rw-r--r--src/state.c121
1 files changed, 110 insertions, 11 deletions
diff --git a/src/state.c b/src/state.c
index ebd0ca6..d130b8e 100644
--- a/src/state.c
+++ b/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,14 +905,61 @@ 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;
}
@@ -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;
}