/* * Copyright © 2013 Ran Benita * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice (including the next * paragraph) shall be included in all copies or substantial portions of the * Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #include "x11-priv.h" /* * References for the lonesome traveler: * Xkb protocol specification: * http://www.x.org/releases/current/doc/kbproto/xkbproto.html * The XCB xkb XML protocol file: * /user/share/xcb/xkb.xml * The XCB xkb header file: * /usr/include/xcb/xkb.h * The old kbproto header files: * /usr/include/X11/extensions/XKB{,proto,str}.h * Xlib XKB source code: * /src/xkb/XKBGetMap.c (and friends) * X server XKB protocol handling: * /xkb/xkb.c * Man pages: * XkbGetMap(3), XkbGetCompatMap(3), etc. */ /* Constants from /usr/include/X11/extensions/XKB.h */ /* XkbNumModifiers. */ #define NUM_REAL_MODS 8u /* XkbNumVirtualMods. */ #define NUM_VMODS 16u /* XkbNoModifier. */ #define NO_MODIFIER 0xff /* XkbNumIndicators. */ #define NUM_INDICATORS 32u /* XkbAllIndicatorsMask. */ #define ALL_INDICATORS_MASK 0xffffffff /* Some macros. Not very nice but it'd be worse without them. */ /* * We try not to trust the server too much and be paranoid. If we get * something which we definitely shouldn't, we fail. */ #define STRINGIFY(expr) #expr #define FAIL_UNLESS(expr) do { \ if (!(expr)) { \ log_err(keymap->ctx, \ "x11: failed to get keymap from X server: unmet condition in %s(): %s\n", \ __func__, STRINGIFY(expr)); \ goto fail; \ } \ } while (0) #define FAIL_IF_BAD_REPLY(reply, request_name) do { \ if (!reply) { \ log_err(keymap->ctx, \ "x11: failed to get keymap from X server: %s request failed\n", \ (request_name)); \ goto fail; \ } \ } while (0) #define ALLOC_OR_FAIL(arr, nmemb) do { \ if ((nmemb) > 0) { \ (arr) = calloc((nmemb), sizeof(*(arr))); \ if (!(arr)) \ goto fail; \ } \ } while (0) static xkb_mod_mask_t translate_mods(uint8_t rmods, uint16_t vmods_low, uint16_t vmods_high) { /* We represent mod masks in a single uint32_t value, with real mods * first and vmods after (though we don't make these distinctions). */ return ((xkb_mod_mask_t) rmods) | ((xkb_mod_mask_t) vmods_low << 8) | ((xkb_mod_mask_t) vmods_high << 16); } static enum xkb_action_controls translate_controls_mask(uint32_t wire) { enum xkb_action_controls ret = 0; if (wire & XCB_XKB_BOOL_CTRL_REPEAT_KEYS) ret |= CONTROL_REPEAT; if (wire & XCB_XKB_BOOL_CTRL_SLOW_KEYS) ret |= CONTROL_SLOW; if (wire & XCB_XKB_BOOL_CTRL_BOUNCE_KEYS) ret |= CONTROL_DEBOUNCE; if (wire & XCB_XKB_BOOL_CTRL_STICKY_KEYS) ret |= CONTROL_STICKY; if (wire & XCB_XKB_BOOL_CTRL_MOUSE_KEYS) ret |= CONTROL_MOUSEKEYS; if (wire & XCB_XKB_BOOL_CTRL_MOUSE_KEYS_ACCEL) ret |= CONTROL_MOUSEKEYS_ACCEL; if (wire & XCB_XKB_BOOL_CTRL_ACCESS_X_KEYS) ret |= CONTROL_AX; if (wire & XCB_XKB_BOOL_CTRL_ACCESS_X_TIMEOUT_MASK) ret |= CONTROL_AX_TIMEOUT; if (wire & XCB_XKB_BOOL_CTRL_ACCESS_X_FEEDBACK_MASK) ret |= CONTROL_AX_FEEDBACK; if (wire & XCB_XKB_BOOL_CTRL_AUDIBLE_BELL_MASK) ret |= CONTROL_BELL; if (wire & XCB_XKB_BOOL_CTRL_IGNORE_GROUP_LOCK_MASK) ret |= CONTROL_IGNORE_GROUP_LOCK; /* Some controls are not supported and don't appear here. */ return ret; } static void translate_action(union xkb_action *action, const xcb_xkb_action_t *wire) { switch (wire->type) { case XCB_XKB_SA_TYPE_SET_MODS: action->type = ACTION_TYPE_MOD_SET; action->mods.mods.mods = translate_mods(wire->setmods.realMods, wire->setmods.vmodsLow, wire->setmods.vmodsHigh); action->mods.mods.mask = translate_mods(wire->setmods.mask, 0, 0); if (wire->setmods.flags & XCB_XKB_SA_CLEAR_LOCKS) action->mods.flags |= ACTION_LOCK_CLEAR; if (wire->setmods.flags & XCB_XKB_SA_LATCH_TO_LOCK) action->mods.flags |= ACTION_LATCH_TO_LOCK; if (wire->setmods.flags & XCB_XKB_SA_USE_MOD_MAP_MODS) action->mods.flags |= ACTION_MODS_LOOKUP_MODMAP; break; case XCB_XKB_SA_TYPE_LATCH_MODS: action->type = ACTION_TYPE_MOD_LATCH; action->mods.mods.mods = translate_mods(wire->latchmods.realMods, wire->latchmods.vmodsLow, wire->latchmods.vmodsHigh); action->mods.mods.mask = translate_mods(wire->latchmods.mask, 0, 0); if (wire->latchmods.flags & XCB_XKB_SA_CLEAR_LOCKS) action->mods.flags |= ACTION_LOCK_CLEAR; if (wire->latchmods.flags & XCB_XKB_SA_LATCH_TO_LOCK) action->mods.flags |= ACTION_LATCH_TO_LOCK; if (wire->latchmods.flags & XCB_XKB_SA_USE_MOD_MAP_MODS) action->mods.flags |= ACTION_MODS_LOOKUP_MODMAP; break; case XCB_XKB_SA_TYPE_LOCK_MODS: action->type = ACTION_TYPE_MOD_LOCK; action->mods.mods.mods = translate_mods(wire->lockmods.realMods, wire->lockmods.vmodsLow, wire->lockmods.vmodsHigh); action->mods.mods.mask = translate_mods(wire->lockmods.mask, 0, 0); if (wire->lockmods.flags & XCB_XKB_SA_ISO_LOCK_FLAG_NO_LOCK) action->mods.flags |= ACTION_LOCK_NO_LOCK; if (wire->lockmods.flags & XCB_XKB_SA_ISO_LOCK_FLAG_NO_UNLOCK) action->mods.flags |= ACTION_LOCK_NO_UNLOCK; if (wire->lockmods.flags & XCB_XKB_SA_USE_MOD_MAP_MODS) action->mods.flags |= ACTION_MODS_LOOKUP_MODMAP; break; case XCB_XKB_SA_TYPE_SET_GROUP: action->type = ACTION_TYPE_GROUP_SET; action->group.group = wire->setgroup.group; if (wire->setmods.flags & XCB_XKB_SA_CLEAR_LOCKS) action->group.flags |= ACTION_LOCK_CLEAR; if (wire->setmods.flags & XCB_XKB_SA_LATCH_TO_LOCK) action->group.flags |= ACTION_LATCH_TO_LOCK; if (wire->setmods.flags & XCB_XKB_SA_ISO_LOCK_FLAG_GROUP_ABSOLUTE) action->group.flags |= ACTION_ABSOLUTE_SWITCH; break; case XCB_XKB_SA_TYPE_LATCH_GROUP: action->type = ACTION_TYPE_GROUP_LATCH; action->group.group = wire->latchgroup.group; if (wire->latchmods.flags & XCB_XKB_SA_CLEAR_LOCKS) action->group.flags |= ACTION_LOCK_CLEAR; if (wire->latchmods.flags & XCB_XKB_SA_LATCH_TO_LOCK) action->group.flags |= ACTION_LATCH_TO_LOCK; if (wire->latchmods.flags & XCB_XKB_SA_ISO_LOCK_FLAG_GROUP_ABSOLUTE) action->group.flags |= ACTION_ABSOLUTE_SWITCH; break; case XCB_XKB_SA_TYPE_LOCK_GROUP: action->type = ACTION_TYPE_GROUP_LOCK; action->group.group = wire->lockgroup.group; if (wire->lockgroup.flags & XCB_XKB_SA_ISO_LOCK_FLAG_GROUP_ABSOLUTE) action->group.flags |= ACTION_ABSOLUTE_SWITCH; break; case XCB_XKB_SA_TYPE_MOVE_PTR: action->type = ACTION_TYPE_PTR_MOVE; action->ptr.x = (wire->moveptr.xLow | (wire->moveptr.xHigh << 8)); action->ptr.y = (wire->moveptr.yLow | (wire->moveptr.yHigh << 8)); if (!(wire->moveptr.flags & XCB_XKB_SA_MOVE_PTR_FLAG_NO_ACCELERATION)) action->ptr.flags |= ACTION_ACCEL; if (wire->moveptr.flags & XCB_XKB_SA_MOVE_PTR_FLAG_MOVE_ABSOLUTE_X) action->ptr.flags |= ACTION_ABSOLUTE_X; if (wire->moveptr.flags & XCB_XKB_SA_MOVE_PTR_FLAG_MOVE_ABSOLUTE_Y) action->ptr.flags |= ACTION_ABSOLUTE_Y; break; case XCB_XKB_SA_TYPE_PTR_BTN: action->type = ACTION_TYPE_PTR_BUTTON; action->btn.count = wire->ptrbtn.count; action->btn.button = wire->ptrbtn.button; action->btn.flags = 0; break; case XCB_XKB_SA_TYPE_LOCK_PTR_BTN: action->type = ACTION_TYPE_PTR_LOCK; action->btn.button = wire->lockptrbtn.button; if (wire->lockptrbtn.flags & XCB_XKB_SA_ISO_LOCK_FLAG_NO_LOCK) action->btn.flags |= ACTION_LOCK_NO_LOCK; if (wire->lockptrbtn.flags & XCB_XKB_SA_ISO_LOCK_FLAG_NO_UNLOCK) action->btn.flags |= ACTION_LOCK_NO_UNLOCK; break; case XCB_XKB_SA_TYPE_SET_PTR_DFLT: action->type = ACTION_TYPE_PTR_DEFAULT; action->dflt.value = wire->setptrdflt.value; if (wire->setptrdflt.flags & XCB_XKB_SA_SET_PTR_DFLT_FLAG_DFLT_BTN_ABSOLUTE) action->dflt.flags |= ACTION_ABSOLUTE_SWITCH; break; case XCB_XKB_SA_TYPE_TERMINATE: action->type = ACTION_TYPE_TERMINATE; break; case XCB_XKB_SA_TYPE_SWITCH_SCREEN: action->type = ACTION_TYPE_SWITCH_VT; action->screen.screen = wire->switchscreen.newScreen; if (!(wire->switchscreen.flags & XCB_XKB_SWITCH_SCREEN_FLAG_APPLICATION)) action->screen.flags |= ACTION_SAME_SCREEN; if (wire->switchscreen.flags & XCB_XKB_SWITCH_SCREEN_FLAG_ABSOLUTE) action->screen.flags |= ACTION_ABSOLUTE_SWITCH; break; case XCB_XKB_SA_TYPE_SET_CONTROLS: action->type = ACTION_TYPE_CTRL_SET; { const uint16_t mask = (wire->setcontrols.boolCtrlsLow | (wire->setcontrols.boolCtrlsHigh << 8)); action->ctrls.ctrls = translate_controls_mask(mask); } break; case XCB_XKB_SA_TYPE_LOCK_CONTROLS: action->type = ACTION_TYPE_CTRL_LOCK; { const uint16_t mask = (wire->lockcontrols.boolCtrlsLow | (wire->lockcontrols.boolCtrlsHigh << 8)); action->ctrls.ctrls = translate_controls_mask(mask); } break; case XCB_XKB_SA_TYPE_NO_ACTION: /* We don't support these. */ case XCB_XKB_SA_TYPE_ISO_LOCK: case XCB_XKB_SA_TYPE_REDIRECT_KEY: case XCB_XKB_SA_TYPE_ACTION_MESSAGE: case XCB_XKB_SA_TYPE_DEVICE_BTN: case XCB_XKB_SA_TYPE_LOCK_DEVICE_BTN: case XCB_XKB_SA_TYPE_DEVICE_VALUATOR: action->type = ACTION_TYPE_NONE; break; default: if (wire->type < ACTION_TYPE_PRIVATE) { action->type = ACTION_TYPE_NONE; break; } /* Treat high unknown actions as Private actions. */ action->priv.type = wire->noaction.type; STATIC_ASSERT(sizeof(action->priv.data) == 7 && sizeof(wire->noaction.pad0) == 7, "The private action data must be 7 bytes long!"); memcpy(action->priv.data, wire->noaction.pad0, 7); break; } } static bool get_types(struct xkb_keymap *keymap, xcb_connection_t *conn, xcb_xkb_get_map_reply_t *reply, xcb_xkb_get_map_map_t *map) { int types_length = xcb_xkb_get_map_map_types_rtrn_length(reply, map); xcb_xkb_key_type_iterator_t types_iter = xcb_xkb_get_map_map_types_rtrn_iterator(reply, map); FAIL_UNLESS(reply->firstType == 0); keymap->num_types = reply->nTypes; ALLOC_OR_FAIL(keymap->types, keymap->num_types); for (int i = 0; i < types_length; i++) { xcb_xkb_key_type_t *wire_type = types_iter.data; struct xkb_key_type *type = &keymap->types[i]; FAIL_UNLESS(wire_type->numLevels > 0); type->mods.mods = translate_mods(wire_type->mods_mods, wire_type->mods_vmods, 0); type->mods.mask = translate_mods(wire_type->mods_mask, 0, 0); type->num_levels = wire_type->numLevels; { int entries_length = xcb_xkb_key_type_map_length(wire_type); xcb_xkb_kt_map_entry_iterator_t entries_iter = xcb_xkb_key_type_map_iterator(wire_type); type->num_entries = wire_type->nMapEntries; ALLOC_OR_FAIL(type->entries, type->num_entries); for (int j = 0; j < entries_length; j++) { xcb_xkb_kt_map_entry_t *wire_entry = entries_iter.data; struct xkb_key_type_entry *entry = &type->entries[j]; FAIL_UNLESS(wire_entry->level < type->num_levels); entry->level = wire_entry->level; entry->mods.mods = translate_mods(wire_entry->mods_mods, wire_entry->mods_vmods, 0); entry->mods.mask = translate_mods(wire_entry->mods_mask, 0, 0); xcb_xkb_kt_map_entry_next(&entries_iter); } } { int preserves_length = xcb_xkb_key_type_preserve_length(wire_type); xcb_xkb_mod_def_iterator_t preserves_iter = xcb_xkb_key_type_preserve_iterator(wire_type); FAIL_UNLESS((unsigned) preserves_length <= type->num_entries); for (int j = 0; j < preserves_length; j++) { xcb_xkb_mod_def_t *wire_preserve = preserves_iter.data; struct xkb_key_type_entry *entry = &type->entries[j]; entry->preserve.mods = translate_mods(wire_preserve->realMods, wire_preserve->vmods, 0); entry->preserve.mask = translate_mods(wire_preserve->mask, 0, 0); xcb_xkb_mod_def_next(&preserves_iter); } } xcb_xkb_key_type_next(&types_iter); } return true; fail: return false; } static bool get_sym_maps(struct xkb_keymap *keymap, xcb_connection_t *conn, xcb_xkb_get_map_reply_t *reply, xcb_xkb_get_map_map_t *map) { int sym_maps_length = xcb_xkb_get_map_map_syms_rtrn_length(reply, map); xcb_xkb_key_sym_map_iterator_t sym_maps_iter = xcb_xkb_get_map_map_syms_rtrn_iterator(reply, map); FAIL_UNLESS(reply->minKeyCode <= reply->maxKeyCode); FAIL_UNLESS(reply->firstKeySym >= reply->minKeyCode); FAIL_UNLESS(reply->firstKeySym + reply->nKeySyms <= reply->maxKeyCode + 1); keymap->min_key_code = reply->minKeyCode; keymap->max_key_code = reply->maxKeyCode; ALLOC_OR_FAIL(keymap->keys, keymap->max_key_code + 1); for (xkb_keycode_t kc = keymap->min_key_code; kc <= keymap->max_key_code; kc++) keymap->keys[kc].keycode = kc; for (int i = 0; i < sym_maps_length; i++) { xcb_xkb_key_sym_map_t *wire_sym_map = sym_maps_iter.data; struct xkb_key *key = &keymap->keys[reply->firstKeySym + i]; key->num_groups = wire_sym_map->groupInfo & 0x0f; FAIL_UNLESS(key->num_groups <= ARRAY_SIZE(wire_sym_map->kt_index)); ALLOC_OR_FAIL(key->groups, key->num_groups); for (unsigned j = 0; j < key->num_groups; j++) { FAIL_UNLESS(wire_sym_map->kt_index[j] < keymap->num_types); key->groups[j].type = &keymap->types[wire_sym_map->kt_index[j]]; ALLOC_OR_FAIL(key->groups[j].levels, key->groups[j].type->num_levels); } key->out_of_range_group_number = (wire_sym_map->groupInfo & 0x30) >> 4; FAIL_UNLESS(key->out_of_range_group_number <= key->num_groups); if (wire_sym_map->groupInfo & XCB_XKB_GROUPS_WRAP_CLAMP_INTO_RANGE) key->out_of_range_group_action = RANGE_SATURATE; else if (wire_sym_map->groupInfo & XCB_XKB_GROUPS_WRAP_REDIRECT_INTO_RANGE) key->out_of_range_group_action = RANGE_REDIRECT; else key->out_of_range_group_action = RANGE_WRAP; { int syms_length = xcb_xkb_key_sym_map_syms_length(wire_sym_map); xcb_keysym_t *syms_iter = xcb_xkb_key_sym_map_syms(wire_sym_map); FAIL_UNLESS((unsigned) syms_length == wire_sym_map->width * key->num_groups); for (int j = 0; j < syms_length; j++) { xcb_keysym_t wire_keysym = *syms_iter; const xkb_layout_index_t group = j / wire_sym_map->width; const xkb_level_index_t level = j % wire_sym_map->width; if (level < key->groups[group].type->num_levels && wire_keysym != XKB_KEY_NoSymbol) { key->groups[group].levels[level].num_syms = 1; key->groups[group].levels[level].u.sym = wire_keysym; } syms_iter++; } } xcb_xkb_key_sym_map_next(&sym_maps_iter); } return true; fail: return false; } static bool get_actions(struct xkb_keymap *keymap, xcb_connection_t *conn, xcb_xkb_get_map_reply_t *reply, xcb_xkb_get_map_map_t *map) { int acts_count_length = xcb_xkb_get_map_map_acts_rtrn_count_length(reply, map); uint8_t *acts_count_iter = xcb_xkb_get_map_map_acts_rtrn_count(map); xcb_xkb_action_iterator_t acts_iter = xcb_xkb_get_map_map_acts_rtrn_acts_iterator(reply, map); xcb_xkb_key_sym_map_iterator_t sym_maps_iter = xcb_xkb_get_map_map_syms_rtrn_iterator(reply, map); FAIL_UNLESS(reply->firstKeyAction == keymap->min_key_code); FAIL_UNLESS(reply->firstKeyAction + reply->nKeyActions == keymap->max_key_code + 1); for (int i = 0; i < acts_count_length; i++) { xcb_xkb_key_sym_map_t *wire_sym_map = sym_maps_iter.data; int syms_length = xcb_xkb_key_sym_map_syms_length(wire_sym_map); uint8_t wire_count = *acts_count_iter; struct xkb_key *key = &keymap->keys[reply->firstKeyAction + i]; FAIL_UNLESS(wire_count == 0 || wire_count == syms_length); for (int j = 0; j < wire_count; j++) { xcb_xkb_action_t *wire_action = acts_iter.data; const xkb_layout_index_t group = j / wire_sym_map->width; const xkb_level_index_t level = j % wire_sym_map->width; if (level < key->groups[group].type->num_levels) { union xkb_action *action = &key->groups[group].levels[level].action; translate_action(action, wire_action); } xcb_xkb_action_next(&acts_iter); } acts_count_iter++; xcb_xkb_key_sym_map_next(&sym_maps_iter); } return true; fail: return false; } static bool get_vmods(struct xkb_keymap *keymap, xcb_connection_t *conn, xcb_xkb_get_map_reply_t *reply, xcb_xkb_get_map_map_t *map) { uint8_t *iter = xcb_xkb_get_map_map_vmods_rtrn(map); keymap->mods.num_mods = NUM_REAL_MODS + MIN(msb_pos(reply->virtualMods), NUM_VMODS); for (unsigned i = 0; i < NUM_VMODS; i++) { if (reply->virtualMods & (1u << i)) { uint8_t wire = *iter; struct xkb_mod *mod = &keymap->mods.mods[NUM_REAL_MODS + i]; mod->type = MOD_VIRT; mod->mapping = translate_mods(wire, 0, 0); iter++; } } return true; } static bool get_explicits(struct xkb_keymap *keymap, xcb_connection_t *conn, xcb_xkb_get_map_reply_t *reply, xcb_xkb_get_map_map_t *map) { int length = xcb_xkb_get_map_map_explicit_rtrn_length(reply, map); xcb_xkb_set_explicit_iterator_t iter = xcb_xkb_get_map_map_explicit_rtrn_iterator(reply, map); for (int i = 0; i < length; i++) { xcb_xkb_set_explicit_t *wire = iter.data; struct xkb_key *key; FAIL_UNLESS(wire->keycode >= keymap->min_key_code && wire->keycode <= keymap->max_key_code); key = &keymap->keys[wire->keycode]; if ((wire->explicit & XCB_XKB_EXPLICIT_KEY_TYPE_1) && key->num_groups > 0) key->groups[0].explicit_type = true; if ((wire->explicit & XCB_XKB_EXPLICIT_KEY_TYPE_2) && key->num_groups > 1) key->groups[1].explicit_type = true; if ((wire->explicit & XCB_XKB_EXPLICIT_KEY_TYPE_3) && key->num_groups > 2) key->groups[2].explicit_type = true; if ((wire->explicit & XCB_XKB_EXPLICIT_KEY_TYPE_4) && key->num_groups > 3) key->groups[3].explicit_type = true; if (wire->explicit & XCB_XKB_EXPLICIT_INTERPRET) key->explicit |= EXPLICIT_INTERP; if (wire->explicit & XCB_XKB_EXPLICIT_AUTO_REPEAT) key->explicit |= EXPLICIT_REPEAT; if (wire->explicit & XCB_XKB_EXPLICIT_V_MOD_MAP) key->explicit |= EXPLICIT_VMODMAP; xcb_xkb_set_explicit_next(&iter); } return true; fail: return false; } static bool get_modmaps(struct xkb_keymap *keymap, xcb_connection_t *conn, xcb_xkb_get_map_reply_t *reply, xcb_xkb_get_map_map_t *map) { int length = xcb_xkb_get_map_map_modmap_rtrn_length(reply, map); xcb_xkb_key_mod_map_iterator_t iter = xcb_xkb_get_map_map_modmap_rtrn_iterator(reply, map); for (int i = 0; i < length; i++) { xcb_xkb_key_mod_map_t *wire = iter.data; struct xkb_key *key; FAIL_UNLESS(wire->keycode >= keymap->min_key_code && wire->keycode <= keymap->max_key_code); key = &keymap->keys[wire->keycode]; key->modmap = wire->mods; xcb_xkb_key_mod_map_next(&iter); } return true; fail: return false; } static bool get_vmodmaps(struct xkb_keymap *keymap, xcb_connection_t *conn, xcb_xkb_get_map_reply_t *reply, xcb_xkb_get_map_map_t *map) { int length = xcb_xkb_get_map_map_vmodmap_rtrn_length(reply, map); xcb_xkb_key_v_mod_map_iterator_t iter = xcb_xkb_get_map_map_vmodmap_rtrn_iterator(reply, map); for (int i = 0; i < length; i++) { xcb_xkb_key_v_mod_map_t *wire = iter.data; struct xkb_key *key; FAIL_UNLESS(wire->keycode >= keymap->min_key_code && wire->keycode <= keymap->max_key_code); key = &keymap->keys[wire->keycode]; key->vmodmap = translate_mods(0, wire->vmods, 0); xcb_xkb_key_v_mod_map_next(&iter); } return true; fail: return false; } static bool get_map(struct xkb_keymap *keymap, xcb_connection_t *conn, uint16_t device_id) { static const xcb_xkb_map_part_t required_components = (XCB_XKB_MAP_PART_KEY_TYPES | XCB_XKB_MAP_PART_KEY_SYMS | XCB_XKB_MAP_PART_MODIFIER_MAP | XCB_XKB_MAP_PART_EXPLICIT_COMPONENTS | XCB_XKB_MAP_PART_KEY_ACTIONS | XCB_XKB_MAP_PART_VIRTUAL_MODS | XCB_XKB_MAP_PART_VIRTUAL_MOD_MAP); xcb_xkb_get_map_cookie_t cookie = xcb_xkb_get_map(conn, device_id, required_components, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); xcb_xkb_get_map_reply_t *reply = xcb_xkb_get_map_reply(conn, cookie, NULL); xcb_xkb_get_map_map_t map; FAIL_IF_BAD_REPLY(reply, "XkbGetMap"); if ((reply->present & required_components) != required_components) goto fail; xcb_xkb_get_map_map_unpack(xcb_xkb_get_map_map(reply), reply->nTypes, reply->nKeySyms, reply->nKeyActions, reply->totalActions, reply->totalKeyBehaviors, reply->virtualMods, reply->totalKeyExplicit, reply->totalModMapKeys, reply->totalVModMapKeys, reply->present, &map); if (!get_types(keymap, conn, reply, &map) || !get_sym_maps(keymap, conn, reply, &map) || !get_actions(keymap, conn, reply, &map) || !get_vmods(keymap, conn, reply, &map) || !get_explicits(keymap, conn, reply, &map) || !get_modmaps(keymap, conn, reply, &map) || !get_vmodmaps(keymap, conn, reply, &map)) goto fail; free(reply); return true; fail: free(reply); return false; } static bool get_indicators(struct xkb_keymap *keymap, xcb_connection_t *conn, xcb_xkb_get_indicator_map_reply_t *reply) { xcb_xkb_indicator_map_iterator_t iter = xcb_xkb_get_indicator_map_maps_iterator(reply); keymap->num_leds = msb_pos(reply->which); for (unsigned i = 0; i < NUM_INDICATORS; i++) { if (reply->which & (1u << i)) { xcb_xkb_indicator_map_t *wire = iter.data; struct xkb_led *led = &keymap->leds[i]; if (wire->whichGroups & XCB_XKB_IM_GROUPS_WHICH_USE_BASE) led->which_groups |= XKB_STATE_LAYOUT_DEPRESSED; if (wire->whichGroups & XCB_XKB_IM_GROUPS_WHICH_USE_LATCHED) led->which_groups |= XKB_STATE_LAYOUT_LATCHED; if (wire->whichGroups & XCB_XKB_IM_GROUPS_WHICH_USE_LOCKED) led->which_groups |= XKB_STATE_LAYOUT_LOCKED; if (wire->whichGroups & XCB_XKB_IM_GROUPS_WHICH_USE_EFFECTIVE) led->which_groups |= XKB_STATE_LAYOUT_EFFECTIVE; if (wire->whichGroups & XCB_XKB_IM_GROUPS_WHICH_USE_COMPAT) led->which_groups |= XKB_STATE_LAYOUT_EFFECTIVE; led->groups = wire->groups; if (wire->whichMods & XCB_XKB_IM_MODS_WHICH_USE_BASE) led->which_mods |= XKB_STATE_MODS_DEPRESSED; if (wire->whichMods & XCB_XKB_IM_MODS_WHICH_USE_LATCHED) led->which_mods |= XKB_STATE_MODS_LATCHED; if (wire->whichMods & XCB_XKB_IM_MODS_WHICH_USE_LOCKED) led->which_mods |= XKB_STATE_MODS_LOCKED; if (wire->whichMods & XCB_XKB_IM_MODS_WHICH_USE_EFFECTIVE) led->which_mods |= XKB_STATE_MODS_EFFECTIVE; if (wire->whichMods & XCB_XKB_IM_MODS_WHICH_USE_COMPAT) led->which_mods |= XKB_STATE_MODS_EFFECTIVE; led->mods.mods = translate_mods(wire->realMods, wire->vmods, 0); led->mods.mask = translate_mods(wire->mods, 0, 0); led->ctrls = translate_controls_mask(wire->ctrls); xcb_xkb_indicator_map_next(&iter); } } return true; } static bool get_indicator_map(struct xkb_keymap *keymap, xcb_connection_t *conn, uint16_t device_id) { xcb_xkb_get_indicator_map_cookie_t cookie = xcb_xkb_get_indicator_map(conn, device_id, ALL_INDICATORS_MASK); xcb_xkb_get_indicator_map_reply_t *reply = xcb_xkb_get_indicator_map_reply(conn, cookie, NULL); FAIL_IF_BAD_REPLY(reply, "XkbGetIndicatorMap"); if (!get_indicators(keymap, conn, reply)) goto fail; free(reply); return true; fail: free(reply); return false; } static bool get_sym_interprets(struct xkb_keymap *keymap, xcb_connection_t *conn, xcb_xkb_get_compat_map_reply_t *reply) { int length = xcb_xkb_get_compat_map_si_rtrn_length(reply); xcb_xkb_sym_interpret_iterator_t iter = xcb_xkb_get_compat_map_si_rtrn_iterator(reply); FAIL_UNLESS(reply->firstSIRtrn == 0); FAIL_UNLESS(reply->nSIRtrn == reply->nTotalSI); keymap->num_sym_interprets = reply->nSIRtrn; ALLOC_OR_FAIL(keymap->sym_interprets, keymap->num_sym_interprets); for (int i = 0; i < length; i++) { xcb_xkb_sym_interpret_t *wire = iter.data; struct xkb_sym_interpret *sym_interpret = &keymap->sym_interprets[i]; sym_interpret->sym = wire->sym; switch (wire->match & XCB_XKB_SYM_INTERP_MATCH_OP_MASK) { case XCB_XKB_SYM_INTERPRET_MATCH_NONE_OF: sym_interpret->match = MATCH_NONE; break; case XCB_XKB_SYM_INTERPRET_MATCH_ANY_OF_OR_NONE: sym_interpret->match = MATCH_ANY_OR_NONE; break; case XCB_XKB_SYM_INTERPRET_MATCH_ANY_OF: sym_interpret->match = MATCH_ANY; break; case XCB_XKB_SYM_INTERPRET_MATCH_ALL_OF: sym_interpret->match = MATCH_ALL; break; case XCB_XKB_SYM_INTERPRET_MATCH_EXACTLY: sym_interpret->match = MATCH_EXACTLY; break; } sym_interpret->level_one_only = (wire->match & XCB_XKB_SYM_INTERP_MATCH_LEVEL_ONE_ONLY); sym_interpret->mods = wire->mods; if (wire->virtualMod == NO_MODIFIER) sym_interpret->virtual_mod = XKB_MOD_INVALID; else sym_interpret->virtual_mod = NUM_REAL_MODS + wire->virtualMod; sym_interpret->repeat = (wire->flags & 0x01); translate_action(&sym_interpret->action, (xcb_xkb_action_t *) &wire->action); xcb_xkb_sym_interpret_next(&iter); } return true; fail: return false; } static bool get_compat_map(struct xkb_keymap *keymap, xcb_connection_t *conn, uint16_t device_id) { xcb_xkb_get_compat_map_cookie_t cookie = xcb_xkb_get_compat_map(conn, device_id, 0, true, 0, 0); xcb_xkb_get_compat_map_reply_t *reply = xcb_xkb_get_compat_map_reply(conn, cookie, NULL); FAIL_IF_BAD_REPLY(reply, "XkbGetCompatMap"); if (!get_sym_interprets(keymap, conn, reply)) goto fail; free(reply); return true; fail: free(reply); return false; } static bool get_type_names(struct xkb_keymap *keymap, xcb_connection_t *conn, xcb_xkb_get_names_reply_t *reply, xcb_xkb_get_names_value_list_t *list) { int key_type_names_length = xcb_xkb_get_names_value_list_type_names_length(reply, list); xcb_atom_t *key_type_names_iter = xcb_xkb_get_names_value_list_type_names(list); int n_levels_per_type_length = xcb_xkb_get_names_value_list_n_levels_per_type_length(reply, list); uint8_t *n_levels_per_type_iter = xcb_xkb_get_names_value_list_n_levels_per_type(list); xcb_atom_t *kt_level_names_iter = xcb_xkb_get_names_value_list_kt_level_names(list); FAIL_UNLESS(reply->nTypes == keymap->num_types); FAIL_UNLESS(key_type_names_length == n_levels_per_type_length); for (int i = 0; i < key_type_names_length; i++) { xcb_atom_t wire_type_name = *key_type_names_iter; uint8_t wire_num_levels = *n_levels_per_type_iter; struct xkb_key_type *type = &keymap->types[i]; /* Levels must have names. */ FAIL_UNLESS(type->num_levels == wire_num_levels); ALLOC_OR_FAIL(type->level_names, type->num_levels); if (!adopt_atom(keymap->ctx, conn, wire_type_name, &type->name)) goto fail; if (!adopt_atoms(keymap->ctx, conn, kt_level_names_iter, type->level_names, wire_num_levels)) goto fail; kt_level_names_iter += wire_num_levels; key_type_names_iter++; n_levels_per_type_iter++; } return true; fail: return false; } static bool get_indicator_names(struct xkb_keymap *keymap, xcb_connection_t *conn, xcb_xkb_get_names_reply_t *reply, xcb_xkb_get_names_value_list_t *list) { xcb_atom_t *iter = xcb_xkb_get_names_value_list_indicator_names(list); FAIL_UNLESS(msb_pos(reply->indicators) <= keymap->num_leds); for (unsigned i = 0; i < NUM_INDICATORS; i++) { if (reply->indicators & (1u << i)) { xcb_atom_t wire = *iter; struct xkb_led *led = &keymap->leds[i]; if (!adopt_atom(keymap->ctx, conn, wire, &led->name)) return false; iter++; } } return true; fail: return false; } static bool get_vmod_names(struct xkb_keymap *keymap, xcb_connection_t *conn, xcb_xkb_get_names_reply_t *reply, xcb_xkb_get_names_value_list_t *list) { xcb_atom_t *iter = xcb_xkb_get_names_value_list_virtual_mod_names(list); /* * GetMap's reply->virtualMods is always 0xffff. This one really * tells us which vmods exist (a vmod must have a name), so we fix * up the size here. */ keymap->mods.num_mods = NUM_REAL_MODS + MIN(msb_pos(reply->virtualMods), NUM_VMODS); for (unsigned i = 0; i < NUM_VMODS; i++) { if (reply->virtualMods & (1u << i)) { xcb_atom_t wire = *iter; struct xkb_mod *mod = &keymap->mods.mods[NUM_REAL_MODS + i]; if (!adopt_atom(keymap->ctx, conn, wire, &mod->name)) return false; iter++; } } return true; } static bool get_group_names(struct xkb_keymap *keymap, xcb_connection_t *conn, xcb_xkb_get_names_reply_t *reply, xcb_xkb_get_names_value_list_t *list) { int length = xcb_xkb_get_names_value_list_groups_length(reply, list); xcb_atom_t *iter = xcb_xkb_get_names_value_list_groups(list); keymap->num_group_names = msb_pos(reply->groupNames); ALLOC_OR_FAIL(keymap->group_names, keymap->num_group_names); if (!adopt_atoms(keymap->ctx, conn, iter, keymap->group_names, length)) goto fail; return true; fail: return false; } static bool get_key_names(struct xkb_keymap *keymap, xcb_connection_t *conn, xcb_xkb_get_names_reply_t *reply, xcb_xkb_get_names_value_list_t *list) { int length = xcb_xkb_get_names_value_list_key_names_length(reply, list); xcb_xkb_key_name_iterator_t iter = xcb_xkb_get_names_value_list_key_names_iterator(reply, list); FAIL_UNLESS(reply->minKeyCode == keymap->min_key_code); FAIL_UNLESS(reply->maxKeyCode == keymap->max_key_code); FAIL_UNLESS(reply->firstKey == keymap->min_key_code); FAIL_UNLESS(reply->firstKey + reply->nKeys - 1U == keymap->max_key_code); for (int i = 0; i < length; i++) { xcb_xkb_key_name_t *wire = iter.data; xkb_atom_t *key_name = &keymap->keys[reply->firstKey + i].name; if (wire->name[0] == '\0') { *key_name = XKB_ATOM_NONE; } else { *key_name = xkb_atom_intern(keymap->ctx, wire->name, strnlen(wire->name, XCB_XKB_CONST_KEY_NAME_LENGTH)); if (!*key_name) return false; } xcb_xkb_key_name_next(&iter); } return true; fail: return false; } static bool get_aliases(struct xkb_keymap *keymap, xcb_connection_t *conn, xcb_xkb_get_names_reply_t *reply, xcb_xkb_get_names_value_list_t *list) { int length = xcb_xkb_get_names_value_list_key_aliases_length(reply, list); xcb_xkb_key_alias_iterator_t iter = xcb_xkb_get_names_value_list_key_aliases_iterator(reply, list); keymap->num_key_aliases = reply->nKeyAliases; ALLOC_OR_FAIL(keymap->key_aliases, keymap->num_key_aliases); for (int i = 0; i < length; i++) { xcb_xkb_key_alias_t *wire = iter.data; struct xkb_key_alias *alias = &keymap->key_aliases[i]; alias->real = xkb_atom_intern(keymap->ctx, wire->real, strnlen(wire->real, XCB_XKB_CONST_KEY_NAME_LENGTH)); alias->alias = xkb_atom_intern(keymap->ctx, wire->alias, strnlen(wire->alias, XCB_XKB_CONST_KEY_NAME_LENGTH)); if (!alias->real || !alias->alias) goto fail; xcb_xkb_key_alias_next(&iter); } return true; fail: return false; } static bool get_names(struct xkb_keymap *keymap, xcb_connection_t *conn, uint16_t device_id) { static const xcb_xkb_name_detail_t wanted = (XCB_XKB_NAME_DETAIL_KEYCODES | XCB_XKB_NAME_DETAIL_SYMBOLS | XCB_XKB_NAME_DETAIL_TYPES | XCB_XKB_NAME_DETAIL_COMPAT | XCB_XKB_NAME_DETAIL_KEY_TYPE_NAMES | XCB_XKB_NAME_DETAIL_KT_LEVEL_NAMES | XCB_XKB_NAME_DETAIL_INDICATOR_NAMES | XCB_XKB_NAME_DETAIL_KEY_NAMES | XCB_XKB_NAME_DETAIL_KEY_ALIASES | XCB_XKB_NAME_DETAIL_VIRTUAL_MOD_NAMES | XCB_XKB_NAME_DETAIL_GROUP_NAMES); static const xcb_xkb_name_detail_t required = (XCB_XKB_NAME_DETAIL_KEY_TYPE_NAMES | XCB_XKB_NAME_DETAIL_KT_LEVEL_NAMES | XCB_XKB_NAME_DETAIL_KEY_NAMES | XCB_XKB_NAME_DETAIL_VIRTUAL_MOD_NAMES); xcb_xkb_get_names_cookie_t cookie = xcb_xkb_get_names(conn, device_id, wanted); xcb_xkb_get_names_reply_t *reply = xcb_xkb_get_names_reply(conn, cookie, NULL); xcb_xkb_get_names_value_list_t list; FAIL_IF_BAD_REPLY(reply, "XkbGetNames"); FAIL_UNLESS((reply->which & required) == required); xcb_xkb_get_names_value_list_unpack(xcb_xkb_get_names_value_list(reply), reply->nTypes, reply->indicators, reply->virtualMods, reply->groupNames, reply->nKeys, reply->nKeyAliases, reply->nRadioGroups, reply->which, &list); if (!get_atom_name(conn, list.keycodesName, &keymap->keycodes_section_name) || !get_atom_name(conn, list.symbolsName, &keymap->symbols_section_name) || !get_atom_name(conn, list.typesName, &keymap->types_section_name) || !get_atom_name(conn, list.compatName, &keymap->compat_section_name) || !get_type_names(keymap, conn, reply, &list) || !get_indicator_names(keymap, conn, reply, &list) || !get_vmod_names(keymap, conn, reply, &list) || !get_group_names(keymap, conn, reply, &list) || !get_key_names(keymap, conn, reply, &list) || !get_aliases(keymap, conn, reply, &list)) goto fail; XkbEscapeMapName(keymap->keycodes_section_name); XkbEscapeMapName(keymap->symbols_section_name); XkbEscapeMapName(keymap->types_section_name); XkbEscapeMapName(keymap->compat_section_name); free(reply); return true; fail: free(reply); return false; } static bool get_controls(struct xkb_keymap *keymap, xcb_connection_t *conn, uint16_t device_id) { xcb_xkb_get_controls_cookie_t cookie = xcb_xkb_get_controls(conn, device_id); xcb_xkb_get_controls_reply_t *reply = xcb_xkb_get_controls_reply(conn, cookie, NULL); FAIL_IF_BAD_REPLY(reply, "XkbGetControls"); FAIL_UNLESS(reply->numGroups > 0 && reply->numGroups <= 4); keymap->enabled_ctrls = translate_controls_mask(reply->enabledControls); keymap->num_groups = reply->numGroups; FAIL_UNLESS(keymap->max_key_code < XCB_XKB_CONST_PER_KEY_BIT_ARRAY_SIZE * 8); for (xkb_keycode_t i = keymap->min_key_code; i <= keymap->max_key_code; i++) keymap->keys[i].repeats = (reply->perKeyRepeat[i / 8] & (1 << (i % 8))); free(reply); return true; fail: free(reply); return false; } XKB_EXPORT struct xkb_keymap * xkb_x11_keymap_new_from_device(struct xkb_context *ctx, xcb_connection_t *conn, int32_t device_id, enum xkb_keymap_compile_flags flags) { struct xkb_keymap *keymap; const enum xkb_keymap_format format = XKB_KEYMAP_FORMAT_TEXT_V1; if (flags & ~(XKB_KEYMAP_COMPILE_NO_FLAGS)) { log_err_func(ctx, "unrecognized flags: %#x\n", flags); return NULL; } if (device_id < 0 || device_id > 255) { log_err_func(ctx, "illegal device ID: %d\n", device_id); return NULL; } keymap = xkb_keymap_new(ctx, format, flags); if (!keymap) return NULL; if (!get_map(keymap, conn, device_id) || !get_indicator_map(keymap, conn, device_id) || !get_compat_map(keymap, conn, device_id) || !get_names(keymap, conn, device_id) || !get_controls(keymap, conn, device_id)) { xkb_keymap_unref(keymap); return NULL; } return keymap; }