/* * synergy -- mouse and keyboard sharing utility * Copyright (C) 2003 Chris Schoeneman * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file COPYING that should have accompanied this file. * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #include "CXWindowsKeyState.h" #include "CXWindowsUtil.h" #include "CLog.h" #include "CStringUtil.h" #include "stdmap.h" #if X_DISPLAY_MISSING # error X11 is required to build synergy #else # include # include # define XK_MISCELLANY # define XK_XKB_KEYS # include #if HAVE_XKB_EXTENSION # include #endif #endif CXWindowsKeyState::CXWindowsKeyState(Display* display, bool useXKB) : m_display(display) { XGetKeyboardControl(m_display, &m_keyboardState); #if HAVE_XKB_EXTENSION if (useXKB) { m_xkb = XkbGetMap(m_display, XkbKeyActionsMask | XkbKeyBehaviorsMask | XkbAllClientInfoMask, XkbUseCoreKbd); } else { m_xkb = NULL; } #endif setActiveGroup(kGroupPollAndSet); } CXWindowsKeyState::~CXWindowsKeyState() { #if HAVE_XKB_EXTENSION if (m_xkb != NULL) { XkbFreeKeyboard(m_xkb, 0, True); } #endif } void CXWindowsKeyState::setActiveGroup(SInt32 group) { if (group == kGroupPollAndSet) { m_group = -1; m_group = pollActiveGroup(); } else if (group == kGroupPoll) { m_group = -1; } else { assert(group >= 0); m_group = group; } } void CXWindowsKeyState::setAutoRepeat(const XKeyboardState& state) { m_keyboardState = state; } KeyModifierMask CXWindowsKeyState::mapModifiersFromX(unsigned int state) const { UInt32 offset = 8 * getGroupFromState(state); KeyModifierMask mask = 0; for (int i = 0; i < 8; ++i) { if ((state & (1u << i)) != 0) { mask |= m_modifierFromX[offset + i]; } } return mask; } bool CXWindowsKeyState::mapModifiersToX(KeyModifierMask mask, unsigned int& modifiers) const { modifiers = 0; for (SInt32 i = 0; i < kKeyModifierNumBits; ++i) { KeyModifierMask bit = (1u << i); if ((mask & bit) != 0) { KeyModifierToXMask::const_iterator j = m_modifierToX.find(bit); if (j == m_modifierToX.end()) { return false; } else { modifiers |= j->second; } } } return true; } void CXWindowsKeyState::mapKeyToKeycodes(KeyID key, CKeycodeList& keycodes) const { keycodes.clear(); std::pair range = m_keyCodeFromKey.equal_range(key); for (KeyToKeyCodeMap::const_iterator i = range.first; i != range.second; ++i) { keycodes.push_back(i->second); } } bool CXWindowsKeyState::fakeCtrlAltDel() { // pass keys through unchanged return false; } KeyModifierMask CXWindowsKeyState::pollActiveModifiers() const { Window root = DefaultRootWindow(m_display), window; int xRoot, yRoot, xWindow, yWindow; unsigned int state; if (!XQueryPointer(m_display, root, &root, &window, &xRoot, &yRoot, &xWindow, &yWindow, &state)) { state = 0; } return mapModifiersFromX(state); } SInt32 CXWindowsKeyState::pollActiveGroup() const { if (m_group != -1) { assert(m_group >= 0); return m_group; } #if HAVE_XKB_EXTENSION if (m_xkb != NULL) { XkbStateRec state; if (XkbGetState(m_display, XkbUseCoreKbd, &state)) { return state.group; } } #endif return 0; } void CXWindowsKeyState::pollPressedKeys(KeyButtonSet& pressedKeys) const { char keys[32]; XQueryKeymap(m_display, keys); for (UInt32 i = 0; i < 32; ++i) { for (UInt32 j = 0; j < 8; ++j) { if ((keys[i] & (1u << j)) != 0) { pressedKeys.insert(8 * i + j); } } } } void CXWindowsKeyState::getKeyMap(CKeyMap& keyMap) { // get autorepeat info. we must use the global_auto_repeat told to // us because it may have modified by synergy. int oldGlobalAutoRepeat = m_keyboardState.global_auto_repeat; XGetKeyboardControl(m_display, &m_keyboardState); m_keyboardState.global_auto_repeat = oldGlobalAutoRepeat; #if HAVE_XKB_EXTENSION if (m_xkb != NULL) { XkbGetUpdatedMap(m_display, XkbKeyActionsMask | XkbKeyBehaviorsMask | XkbAllClientInfoMask, m_xkb); updateKeysymMapXKB(keyMap); } else #endif { updateKeysymMap(keyMap); } } void CXWindowsKeyState::fakeKey(const Keystroke& keystroke) { switch (keystroke.m_type) { case Keystroke::kButton: LOG((CLOG_DEBUG1 " %03x (%08x) %s", keystroke.m_data.m_button.m_button, keystroke.m_data.m_button.m_client, keystroke.m_data.m_button.m_press ? "down" : "up")); if (keystroke.m_data.m_button.m_repeat) { int c = keystroke.m_data.m_button.m_button; int i = (c >> 3); int b = 1 << (c & 7); if (m_keyboardState.global_auto_repeat == AutoRepeatModeOff || (m_keyboardState.auto_repeats[i] & b) == 0) { LOG((CLOG_DEBUG1 " discard autorepeat")); break; } } XTestFakeKeyEvent(m_display, keystroke.m_data.m_button.m_button, keystroke.m_data.m_button.m_press ? True : False, CurrentTime); break; case Keystroke::kGroup: if (keystroke.m_data.m_group.m_absolute) { LOG((CLOG_DEBUG1 " group %d", keystroke.m_data.m_group.m_group)); #if HAVE_XKB_EXTENSION if (m_xkb != NULL) { XkbLockGroup(m_display, XkbUseCoreKbd, keystroke.m_data.m_group.m_group); } else #endif { LOG((CLOG_DEBUG1 " ignored")); } } else { LOG((CLOG_DEBUG1 " group %+d", keystroke.m_data.m_group.m_group)); #if HAVE_XKB_EXTENSION if (m_xkb != NULL) { XkbLockGroup(m_display, XkbUseCoreKbd, getEffectiveGroup(pollActiveGroup(), keystroke.m_data.m_group.m_group)); } else #endif { LOG((CLOG_DEBUG1 " ignored")); } } break; } XFlush(m_display); } void CXWindowsKeyState::updateKeysymMap(CKeyMap& keyMap) { // there are up to 4 keysyms per keycode static const int maxKeysyms = 4; LOG((CLOG_DEBUG1 "non-XKB mapping")); // prepare map from X modifier to KeyModifierMask. certain bits // are predefined. m_modifierFromX.clear(); m_modifierFromX.resize(8); m_modifierFromX[ShiftMapIndex] = KeyModifierShift; m_modifierFromX[LockMapIndex] = KeyModifierCapsLock; m_modifierFromX[ControlMapIndex] = KeyModifierControl; m_modifierToX.clear(); m_modifierToX[KeyModifierShift] = ShiftMask; m_modifierToX[KeyModifierCapsLock] = LockMask; m_modifierToX[KeyModifierControl] = ControlMask; // prepare map from KeyID to KeyCode m_keyCodeFromKey.clear(); // get the number of keycodes int minKeycode, maxKeycode; XDisplayKeycodes(m_display, &minKeycode, &maxKeycode); int numKeycodes = maxKeycode - minKeycode + 1; // get the keyboard mapping for all keys int keysymsPerKeycode; KeySym* allKeysyms = XGetKeyboardMapping(m_display, minKeycode, numKeycodes, &keysymsPerKeycode); // it's more convenient to always have maxKeysyms KeySyms per key { KeySym* tmpKeysyms = new KeySym[maxKeysyms * numKeycodes]; for (int i = 0; i < numKeycodes; ++i) { for (int j = 0; j < maxKeysyms; ++j) { if (j < keysymsPerKeycode) { tmpKeysyms[maxKeysyms * i + j] = allKeysyms[keysymsPerKeycode * i + j]; } else { tmpKeysyms[maxKeysyms * i + j] = NoSymbol; } } } XFree(allKeysyms); allKeysyms = tmpKeysyms; } // get the buttons assigned to modifiers. X11 does not predefine // the meaning of any modifiers except shift, caps lock, and the // control key. the meaning of a modifier bit (other than those) // depends entirely on the KeySyms mapped to that bit. unfortunately // you cannot map a bit back to the KeySym used to produce it. // for example, let's say button 1 maps to Alt_L without shift and // Meta_L with shift. now if mod1 is mapped to button 1 that could // mean the user used Alt or Meta to turn on that modifier and there's // no way to know which. it's also possible for one button to be // mapped to multiple bits so both mod1 and mod2 could be generated // by button 1. // // we're going to ignore any modifier for a button except the first. // with the above example, that means we'll ignore the mod2 modifier // bit unless it's also mapped to some other button. we're also // going to ignore all KeySyms except the first modifier KeySym, // which means button 1 above won't map to Meta, just Alt. std::map modifierButtons; XModifierKeymap* modifiers = XGetModifierMapping(m_display); for (unsigned int i = 0; i < 8; ++i) { const KeyCode* buttons = modifiers->modifiermap + i * modifiers->max_keypermod; for (int j = 0; j < modifiers->max_keypermod; ++j) { modifierButtons.insert(std::make_pair(buttons[j], i)); } } XFreeModifiermap(modifiers); modifierButtons.erase(0); // Hack to deal with VMware. When a VMware client grabs input the // player clears out the X modifier map for whatever reason. We're // notified of the change and arrive here to discover that there // are no modifiers at all. Since this prevents the modifiers from // working in the VMware client we'll use the last known good set // of modifiers when there are no modifiers. If there are modifiers // we update the last known good set. if (!modifierButtons.empty()) { m_lastGoodNonXKBModifiers = modifierButtons; } else { modifierButtons = m_lastGoodNonXKBModifiers; } // add entries for each keycode CKeyMap::KeyItem item; for (int i = 0; i < numKeycodes; ++i) { KeySym* keysyms = allKeysyms + maxKeysyms * i; KeyCode keycode = static_cast(i + minKeycode); item.m_button = static_cast(keycode); item.m_client = 0; // determine modifier sensitivity item.m_sensitive = 0; // if the keysyms in levels 2 or 3 exist and differ from levels // 0 and 1 then the key is sensitive AltGr (Mode_switch) if ((keysyms[2] != NoSymbol && keysyms[2] != keysyms[0]) || (keysyms[3] != NoSymbol && keysyms[2] != keysyms[1])) { item.m_sensitive |= KeyModifierAltGr; } // check if the key is caps-lock sensitive. some systems only // provide one keysym for keys sensitive to caps-lock. if we // find that then fill in the missing keysym. if (keysyms[0] != NoSymbol && keysyms[1] == NoSymbol && keysyms[2] == NoSymbol && keysyms[3] == NoSymbol) { KeySym lKeysym, uKeysym; XConvertCase(keysyms[0], &lKeysym, &uKeysym); if (lKeysym != uKeysym) { keysyms[0] = lKeysym; keysyms[1] = uKeysym; item.m_sensitive |= KeyModifierCapsLock; } } else if (keysyms[0] != NoSymbol && keysyms[1] != NoSymbol) { KeySym lKeysym, uKeysym; XConvertCase(keysyms[0], &lKeysym, &uKeysym); if (lKeysym != uKeysym && lKeysym == keysyms[0] && uKeysym == keysyms[1]) { item.m_sensitive |= KeyModifierCapsLock; } else if (keysyms[2] != NoSymbol && keysyms[3] != NoSymbol) { XConvertCase(keysyms[2], &lKeysym, &uKeysym); if (lKeysym != uKeysym && lKeysym == keysyms[2] && uKeysym == keysyms[3]) { item.m_sensitive |= KeyModifierCapsLock; } } } // key is sensitive to shift if keysyms in levels 0 and 1 or // levels 2 and 3 don't match. it's also sensitive to shift // if it's sensitive to caps-lock. if ((item.m_sensitive & KeyModifierCapsLock) != 0) { item.m_sensitive |= KeyModifierShift; } else if ((keysyms[0] != NoSymbol && keysyms[1] != NoSymbol && keysyms[0] != keysyms[1]) || (keysyms[2] != NoSymbol && keysyms[3] != NoSymbol && keysyms[2] != keysyms[3])) { item.m_sensitive |= KeyModifierShift; } // key is sensitive to numlock if any keysym on it is if (IsKeypadKey(keysyms[0]) || IsPrivateKeypadKey(keysyms[0]) || IsKeypadKey(keysyms[1]) || IsPrivateKeypadKey(keysyms[1]) || IsKeypadKey(keysyms[2]) || IsPrivateKeypadKey(keysyms[2]) || IsKeypadKey(keysyms[3]) || IsPrivateKeypadKey(keysyms[3])) { item.m_sensitive |= KeyModifierNumLock; } // do each keysym (shift level) for (int j = 0; j < maxKeysyms; ++j) { item.m_id = CXWindowsUtil::mapKeySymToKeyID(keysyms[j]); if (item.m_id == kKeyNone) { if (j != 0 && modifierButtons.count(keycode) > 0) { // pretend the modifier works in other shift levels // because it probably does. if (keysyms[1] == NoSymbol || j != 3) { item.m_id = CXWindowsUtil::mapKeySymToKeyID(keysyms[0]); } else { item.m_id = CXWindowsUtil::mapKeySymToKeyID(keysyms[1]); } } if (item.m_id == kKeyNone) { continue; } } // group is 0 for levels 0 and 1 and 1 for levels 2 and 3 item.m_group = (j >= 2) ? 1 : 0; // compute required modifiers item.m_required = 0; if ((j & 1) != 0) { item.m_required |= KeyModifierShift; } if ((j & 2) != 0) { item.m_required |= KeyModifierAltGr; } item.m_generates = 0; item.m_lock = false; if (modifierButtons.count(keycode) > 0) { // get flags for modifier keys CKeyMap::initModifierKey(item); // add mapping from X (unless we already have) if (item.m_generates != 0) { unsigned int bit = modifierButtons[keycode]; if (m_modifierFromX[bit] == 0) { m_modifierFromX[bit] = item.m_generates; m_modifierToX[item.m_generates] = (1u << bit); } } } // add key keyMap.addKeyEntry(item); m_keyCodeFromKey.insert(std::make_pair(item.m_id, keycode)); // add other ways to synthesize the key if ((j & 1) != 0) { // add capslock version of key is sensitive to capslock KeySym lKeysym, uKeysym; XConvertCase(keysyms[j], &lKeysym, &uKeysym); if (lKeysym != uKeysym && lKeysym == keysyms[j - 1] && uKeysym == keysyms[j]) { item.m_required &= ~KeyModifierShift; item.m_required |= KeyModifierCapsLock; keyMap.addKeyEntry(item); item.m_required |= KeyModifierShift; item.m_required &= ~KeyModifierCapsLock; } // add numlock version of key if sensitive to numlock if (IsKeypadKey(keysyms[j]) || IsPrivateKeypadKey(keysyms[j])) { item.m_required &= ~KeyModifierShift; item.m_required |= KeyModifierNumLock; keyMap.addKeyEntry(item); item.m_required |= KeyModifierShift; item.m_required &= ~KeyModifierNumLock; } } } } delete[] allKeysyms; } #if HAVE_XKB_EXTENSION void CXWindowsKeyState::updateKeysymMapXKB(CKeyMap& keyMap) { static const XkbKTMapEntryRec defMapEntry = { True, // active 0, // level { 0, // mods.mask 0, // mods.real_mods 0 // mods.vmods } }; LOG((CLOG_DEBUG1 "XKB mapping")); // find the number of groups int maxNumGroups = 0; for (int i = m_xkb->min_key_code; i <= m_xkb->max_key_code; ++i) { int numGroups = XkbKeyNumGroups(m_xkb, static_cast(i)); if (numGroups > maxNumGroups) { maxNumGroups = numGroups; } } // prepare map from X modifier to KeyModifierMask std::vector modifierLevel(maxNumGroups * 8, 4); m_modifierFromX.clear(); m_modifierFromX.resize(maxNumGroups * 8); m_modifierToX.clear(); // prepare map from KeyID to KeyCode m_keyCodeFromKey.clear(); // Hack to deal with VMware. When a VMware client grabs input the // player clears out the X modifier map for whatever reason. We're // notified of the change and arrive here to discover that there // are no modifiers at all. Since this prevents the modifiers from // working in the VMware client we'll use the last known good set // of modifiers when there are no modifiers. If there are modifiers // we update the last known good set. bool useLastGoodModifiers = !hasModifiersXKB(); if (!useLastGoodModifiers) { m_lastGoodXKBModifiers.clear(); } // check every button. on this pass we save all modifiers as native // X modifier masks. CKeyMap::KeyItem item; for (int i = m_xkb->min_key_code; i <= m_xkb->max_key_code; ++i) { KeyCode keycode = static_cast(i); item.m_button = static_cast(keycode); item.m_client = 0; // skip keys with no groups (they generate no symbols) if (XkbKeyNumGroups(m_xkb, keycode) == 0) { continue; } // note half-duplex keys const XkbBehavior& b = m_xkb->server->behaviors[keycode]; if ((b.type & XkbKB_OpMask) == XkbKB_Lock) { keyMap.addHalfDuplexButton(item.m_button); } // iterate over all groups for (int group = 0; group < maxNumGroups; ++group) { item.m_group = group; int eGroup = getEffectiveGroup(keycode, group); // get key info XkbKeyTypePtr type = XkbKeyKeyType(m_xkb, keycode, eGroup); // set modifiers the item is sensitive to item.m_sensitive = type->mods.mask; // iterate over all shift levels for the button (including none) for (int j = -1; j < type->map_count; ++j) { const XkbKTMapEntryRec* mapEntry = ((j == -1) ? &defMapEntry : type->map + j); if (!mapEntry->active) { continue; } int level = mapEntry->level; // set required modifiers for this item item.m_required = mapEntry->mods.mask; if ((item.m_required & LockMask) != 0 && j != -1 && type->preserve != NULL && (type->preserve[j].mask & LockMask) != 0) { // sensitive caps lock and we preserve caps-lock. // preserving caps-lock means we Xlib functions would // yield the capitialized KeySym so we'll adjust the // level accordingly. if ((level ^ 1) < type->num_levels) { level ^= 1; } } // get the keysym for this item KeySym keysym = XkbKeySymEntry(m_xkb, keycode, level, eGroup); // check for group change actions, locking modifiers, and // modifier masks. item.m_lock = false; bool isModifier = false; UInt32 modifierMask = m_xkb->map->modmap[keycode]; if (XkbKeyHasActions(m_xkb, keycode)) { XkbAction* action = XkbKeyActionEntry(m_xkb, keycode, level, eGroup); if (action->type == XkbSA_SetMods || action->type == XkbSA_LockMods) { isModifier = true; // note toggles item.m_lock = (action->type == XkbSA_LockMods); // maybe use action's mask if ((action->mods.flags & XkbSA_UseModMapMods) == 0) { modifierMask = action->mods.mask; } } else if (action->type == XkbSA_SetGroup || action->type == XkbSA_LatchGroup || action->type == XkbSA_LockGroup) { // ignore group change key continue; } } level = mapEntry->level; // VMware modifier hack if (useLastGoodModifiers) { XKBModifierMap::const_iterator k = m_lastGoodXKBModifiers.find(eGroup * 256 + keycode); if (k != m_lastGoodXKBModifiers.end()) { // Use last known good modifier isModifier = true; level = k->second.m_level; modifierMask = k->second.m_mask; item.m_lock = k->second.m_lock; } } else if (isModifier) { // Save known good modifier XKBModifierInfo& info = m_lastGoodXKBModifiers[eGroup * 256 + keycode]; info.m_level = level; info.m_mask = modifierMask; info.m_lock = item.m_lock; } // record the modifier mask for this key. don't bother // for keys that change the group. item.m_generates = 0; UInt32 modifierBit = CXWindowsUtil::getModifierBitForKeySym(keysym); if (isModifier && modifierBit != kKeyModifierBitNone) { item.m_generates = (1u << modifierBit); for (SInt32 j = 0; j < 8; ++j) { // skip modifiers this key doesn't generate if ((modifierMask & (1u << j)) == 0) { continue; } // skip keys that map to a modifier that we've // already seen using fewer modifiers. that is // if this key must combine with other modifiers // and we know of a key that combines with fewer // modifiers (or no modifiers) then prefer the // other key. if (level >= modifierLevel[8 * group + j]) { continue; } modifierLevel[8 * group + j] = level; // save modifier m_modifierFromX[8 * group + j] |= (1u << modifierBit); m_modifierToX.insert(std::make_pair( 1u << modifierBit, 1u << j)); } } // handle special cases of just one keysym for the keycode if (type->num_levels == 1) { // if there are upper- and lowercase versions of the // keysym then add both. KeySym lKeysym, uKeysym; XConvertCase(keysym, &lKeysym, &uKeysym); if (lKeysym != uKeysym) { if (j != -1) { continue; } item.m_sensitive |= ShiftMask | LockMask; KeyID lKeyID = CXWindowsUtil::mapKeySymToKeyID(lKeysym); KeyID uKeyID = CXWindowsUtil::mapKeySymToKeyID(uKeysym); if (lKeyID == kKeyNone || uKeyID == kKeyNone) { continue; } item.m_id = lKeyID; item.m_required = 0; keyMap.addKeyEntry(item); item.m_id = uKeyID; item.m_required = ShiftMask; keyMap.addKeyEntry(item); item.m_required = LockMask; keyMap.addKeyEntry(item); if (group == 0) { m_keyCodeFromKey.insert( std::make_pair(lKeyID, keycode)); m_keyCodeFromKey.insert( std::make_pair(uKeyID, keycode)); } continue; } } // add entry item.m_id = CXWindowsUtil::mapKeySymToKeyID(keysym); keyMap.addKeyEntry(item); if (group == 0) { m_keyCodeFromKey.insert(std::make_pair(item.m_id, keycode)); } } } } // change all modifier masks to synergy masks from X masks keyMap.foreachKey(&CXWindowsKeyState::remapKeyModifiers, this); // allow composition across groups keyMap.allowGroupSwitchDuringCompose(); } #endif void CXWindowsKeyState::remapKeyModifiers(KeyID id, SInt32 group, CKeyMap::KeyItem& item, void* vself) { CXWindowsKeyState* self = reinterpret_cast(vself); item.m_required = self->mapModifiersFromX(XkbBuildCoreState(item.m_required, group)); item.m_sensitive = self->mapModifiersFromX(XkbBuildCoreState(item.m_sensitive, group)); } bool CXWindowsKeyState::hasModifiersXKB() const { #if HAVE_XKB_EXTENSION // iterate over all keycodes for (int i = m_xkb->min_key_code; i <= m_xkb->max_key_code; ++i) { KeyCode keycode = static_cast(i); if (XkbKeyHasActions(m_xkb, keycode)) { // iterate over all groups int numGroups = XkbKeyNumGroups(m_xkb, keycode); for (int group = 0; group < numGroups; ++group) { // iterate over all shift levels for the button (including none) XkbKeyTypePtr type = XkbKeyKeyType(m_xkb, keycode, group); for (int j = -1; j < type->map_count; ++j) { if (j != -1 && !type->map[j].active) { continue; } int level = ((j == -1) ? 0 : type->map[j].level); XkbAction* action = XkbKeyActionEntry(m_xkb, keycode, level, group); if (action->type == XkbSA_SetMods || action->type == XkbSA_LockMods) { return true; } } } } } #endif return false; } int CXWindowsKeyState::getEffectiveGroup(KeyCode keycode, int group) const { (void)keycode; #if HAVE_XKB_EXTENSION // get effective group for key int numGroups = XkbKeyNumGroups(m_xkb, keycode); if (group >= numGroups) { unsigned char groupInfo = XkbKeyGroupInfo(m_xkb, keycode); switch (XkbOutOfRangeGroupAction(groupInfo)) { case XkbClampIntoRange: group = numGroups - 1; break; case XkbRedirectIntoRange: group = XkbOutOfRangeGroupNumber(groupInfo); if (group >= numGroups) { group = 0; } break; default: // wrap group %= numGroups; break; } } #endif return group; } UInt32 CXWindowsKeyState::getGroupFromState(unsigned int state) const { #if HAVE_XKB_EXTENSION if (m_xkb != NULL) { return XkbGroupForCoreState(state); } #endif return 0; }