mirror of
https://github.com/hyprwm/Hyprland.git
synced 2025-08-18 21:43:48 -07:00
keybinds: add keybind combos and add Left and Right mod distinction. (#5966)
This commit is contained in:
@@ -6,6 +6,7 @@
|
|||||||
#include "helpers/VarList.hpp"
|
#include "helpers/VarList.hpp"
|
||||||
#include "../protocols/LayerShell.hpp"
|
#include "../protocols/LayerShell.hpp"
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
@@ -18,6 +19,7 @@
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <ranges>
|
#include <ranges>
|
||||||
|
#include <xkbcommon/xkbcommon.h>
|
||||||
|
|
||||||
extern "C" char** environ;
|
extern "C" char** environ;
|
||||||
|
|
||||||
@@ -1908,6 +1910,7 @@ std::optional<std::string> CConfigManager::handleBind(const std::string& command
|
|||||||
bool nonConsuming = false;
|
bool nonConsuming = false;
|
||||||
bool transparent = false;
|
bool transparent = false;
|
||||||
bool ignoreMods = false;
|
bool ignoreMods = false;
|
||||||
|
bool multiKey = false;
|
||||||
const auto BINDARGS = command.substr(4);
|
const auto BINDARGS = command.substr(4);
|
||||||
|
|
||||||
for (auto& arg : BINDARGS) {
|
for (auto& arg : BINDARGS) {
|
||||||
@@ -1925,6 +1928,8 @@ std::optional<std::string> CConfigManager::handleBind(const std::string& command
|
|||||||
transparent = true;
|
transparent = true;
|
||||||
} else if (arg == 'i') {
|
} else if (arg == 'i') {
|
||||||
ignoreMods = true;
|
ignoreMods = true;
|
||||||
|
} else if (arg == 's') {
|
||||||
|
multiKey = true;
|
||||||
} else {
|
} else {
|
||||||
return "bind: invalid flag";
|
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))
|
else if ((ARGS.size() > 4 && !mouse) || (ARGS.size() > 3 && mouse))
|
||||||
return "bind: too many args";
|
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 MOD = g_pKeybindManager->stringToModMask(ARGS[0]);
|
||||||
const auto MODSTR = ARGS[0];
|
const auto MODSTR = ARGS[0];
|
||||||
|
|
||||||
const auto KEY = ARGS[1];
|
const auto KEY = multiKey ? "" : ARGS[1];
|
||||||
|
|
||||||
auto HANDLER = ARGS[2];
|
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.";
|
return "Invalid mod, requested mod \"" + MODSTR + "\" is not a valid mod.";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (KEY != "") {
|
if ((KEY != "") || multiKey) {
|
||||||
SParsedKey parsedKey = parseKey(KEY);
|
SParsedKey parsedKey = parseKey(KEY);
|
||||||
|
|
||||||
if (parsedKey.catchAll && m_szCurrentSubmap == "") {
|
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.";
|
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,
|
g_pKeybindManager->addKeybind(SKeybind{parsedKey.key, KEYSYMS, parsedKey.keycode, parsedKey.catchAll, MOD, MODS, HANDLER, COMMAND, locked, m_szCurrentSubmap, release,
|
||||||
nonConsuming, transparent, ignoreMods});
|
repeat, mouse, nonConsuming, transparent, ignoreMods, multiKey});
|
||||||
}
|
}
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
|
@@ -8,7 +8,9 @@
|
|||||||
#include "../devices/IKeyboard.hpp"
|
#include "../devices/IKeyboard.hpp"
|
||||||
#include "../managers/SeatManager.hpp"
|
#include "../managers/SeatManager.hpp"
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
#include <regex>
|
#include <regex>
|
||||||
|
#include <string>
|
||||||
#include <tuple>
|
#include <tuple>
|
||||||
|
|
||||||
#include <sys/ioctl.h>
|
#include <sys/ioctl.h>
|
||||||
@@ -536,8 +538,48 @@ int repeatKeyHandler(void* data) {
|
|||||||
return 0;
|
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 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");
|
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))
|
if (!IGNORECONDITIONS && ((modmask != k.modmask && !k.ignoreMods) || k.submap != m_szCurrentSelectedSubmap || k.shadowed))
|
||||||
continue;
|
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)
|
if (key.keyName != k.key)
|
||||||
continue;
|
continue;
|
||||||
} else if (k.keycode != 0) {
|
} 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)
|
if (k.handler == "global" || k.transparent)
|
||||||
continue; // can't be shadowed
|
continue; // can't be shadowed
|
||||||
|
|
||||||
const auto KBKEY = xkb_keysym_from_name(k.key.c_str(), XKB_KEYSYM_CASE_INSENSITIVE);
|
if (k.multiKey && (mkBindMatches(k) == MK_FULL_MATCH))
|
||||||
const auto KBKEYUPPER = xkb_keysym_to_upper(KBKEY);
|
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) {
|
for (auto& pk : m_dPressedKeys) {
|
||||||
if ((pk.keysym != 0 && (pk.keysym == KBKEY || pk.keysym == KBKEYUPPER))) {
|
if ((pk.keysym != 0 && (pk.keysym == KBKEY || pk.keysym == KBKEYUPPER))) {
|
||||||
shadow = true;
|
shadow = true;
|
||||||
|
|
||||||
if (pk.keysym == doesntHave && doesntHave != 0) {
|
if (pk.keysym == doesntHave && doesntHave != 0) {
|
||||||
shadow = false;
|
shadow = false;
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (pk.keycode != 0 && pk.keycode == k.keycode) {
|
if (pk.keycode != 0 && pk.keycode == k.keycode) {
|
||||||
shadow = true;
|
shadow = true;
|
||||||
|
|
||||||
if (pk.keycode == doesntHaveCode && doesntHaveCode != 0) {
|
if (pk.keycode == doesntHaveCode && doesntHaveCode != 0) {
|
||||||
shadow = false;
|
shadow = false;
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include "../defines.hpp"
|
#include "../defines.hpp"
|
||||||
#include <deque>
|
#include <deque>
|
||||||
|
#include <set>
|
||||||
#include "../Compositor.hpp"
|
#include "../Compositor.hpp"
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
@@ -13,20 +14,23 @@ class CPluginSystem;
|
|||||||
class IKeyboard;
|
class IKeyboard;
|
||||||
|
|
||||||
struct SKeybind {
|
struct SKeybind {
|
||||||
std::string key = "";
|
std::string key = "";
|
||||||
uint32_t keycode = 0;
|
std::set<xkb_keysym_t> sMkKeys = {};
|
||||||
bool catchAll = false;
|
uint32_t keycode = 0;
|
||||||
uint32_t modmask = 0;
|
bool catchAll = false;
|
||||||
std::string handler = "";
|
uint32_t modmask = 0;
|
||||||
std::string arg = "";
|
std::set<xkb_keysym_t> sMkMods = {};
|
||||||
bool locked = false;
|
std::string handler = "";
|
||||||
std::string submap = "";
|
std::string arg = "";
|
||||||
bool release = false;
|
bool locked = false;
|
||||||
bool repeat = false;
|
std::string submap = "";
|
||||||
bool mouse = false;
|
bool release = false;
|
||||||
bool nonConsuming = false;
|
bool repeat = false;
|
||||||
bool transparent = false;
|
bool mouse = false;
|
||||||
bool ignoreMods = false;
|
bool nonConsuming = false;
|
||||||
|
bool transparent = false;
|
||||||
|
bool ignoreMods = false;
|
||||||
|
bool multiKey = false;
|
||||||
|
|
||||||
// DO NOT INITIALIZE
|
// DO NOT INITIALIZE
|
||||||
bool shadowed = false;
|
bool shadowed = false;
|
||||||
@@ -57,6 +61,12 @@ struct SParsedKey {
|
|||||||
bool catchAll = false;
|
bool catchAll = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum eMultiKeyCase {
|
||||||
|
MK_NO_MATCH = 0,
|
||||||
|
MK_PARTIAL_MATCH,
|
||||||
|
MK_FULL_MATCH
|
||||||
|
};
|
||||||
|
|
||||||
class CKeybindManager {
|
class CKeybindManager {
|
||||||
public:
|
public:
|
||||||
CKeybindManager();
|
CKeybindManager();
|
||||||
@@ -105,6 +115,11 @@ class CKeybindManager {
|
|||||||
|
|
||||||
bool handleKeybinds(const uint32_t, const SPressedKeyWithMods&, bool);
|
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 handleInternalKeybinds(xkb_keysym_t);
|
||||||
bool handleVT(xkb_keysym_t);
|
bool handleVT(xkb_keysym_t);
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user