diff --git a/src/lib/platform/EiKeyState.cpp b/src/lib/platform/EiKeyState.cpp index 60c484d5f..9af29c05c 100644 --- a/src/lib/platform/EiKeyState.cpp +++ b/src/lib/platform/EiKeyState.cpp @@ -235,28 +235,49 @@ void EiKeyState::getKeyMap(deskflow::KeyMap &keyMap) if (nsyms > 1) LOG_WARN("multiple keysyms per keycode are not supported, keycode %d", keycode); - deskflow::KeyMap::KeyItem item{}; xkb_keysym_t keysym = syms[0]; - KeySym sym = keysym; - item.m_id = XWindowsUtil::mapKeySymToKeyID(sym); - item.m_button = static_cast(keycode) - 8; // X keycode offset - item.m_group = group; // For debugging only char keysymName[128] = {0}; xkb_keysym_get_name(keysym, keysymName, sizeof(keysymName)); - // Set to all modifiers this key may be affected by + // Skip XF86_Switch_VT_* keysyms - these are local VT switching actions + // that shouldn't be sent over the network. They appear in newer + // xkeyboard-config on level 5 of function keys with CTRL+ALT type. + if (strncmp(keysymName, "XF86_Switch_VT_", 15) == 0) { + LOG_DEBUG2("skipping VT switch keysym %s for keycode %d", keysymName, keycode); + continue; + } + + deskflow::KeyMap::KeyItem item{}; + KeySym sym = keysym; + item.m_id = XWindowsUtil::mapKeySymToKeyID(sym); + item.m_button = static_cast(keycode) - 8; // X keycode offset + item.m_group = group; + + // xkb_keymap_key_get_mods_for_level() returns ALL modifier combinations + // that lead to this level. For example, with CTRL+ALT type, Level1 (F1) can + // be accessed via None, Control, or Alt. We want the SIMPLEST (fewest bits) + // combination, not the OR of all combinations. + // + // For modSensitive, we only OR modifiers from this level, not all levels. + // This prevents marking F1 as sensitive to Ctrl+Alt just because Level5 + // (which we skip) uses those modifiers. uint32_t modSensitive = 0; - for (auto n = 0U; n < nmasks; n++) { - modSensitive |= masks[n]; + uint32_t modRequired = 0xFFFFFFFF; + int minBits = 32; + for (std::size_t m = 0; m < nmasks; m++) { + modSensitive |= masks[m]; + int bits = __builtin_popcount(masks[m]); + if (bits < minBits) { + minBits = bits; + modRequired = masks[m]; + } + } + if (modRequired == 0xFFFFFFFF) { + modRequired = 0; // No masks found, use no modifiers } item.m_sensitive = convertModMask(modSensitive); - - uint32_t modRequired = 0; - for (std::size_t m = 0; m < nmasks; m++) { - modRequired |= masks[m]; - } item.m_required = convertModMask(modRequired); assignGeneratedModifiers(keycode, item); @@ -293,10 +314,23 @@ void EiKeyState::fakeKey(const Keystroke &keystroke) KeyID EiKeyState::mapKeyFromKeyval(uint32_t keyval) const { - // FIXME: That might be a bit crude...? - xkb_keysym_t xkbKeysym = xkb_state_key_get_one_sym(m_xkbState, keyval); - auto keysym = static_cast(xkbKeysym); + // Get the base keysym from level 0, ignoring current modifiers. + // We need this because with newer xkeyboard-config, function keys use CTRL+ALT type, + // and xkb_state_key_get_one_sym() would return XF86_Switch_VT_* when Ctrl+Alt are + // pressed, instead of F1. We want to send F1 + modifiers to the server, not the + // VT switch action. + const xkb_keysym_t *syms; + int nsyms = xkb_keymap_key_get_syms_by_level(m_xkbKeymap, keyval, 0, 0, &syms); + xkb_keysym_t xkbKeysym; + if (nsyms > 0) { + xkbKeysym = syms[0]; + } else { + // Fallback to state-based lookup if level 0 has no symbols + xkbKeysym = xkb_state_key_get_one_sym(m_xkbState, keyval); + } + + auto keysym = static_cast(xkbKeysym); KeyID keyid = XWindowsUtil::mapKeySymToKeyID(keysym); LOG_DEBUG1("mapped key: code=%d keysym=0x%04lx to keyID=%d", keyval, keysym, keyid);