summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGabriel Ford <gabe@fordltc.net>2024-05-16 10:48:30 +0000
committerGitHub <noreply@github.com>2024-05-16 11:48:30 +0100
commitd693c448366131de9e5729690a6d894fd1adff5e (patch)
treeaf0dd6ec05e04f4b66323063e353c95f62886991 /src
parentca0833c9ed6d6034b360d0ea83f3ac942ffb45b5 (diff)
keybinds: add keybind combos and add Left and Right mod distinction. (#5966)
Diffstat (limited to 'src')
-rw-r--r--src/config/ConfigManager.cpp24
-rw-r--r--src/managers/KeybindManager.cpp84
-rw-r--r--src/managers/KeybindManager.hpp43
3 files changed, 117 insertions, 34 deletions
diff --git a/src/config/ConfigManager.cpp b/src/config/ConfigManager.cpp
index b0c44408..ac5c101e 100644
--- a/src/config/ConfigManager.cpp
+++ b/src/config/ConfigManager.cpp
@@ -6,6 +6,7 @@
#include "helpers/VarList.hpp"
#include "../protocols/LayerShell.hpp"
+#include <cstdint>
#include <string.h>
#include <string>
#include <sys/stat.h>
@@ -18,6 +19,7 @@
#include <iostream>
#include <sstream>
#include <ranges>
+#include <xkbcommon/xkbcommon.h>
extern "C" char** environ;
@@ -1908,6 +1910,7 @@ std::optional<std::string> CConfigManager::handleBind(const std::string& command
bool nonConsuming = false;
bool transparent = false;
bool ignoreMods = false;
+ bool multiKey = false;
const auto BINDARGS = command.substr(4);
for (auto& arg : BINDARGS) {
@@ -1925,6 +1928,8 @@ std::optional<std::string> CConfigManager::handleBind(const std::string& command
transparent = true;
} else if (arg == 'i') {
ignoreMods = true;
+ } else if (arg == 's') {
+ multiKey = true;
} else {
return "bind: invalid flag";
}
@@ -1943,10 +1948,21 @@ std::optional<std::string> CConfigManager::handleBind(const std::string& command
else if ((ARGS.size() > 4 && !mouse) || (ARGS.size() > 3 && mouse))
return "bind: too many args";
+ std::set<xkb_keysym_t> KEYSYMS;
+ std::set<xkb_keysym_t> MODS;
+
+ if (multiKey) {
+ for (auto splitKey : CVarList(ARGS[1], 8, '&')) {
+ KEYSYMS.insert(xkb_keysym_from_name(splitKey.c_str(), XKB_KEYSYM_CASE_INSENSITIVE));
+ }
+ for (auto splitMod : CVarList(ARGS[0], 8, '&')) {
+ MODS.insert(xkb_keysym_from_name(splitMod.c_str(), XKB_KEYSYM_CASE_INSENSITIVE));
+ }
+ }
const auto MOD = g_pKeybindManager->stringToModMask(ARGS[0]);
const auto MODSTR = ARGS[0];
- const auto KEY = ARGS[1];
+ const auto KEY = multiKey ? "" : ARGS[1];
auto HANDLER = ARGS[2];
@@ -1970,7 +1986,7 @@ std::optional<std::string> CConfigManager::handleBind(const std::string& command
return "Invalid mod, requested mod \"" + MODSTR + "\" is not a valid mod.";
}
- if (KEY != "") {
+ if ((KEY != "") || multiKey) {
SParsedKey parsedKey = parseKey(KEY);
if (parsedKey.catchAll && m_szCurrentSubmap == "") {
@@ -1978,8 +1994,8 @@ std::optional<std::string> CConfigManager::handleBind(const std::string& command
return "Invalid catchall, catchall keybinds are only allowed in submaps.";
}
- g_pKeybindManager->addKeybind(SKeybind{parsedKey.key, parsedKey.keycode, parsedKey.catchAll, MOD, HANDLER, COMMAND, locked, m_szCurrentSubmap, release, repeat, mouse,
- nonConsuming, transparent, ignoreMods});
+ g_pKeybindManager->addKeybind(SKeybind{parsedKey.key, KEYSYMS, parsedKey.keycode, parsedKey.catchAll, MOD, MODS, HANDLER, COMMAND, locked, m_szCurrentSubmap, release,
+ repeat, mouse, nonConsuming, transparent, ignoreMods, multiKey});
}
return {};
diff --git a/src/managers/KeybindManager.cpp b/src/managers/KeybindManager.cpp
index ead83fa8..ea064031 100644
--- a/src/managers/KeybindManager.cpp
+++ b/src/managers/KeybindManager.cpp
@@ -8,7 +8,9 @@
#include "../devices/IKeyboard.hpp"
#include "../managers/SeatManager.hpp"
+#include <optional>
#include <regex>
+#include <string>
#include <tuple>
#include <sys/ioctl.h>
@@ -536,8 +538,48 @@ int repeatKeyHandler(void* data) {
return 0;
}
+eMultiKeyCase CKeybindManager::mkKeysymSetMatches(const std::set<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)
+
+ std::set<xkb_keysym_t> boundKeysNotPressed;
+ std::set<xkb_keysym_t> pressedKeysNotBound;
+
+ std::set_difference(keybindKeysyms.begin(), keybindKeysyms.end(), pressedKeysyms.begin(), pressedKeysyms.end(),
+ std::inserter(boundKeysNotPressed, boundKeysNotPressed.begin()));
+ std::set_difference(pressedKeysyms.begin(), pressedKeysyms.end(), keybindKeysyms.begin(), keybindKeysyms.end(),
+ std::inserter(pressedKeysNotBound, pressedKeysNotBound.begin()));
+
+ if (boundKeysNotPressed.empty() && pressedKeysNotBound.empty())
+ return MK_FULL_MATCH;
+
+ if (boundKeysNotPressed.size() && pressedKeysNotBound.empty())
+ return MK_PARTIAL_MATCH;
+
+ return MK_NO_MATCH;
+}
+
+eMultiKeyCase CKeybindManager::mkBindMatches(const SKeybind keybind) {
+ if (mkKeysymSetMatches(keybind.sMkMods, m_sMkMods) != MK_FULL_MATCH)
+ return MK_NO_MATCH;
+
+ return mkKeysymSetMatches(keybind.sMkKeys, m_sMkKeys);
+}
+
bool CKeybindManager::handleKeybinds(const uint32_t modmask, const SPressedKeyWithMods& key, bool pressed) {
- bool found = false;
+ bool found = false;
+
+ if (pressed) {
+ if (keycodeToModifier(key.keycode))
+ m_sMkMods.insert(key.keysym);
+ else
+ m_sMkKeys.insert(key.keysym);
+ } else {
+ if (keycodeToModifier(key.keycode))
+ m_sMkMods.erase(key.keysym);
+ else
+ m_sMkKeys.erase(key.keysym);
+ }
static auto PDISABLEINHIBIT = CConfigValue<Hyprlang::INT>("binds:disable_keybind_grabbing");
@@ -559,7 +601,13 @@ bool CKeybindManager::handleKeybinds(const uint32_t modmask, const SPressedKeyWi
if (!IGNORECONDITIONS && ((modmask != k.modmask && !k.ignoreMods) || k.submap != m_szCurrentSelectedSubmap || k.shadowed))
continue;
- if (!key.keyName.empty()) {
+ if (k.multiKey) {
+ switch (mkBindMatches(k)) {
+ case MK_NO_MATCH: continue;
+ case MK_PARTIAL_MATCH: found = true; continue;
+ case MK_FULL_MATCH: found = true;
+ }
+ } else if (!key.keyName.empty()) {
if (key.keyName != k.key)
continue;
} else if (k.keycode != 0) {
@@ -672,25 +720,29 @@ void CKeybindManager::shadowKeybinds(const xkb_keysym_t& doesntHave, const uint3
if (k.handler == "global" || k.transparent)
continue; // can't be shadowed
- const auto KBKEY = xkb_keysym_from_name(k.key.c_str(), XKB_KEYSYM_CASE_INSENSITIVE);
- const auto KBKEYUPPER = xkb_keysym_to_upper(KBKEY);
+ if (k.multiKey && (mkBindMatches(k) == MK_FULL_MATCH))
+ shadow = true;
+ else {
+ const auto KBKEY = xkb_keysym_from_name(k.key.c_str(), XKB_KEYSYM_CASE_INSENSITIVE);
+ const auto KBKEYUPPER = xkb_keysym_to_upper(KBKEY);
- for (auto& pk : m_dPressedKeys) {
- if ((pk.keysym != 0 && (pk.keysym == KBKEY || pk.keysym == KBKEYUPPER))) {
- shadow = true;
+ for (auto& pk : m_dPressedKeys) {
+ if ((pk.keysym != 0 && (pk.keysym == KBKEY || pk.keysym == KBKEYUPPER))) {
+ shadow = true;
- if (pk.keysym == doesntHave && doesntHave != 0) {
- shadow = false;
- break;
+ if (pk.keysym == doesntHave && doesntHave != 0) {
+ shadow = false;
+ break;
+ }
}
- }
- if (pk.keycode != 0 && pk.keycode == k.keycode) {
- shadow = true;
+ if (pk.keycode != 0 && pk.keycode == k.keycode) {
+ shadow = true;
- if (pk.keycode == doesntHaveCode && doesntHaveCode != 0) {
- shadow = false;
- break;
+ if (pk.keycode == doesntHaveCode && doesntHaveCode != 0) {
+ shadow = false;
+ break;
+ }
}
}
}
diff --git a/src/managers/KeybindManager.hpp b/src/managers/KeybindManager.hpp
index da98a749..a2025bb0 100644
--- a/src/managers/KeybindManager.hpp
+++ b/src/managers/KeybindManager.hpp
@@ -2,6 +2,7 @@
#include "../defines.hpp"
#include <deque>
+#include <set>
#include "../Compositor.hpp"
#include <unordered_map>
#include <functional>
@@ -13,20 +14,23 @@ class CPluginSystem;
class IKeyboard;
struct SKeybind {
- std::string key = "";
- uint32_t keycode = 0;
- bool catchAll = false;
- uint32_t modmask = 0;
- std::string handler = "";
- std::string arg = "";
- bool locked = false;
- std::string submap = "";
- bool release = false;
- bool repeat = false;
- bool mouse = false;
- bool nonConsuming = false;
- bool transparent = false;
- bool ignoreMods = false;
+ std::string key = "";
+ std::set<xkb_keysym_t> sMkKeys = {};
+ uint32_t keycode = 0;
+ bool catchAll = false;
+ uint32_t modmask = 0;
+ std::set<xkb_keysym_t> sMkMods = {};
+ std::string handler = "";
+ std::string arg = "";
+ bool locked = false;
+ std::string submap = "";
+ bool release = false;
+ bool repeat = false;
+ bool mouse = false;
+ bool nonConsuming = false;
+ bool transparent = false;
+ bool ignoreMods = false;
+ bool multiKey = false;
// DO NOT INITIALIZE
bool shadowed = false;
@@ -57,6 +61,12 @@ struct SParsedKey {
bool catchAll = false;
};
+enum eMultiKeyCase {
+ MK_NO_MATCH = 0,
+ MK_PARTIAL_MATCH,
+ MK_FULL_MATCH
+};
+
class CKeybindManager {
public:
CKeybindManager();
@@ -105,6 +115,11 @@ class CKeybindManager {
bool handleKeybinds(const uint32_t, const SPressedKeyWithMods&, bool);
+ std::set<xkb_keysym_t> m_sMkKeys = {};
+ std::set<xkb_keysym_t> m_sMkMods = {};
+ eMultiKeyCase mkBindMatches(const SKeybind);
+ eMultiKeyCase mkKeysymSetMatches(const std::set<xkb_keysym_t>, const std::set<xkb_keysym_t>);
+
bool handleInternalKeybinds(xkb_keysym_t);
bool handleVT(xkb_keysym_t);