#include "IKeyboard.hpp" #include "../defines.hpp" #include "../helpers/varlist/VarList.hpp" #include "../managers/input/InputManager.hpp" #include "../managers/SeatManager.hpp" #include "../config/ConfigManager.hpp" #include #include #include #define LED_COUNT 3 constexpr static std::array MODNAMES = { XKB_MOD_NAME_SHIFT, XKB_MOD_NAME_CAPS, XKB_MOD_NAME_CTRL, XKB_MOD_NAME_ALT, XKB_MOD_NAME_NUM, "Mod3", XKB_MOD_NAME_LOGO, "Mod5", }; constexpr static std::array LEDNAMES = {XKB_LED_NAME_NUM, XKB_LED_NAME_CAPS, XKB_LED_NAME_SCROLL}; // uint32_t IKeyboard::getCapabilities() { return HID_INPUT_CAPABILITY_KEYBOARD; } eHIDType IKeyboard::getType() { return HID_TYPE_KEYBOARD; } IKeyboard::~IKeyboard() { events.destroy.emit(); clearManuallyAllocd(); } void IKeyboard::clearManuallyAllocd() { if (xkbStaticState) xkb_state_unref(xkbStaticState); if (xkbState) xkb_state_unref(xkbState); if (xkbKeymap) xkb_keymap_unref(xkbKeymap); if (xkbKeymapFD >= 0) close(xkbKeymapFD); xkbKeymap = nullptr; xkbState = nullptr; xkbStaticState = nullptr; xkbKeymapFD = -1; } void IKeyboard::setKeymap(const SStringRuleNames& rules) { if (keymapOverridden) { Debug::log(LOG, "Ignoring setKeymap: keymap is overridden"); return; } currentRules = rules; xkb_rule_names XKBRULES = { .rules = rules.rules.c_str(), .model = rules.model.c_str(), .layout = rules.layout.c_str(), .variant = rules.variant.c_str(), .options = rules.options.c_str(), }; const auto CONTEXT = xkb_context_new(XKB_CONTEXT_NO_FLAGS); if (!CONTEXT) { Debug::log(ERR, "setKeymap: CONTEXT null??"); return; } clearManuallyAllocd(); Debug::log(LOG, "Attempting to create a keymap for layout {} with variant {} (rules: {}, model: {}, options: {})", rules.layout, rules.variant, rules.rules, rules.model, rules.options); if (!xkbFilePath.empty()) { auto path = absolutePath(xkbFilePath, g_pConfigManager->configCurrentPath); if (FILE* const KEYMAPFILE = fopen(path.c_str(), "r"); !KEYMAPFILE) Debug::log(ERR, "Cannot open input:kb_file= file for reading"); else { xkbKeymap = xkb_keymap_new_from_file(CONTEXT, KEYMAPFILE, XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS); fclose(KEYMAPFILE); } } if (!xkbKeymap) xkbKeymap = xkb_keymap_new_from_names(CONTEXT, &XKBRULES, XKB_KEYMAP_COMPILE_NO_FLAGS); if (!xkbKeymap) { g_pConfigManager->addParseError("Invalid keyboard layout passed. ( rules: " + rules.rules + ", model: " + rules.model + ", variant: " + rules.variant + ", options: " + rules.options + ", layout: " + rules.layout + " )"); Debug::log(ERR, "Keyboard layout {} with variant {} (rules: {}, model: {}, options: {}) couldn't have been loaded.", rules.layout, rules.variant, rules.rules, rules.model, rules.options); memset(&XKBRULES, 0, sizeof(XKBRULES)); currentRules.rules = ""; currentRules.model = ""; currentRules.variant = ""; currentRules.options = ""; currentRules.layout = "us"; xkbKeymap = xkb_keymap_new_from_names(CONTEXT, &XKBRULES, XKB_KEYMAP_COMPILE_NO_FLAGS); } updateXKBTranslationState(xkbKeymap); const auto NUMLOCKON = g_pConfigManager->getDeviceInt(hlName, "numlock_by_default", "input:numlock_by_default"); if (NUMLOCKON == 1) { // lock numlock const auto IDX = xkb_map_mod_get_index(xkbKeymap, XKB_MOD_NAME_NUM); if (IDX != XKB_MOD_INVALID) modifiersState.locked |= (uint32_t)1 << IDX; // 0 to avoid mods getting stuck if depressed during reload updateModifiers(0, 0, modifiersState.locked, modifiersState.group); } for (size_t i = 0; i < LEDNAMES.size(); ++i) { ledIndexes.at(i) = xkb_map_led_get_index(xkbKeymap, LEDNAMES.at(i)); Debug::log(LOG, "xkb: LED index {} (name {}) got index {}", i, LEDNAMES.at(i), ledIndexes.at(i)); } for (size_t i = 0; i < MODNAMES.size(); ++i) { modIndexes.at(i) = xkb_map_mod_get_index(xkbKeymap, MODNAMES.at(i)); Debug::log(LOG, "xkb: Mod index {} (name {}) got index {}", i, MODNAMES.at(i), modIndexes.at(i)); } updateKeymapFD(); xkb_context_unref(CONTEXT); g_pSeatManager->updateActiveKeyboardData(); } void IKeyboard::updateKeymapFD() { Debug::log(LOG, "Updating keymap fd for keyboard {}", deviceName); if (xkbKeymapFD >= 0) close(xkbKeymapFD); xkbKeymapFD = -1; auto cKeymapStr = xkb_keymap_get_as_string(xkbKeymap, XKB_KEYMAP_FORMAT_TEXT_V1); xkbKeymapString = cKeymapStr; free(cKeymapStr); int rw, ro; if (!allocateSHMFilePair(xkbKeymapString.length() + 1, &rw, &ro)) Debug::log(ERR, "IKeyboard: failed to allocate shm pair for the keymap"); else { auto keymapFDDest = mmap(nullptr, xkbKeymapString.length() + 1, PROT_READ | PROT_WRITE, MAP_SHARED, rw, 0); close(rw); if (keymapFDDest == MAP_FAILED) { Debug::log(ERR, "IKeyboard: failed to mmap a shm pair for the keymap"); close(ro); } else { memcpy(keymapFDDest, xkbKeymapString.c_str(), xkbKeymapString.length()); munmap(keymapFDDest, xkbKeymapString.length() + 1); xkbKeymapFD = ro; } } Debug::log(LOG, "Updated keymap fd to {}", xkbKeymapFD); } void IKeyboard::updateXKBTranslationState(xkb_keymap* const keymap) { if (xkbStaticState) xkb_state_unref(xkbStaticState); if (xkbState) xkb_state_unref(xkbState); xkbState = nullptr; xkbStaticState = nullptr; if (keymap) { Debug::log(LOG, "Updating keyboard {:x}'s translation state from a provided keymap", (uintptr_t)this); xkbStaticState = xkb_state_new(keymap); xkbState = xkb_state_new(keymap); return; } const auto KEYMAP = xkbKeymap; const auto STATE = xkbState; const auto LAYOUTSNUM = xkb_keymap_num_layouts(KEYMAP); const auto PCONTEXT = xkb_context_new(XKB_CONTEXT_NO_FLAGS); for (uint32_t i = 0; i < LAYOUTSNUM; ++i) { if (xkb_state_layout_index_is_active(STATE, i, XKB_STATE_LAYOUT_EFFECTIVE) == 1) { Debug::log(LOG, "Updating keyboard {:x}'s translation state from an active index {}", (uintptr_t)this, i); CVarList keyboardLayouts(currentRules.layout, 0, ','); CVarList keyboardModels(currentRules.model, 0, ','); CVarList keyboardVariants(currentRules.variant, 0, ','); xkb_rule_names rules = {.rules = "", .model = "", .layout = "", .variant = "", .options = ""}; std::string layout, model, variant; layout = keyboardLayouts[i % keyboardLayouts.size()]; model = keyboardModels[i % keyboardModels.size()]; variant = keyboardVariants[i % keyboardVariants.size()]; rules.layout = layout.c_str(); rules.model = model.c_str(); rules.variant = variant.c_str(); auto KEYMAP = xkb_keymap_new_from_names(PCONTEXT, &rules, XKB_KEYMAP_COMPILE_NO_FLAGS); if (!KEYMAP) { Debug::log(ERR, "updateXKBTranslationState: keymap failed 1, fallback without model/variant"); rules.model = ""; rules.variant = ""; KEYMAP = xkb_keymap_new_from_names(PCONTEXT, &rules, XKB_KEYMAP_COMPILE_NO_FLAGS); } if (!KEYMAP) { Debug::log(ERR, "updateXKBTranslationState: keymap failed 2, fallback to us"); rules.layout = "us"; KEYMAP = xkb_keymap_new_from_names(PCONTEXT, &rules, XKB_KEYMAP_COMPILE_NO_FLAGS); } xkbState = xkb_state_new(KEYMAP); xkbStaticState = xkb_state_new(KEYMAP); xkb_keymap_unref(KEYMAP); xkb_context_unref(PCONTEXT); return; } } Debug::log(LOG, "Updating keyboard {:x}'s translation state from an unknown index", (uintptr_t)this); xkb_rule_names rules = { .rules = currentRules.rules.c_str(), .model = currentRules.model.c_str(), .layout = currentRules.layout.c_str(), .variant = currentRules.variant.c_str(), .options = currentRules.options.c_str(), }; const auto NEWKEYMAP = xkb_keymap_new_from_names(PCONTEXT, &rules, XKB_KEYMAP_COMPILE_NO_FLAGS); xkbState = xkb_state_new(NEWKEYMAP); xkbStaticState = xkb_state_new(NEWKEYMAP); xkb_keymap_unref(NEWKEYMAP); xkb_context_unref(PCONTEXT); } std::string IKeyboard::getActiveLayout() { const auto KEYMAP = xkbKeymap; const auto STATE = xkbState; const auto LAYOUTSNUM = xkb_keymap_num_layouts(KEYMAP); for (uint32_t i = 0; i < LAYOUTSNUM; ++i) { if (xkb_state_layout_index_is_active(STATE, i, XKB_STATE_LAYOUT_EFFECTIVE) == 1) { const auto LAYOUTNAME = xkb_keymap_layout_get_name(KEYMAP, i); if (LAYOUTNAME) return std::string(LAYOUTNAME); return "error"; } } return "none"; } std::optional IKeyboard::getLEDs() { if (xkbState == nullptr) return {}; uint32_t leds = 0; for (uint32_t i = 0; i < LED_COUNT; ++i) { if (xkb_state_led_index_is_active(xkbState, ledIndexes.at(i))) leds |= (1 << i); } return leds; } void IKeyboard::updateLEDs() { std::optional leds = getLEDs(); if (!leds.has_value()) return; updateLEDs(leds.value()); } void IKeyboard::updateLEDs(uint32_t leds) { if (!xkbState) return; if (isVirtual() && g_pInputManager->shouldIgnoreVirtualKeyboard(self.lock())) return; if (!aq()) return; aq()->updateLEDs(leds); } uint32_t IKeyboard::getModifiers() { uint32_t modMask = modifiersState.depressed | modifiersState.latched; uint32_t mods = 0; for (size_t i = 0; i < modIndexes.size(); ++i) { if (modIndexes.at(i) == XKB_MOD_INVALID) continue; if (!(modMask & (1 << modIndexes.at(i)))) continue; mods |= (1 << i); } return mods; } void IKeyboard::updateModifiers(uint32_t depressed, uint32_t latched, uint32_t locked, uint32_t group) { if (!xkbState) return; xkb_state_update_mask(xkbState, depressed, latched, locked, 0, 0, group); if (!updateModifiersState()) return; keyboardEvents.modifiers.emit(SModifiersEvent{ .depressed = modifiersState.depressed, .latched = modifiersState.latched, .locked = modifiersState.locked, .group = modifiersState.group, }); updateLEDs(); } bool IKeyboard::updateModifiersState() { if (!xkbState) return false; auto depressed = xkb_state_serialize_mods(xkbState, XKB_STATE_MODS_DEPRESSED); auto latched = xkb_state_serialize_mods(xkbState, XKB_STATE_MODS_LATCHED); auto locked = xkb_state_serialize_mods(xkbState, XKB_STATE_MODS_LOCKED); auto group = xkb_state_serialize_layout(xkbState, XKB_STATE_LAYOUT_EFFECTIVE); if (depressed == modifiersState.depressed && latched == modifiersState.latched && locked == modifiersState.locked && group == modifiersState.group) return false; modifiersState.depressed = depressed; modifiersState.latched = latched; modifiersState.locked = locked; modifiersState.group = group; return true; } void IKeyboard::updateXkbStateWithKey(uint32_t xkbKey, bool pressed) { const auto contains = std::find(pressedXKB.begin(), pressedXKB.end(), xkbKey) != pressedXKB.end(); if (contains && pressed) return; if (!contains && !pressed) return; if (contains) std::erase(pressedXKB, xkbKey); else pressedXKB.emplace_back(xkbKey); xkb_state_update_key(xkbState, xkbKey, pressed ? XKB_KEY_DOWN : XKB_KEY_UP); if (updateModifiersState()) { keyboardEvents.modifiers.emit(SModifiersEvent{ .depressed = modifiersState.depressed, .latched = modifiersState.latched, .locked = modifiersState.locked, .group = modifiersState.group, }); } }