fix: ctrl+alt+fn issue

This commit is contained in:
enzious
2025-10-20 14:05:31 -05:00
committed by Chris Rizzitello
parent 42b90a2b14
commit 5fc4e9dba6

View File

@ -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<KeyButton>(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<KeyButton>(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<KeySym>(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<KeySym>(xkbKeysym);
KeyID keyid = XWindowsUtil::mapKeySymToKeyID(keysym);
LOG_DEBUG1("mapped key: code=%d keysym=0x%04lx to keyID=%d", keyval, keysym, keyid);