mirror of
https://github.com/hyprwm/Hyprland
synced 2026-05-07 10:58:20 +02:00
keybinds: fix keycode matching on lua (#14254)
This commit is contained in:
parent
fceb15979e
commit
a3fa7b4950
4 changed files with 66 additions and 33 deletions
|
|
@ -1538,15 +1538,15 @@ std::optional<std::string> CConfigManager::handleBind(const std::string& command
|
|||
else if ((ARGS.size() > sc<size_t>(4) + DESCR_OFFSET + DEVICE_OFFSET && !mouse) || (ARGS.size() > sc<size_t>(3) + DESCR_OFFSET + DEVICE_OFFSET && mouse))
|
||||
return "bind: too many args";
|
||||
|
||||
std::vector<xkb_keysym_t> KEYSYMS;
|
||||
std::vector<xkb_keysym_t> MODS;
|
||||
std::vector<KeybindKey> KEYSYMS;
|
||||
std::vector<KeybindKey> MODS;
|
||||
|
||||
if (multiKey) {
|
||||
for (const auto& splitKey : CVarList(ARGS[1], 8, '&')) {
|
||||
KEYSYMS.emplace_back(xkb_keysym_from_name(splitKey.c_str(), XKB_KEYSYM_CASE_INSENSITIVE));
|
||||
KEYSYMS.emplace_back(xkb_keysym_from_name(splitKey.c_str(), XKB_KEYSYM_CASE_INSENSITIVE), 0);
|
||||
}
|
||||
for (const auto& splitMod : CVarList(ARGS[0], 8, '&')) {
|
||||
MODS.emplace_back(xkb_keysym_from_name(splitMod.c_str(), XKB_KEYSYM_CASE_INSENSITIVE));
|
||||
MODS.emplace_back(xkb_keysym_from_name(splitMod.c_str(), XKB_KEYSYM_CASE_INSENSITIVE), 0);
|
||||
}
|
||||
}
|
||||
const auto MOD = g_pKeybindManager->stringToModMask(ARGS[0]);
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
#include "../../../devices/IKeyboard.hpp"
|
||||
#include "../../../managers/eventLoop/EventLoopManager.hpp"
|
||||
|
||||
#include <hyprutils/string/Numeric.hpp>
|
||||
#include <hyprutils/string/String.hpp>
|
||||
#include <hyprutils/string/VarList.hpp>
|
||||
|
||||
|
|
@ -46,12 +47,12 @@ static bool isSymSpecial(std::string_view sv) {
|
|||
}
|
||||
|
||||
static std::expected<void, std::string> parseKeyString(SKeybind& kb, std::string_view sv) {
|
||||
bool modsEnded = false, specialSym = false;
|
||||
CVarList2 vl(sv, 0, '+', true);
|
||||
bool modsEnded = false, specialSym = false;
|
||||
CVarList2 vl(sv, 0, '+', true);
|
||||
|
||||
uint32_t modMask = 0;
|
||||
std::vector<xkb_keysym_t> keysyms;
|
||||
std::string lastKeyArg;
|
||||
uint32_t modMask = 0;
|
||||
std::vector<std::pair<xkb_keysym_t, xkb_keycode_t>> keysyms;
|
||||
std::string lastKeyArg;
|
||||
|
||||
if (sv == "catchall") {
|
||||
kb.catchAll = true;
|
||||
|
|
@ -86,6 +87,16 @@ static std::expected<void, std::string> parseKeyString(SKeybind& kb, std::string
|
|||
continue;
|
||||
}
|
||||
|
||||
if (arg.starts_with("code:") && isNumber(std::string{arg.substr(5)})) {
|
||||
auto res = strToNumber<uint32_t>(arg.substr(5));
|
||||
|
||||
if (!res)
|
||||
return std::unexpected(std::format("Invalid keycode: \"{}\".", arg));
|
||||
|
||||
keysyms.emplace_back(XKB_KEY_NoSymbol, xkb_keycode_t{*res});
|
||||
continue;
|
||||
}
|
||||
|
||||
auto sym = xkb_keysym_from_name(std::string{arg}.c_str(), XKB_KEYSYM_CASE_INSENSITIVE);
|
||||
|
||||
if (sym == XKB_KEY_NoSymbol) {
|
||||
|
|
@ -99,7 +110,7 @@ static std::expected<void, std::string> parseKeyString(SKeybind& kb, std::string
|
|||
}
|
||||
|
||||
lastKeyArg = arg;
|
||||
keysyms.emplace_back(sym);
|
||||
keysyms.emplace_back(sym, 0);
|
||||
}
|
||||
|
||||
kb.modmask = modMask;
|
||||
|
|
|
|||
|
|
@ -505,25 +505,44 @@ void CKeybindManager::onSwitchOffEvent(const std::string& switchName) {
|
|||
handleKeybinds(0, SPressedKeyWithMods{.keyName = "switch:off:" + switchName}, true, nullptr, nullptr);
|
||||
}
|
||||
|
||||
eMultiKeyCase CKeybindManager::mkKeysymSetMatches(const std::vector<xkb_keysym_t> keybindKeysyms, const std::set<xkb_keysym_t> pressedKeysyms) {
|
||||
// Returns whether two sets of keysyms are equal, partially equal, or not
|
||||
// matching. (Partially matching means that pressed is a subset of bound)
|
||||
eMultiKeyCase CKeybindManager::mkKeysymSetMatches(const std::vector<KeybindKey>& keybindKeysyms, const std::set<KeybindKey>& pressedKeysyms) {
|
||||
// Returns whether the bound and pressed keys match fully, partially, or not at all.
|
||||
// KeybindKey stores {keysym, keycode}; either non-zero field matching is enough.
|
||||
|
||||
std::set<xkb_keysym_t> boundKeysNotPressed;
|
||||
std::set<xkb_keysym_t> pressedKeysNotBound;
|
||||
const auto MATCHES = [](const KeybindKey& lhs, const KeybindKey& rhs) {
|
||||
return (lhs.first != 0 && rhs.first != 0 && lhs.first == rhs.first) || (lhs.second != 0 && rhs.second != 0 && lhs.second == rhs.second);
|
||||
};
|
||||
|
||||
std::set<xkb_keysym_t> symsKb;
|
||||
for (const auto& k : keybindKeysyms) {
|
||||
symsKb.emplace(k);
|
||||
std::vector<KeybindKey> pressed{pressedKeysyms.begin(), pressedKeysyms.end()};
|
||||
std::vector<int> boundForPressed(pressed.size(), -1);
|
||||
|
||||
const auto tryMatch = [&](auto&& self, const size_t boundIdx, std::vector<uint8_t>& seen) -> bool {
|
||||
for (size_t pressedIdx = 0; pressedIdx < pressed.size(); ++pressedIdx) {
|
||||
if (seen[pressedIdx] || !MATCHES(keybindKeysyms[boundIdx], pressed[pressedIdx]))
|
||||
continue;
|
||||
|
||||
seen[pressedIdx] = true;
|
||||
|
||||
if (boundForPressed[pressedIdx] == -1 || self(self, static_cast<size_t>(boundForPressed[pressedIdx]), seen)) {
|
||||
boundForPressed[pressedIdx] = static_cast<int>(boundIdx);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
size_t matches = 0;
|
||||
for (size_t boundIdx = 0; boundIdx < keybindKeysyms.size(); ++boundIdx) {
|
||||
std::vector<uint8_t> seen(pressed.size(), false);
|
||||
if (tryMatch(tryMatch, boundIdx, seen))
|
||||
++matches;
|
||||
}
|
||||
|
||||
std::ranges::set_difference(symsKb, pressedKeysyms, std::inserter(boundKeysNotPressed, boundKeysNotPressed.begin()));
|
||||
std::ranges::set_difference(pressedKeysyms, symsKb, std::inserter(pressedKeysNotBound, pressedKeysNotBound.begin()));
|
||||
|
||||
if (boundKeysNotPressed.empty() && pressedKeysNotBound.empty())
|
||||
if (matches == keybindKeysyms.size() && matches == pressed.size())
|
||||
return MK_FULL_MATCH;
|
||||
|
||||
if (!boundKeysNotPressed.empty() && pressedKeysNotBound.empty())
|
||||
if (matches > 0 || (pressed.empty() && !keybindKeysyms.empty()))
|
||||
return MK_PARTIAL_MATCH;
|
||||
|
||||
return MK_NO_MATCH;
|
||||
|
|
@ -556,14 +575,14 @@ SDispatchResult CKeybindManager::handleKeybinds(const uint32_t modmask, const SP
|
|||
if (key.keysym != 0) {
|
||||
if (pressed) {
|
||||
if (keycodeToModifier(key.keycode))
|
||||
m_mkMods.insert(key.keysym);
|
||||
m_mkMods.emplace(key.keysym, key.keycode);
|
||||
else
|
||||
m_mkKeys.insert(key.keysym);
|
||||
m_mkKeys.emplace(key.keysym, key.keycode);
|
||||
} else {
|
||||
if (keycodeToModifier(key.keycode))
|
||||
m_mkMods.erase(key.keysym);
|
||||
std::erase_if(m_mkMods, [&key](const auto& e) { return e.first == key.keysym || e.second == key.keycode; });
|
||||
else
|
||||
m_mkKeys.erase(key.keysym);
|
||||
std::erase_if(m_mkKeys, [&key](const auto& e) { return e.first == key.keysym || e.second == key.keycode; });
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -615,7 +634,8 @@ SDispatchResult CKeybindManager::handleKeybinds(const uint32_t modmask, const SP
|
|||
// check for just the one match
|
||||
// this is also needed for multi-key binds so that SUPER + A + K can't
|
||||
// be actuated by SUPER + K + A
|
||||
if (key.keysym != k->sMkKeys.back())
|
||||
auto& back = k->sMkKeys.back();
|
||||
if (key.keysym != back.first && key.keycode != back.second)
|
||||
continue;
|
||||
} else if (!key.keyName.empty()) {
|
||||
if (key.keyName != k->key)
|
||||
|
|
|
|||
|
|
@ -25,13 +25,15 @@ struct SSubmap {
|
|||
}
|
||||
};
|
||||
|
||||
using KeybindKey = std::pair<xkb_keysym_t, xkb_keycode_t>;
|
||||
|
||||
struct SKeybind {
|
||||
std::string key = "";
|
||||
std::vector<xkb_keysym_t> sMkKeys = {};
|
||||
std::vector<KeybindKey> sMkKeys = {};
|
||||
uint32_t keycode = 0;
|
||||
bool catchAll = false;
|
||||
uint32_t modmask = 0;
|
||||
std::vector<xkb_keysym_t> sMkMods = {};
|
||||
std::vector<KeybindKey> sMkMods = {};
|
||||
std::string handler = "";
|
||||
std::string arg = "";
|
||||
bool locked = false;
|
||||
|
|
@ -156,10 +158,10 @@ class CKeybindManager {
|
|||
|
||||
SDispatchResult handleKeybinds(const uint32_t, const SPressedKeyWithMods&, bool, SP<IKeyboard>, SP<IHID>);
|
||||
|
||||
std::set<xkb_keysym_t> m_mkKeys = {};
|
||||
std::set<xkb_keysym_t> m_mkMods = {};
|
||||
std::set<KeybindKey> m_mkKeys = {};
|
||||
std::set<KeybindKey> m_mkMods = {};
|
||||
eMultiKeyCase mkBindMatches(const SP<SKeybind>);
|
||||
eMultiKeyCase mkKeysymSetMatches(const std::vector<xkb_keysym_t>, const std::set<xkb_keysym_t>);
|
||||
eMultiKeyCase mkKeysymSetMatches(const std::vector<KeybindKey>&, const std::set<KeybindKey>&);
|
||||
|
||||
bool handleInternalKeybinds(xkb_keysym_t);
|
||||
bool handleVT(xkb_keysym_t);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue