Skip to content

Commit

Permalink
keybinds: Add the 'catchall' keyword that matches all keys (hyprwm#4930)
Browse files Browse the repository at this point in the history
* Add the 'catchall' keyword that matches all keys

This keyword can be used to define arbitrary keybinds. The only special
behavior that it exhibits is that it matches every key, including
modifier keys. Any flags still apply normally.

This commit also fixes an issue that keys bound via the code:KEYCODE
format were not unbound correctly.

* Disallow catchall keybinds outside of submaps

A catchall keybind outside a submap would prevent essentially all key
events from going through to applications and would be difficult to
remove again.
  • Loading branch information
Cu3PO42 authored and lisuke committed Apr 15, 2024
1 parent b2fdc99 commit d461327
Show file tree
Hide file tree
Showing 4 changed files with 39 additions and 24 deletions.
30 changes: 21 additions & 9 deletions src/config/ConfigManager.cpp
Expand Up @@ -1732,6 +1732,17 @@ std::optional<std::string> CConfigManager::handleAnimation(const std::string& co
return {};
}

SParsedKey parseKey(const std::string& key) {
if (isNumber(key) && std::stoi(key) > 9)
return {.keycode = std::stoi(key)};
else if (key.starts_with("code:") && isNumber(key.substr(5)))
return {.keycode = std::stoi(key.substr(5))};
else if (key == "catchall")
return {.catchAll = true};
else
return {.key = key};
}

std::optional<std::string> CConfigManager::handleBind(const std::string& command, const std::string& value) {
// example:
// bind[fl]=SUPER,G,exec,dmenu_run <args>
Expand Down Expand Up @@ -1807,14 +1818,15 @@ std::optional<std::string> CConfigManager::handleBind(const std::string& command
}

if (KEY != "") {
if (isNumber(KEY) && std::stoi(KEY) > 9)
g_pKeybindManager->addKeybind(
SKeybind{"", std::stoi(KEY), MOD, HANDLER, COMMAND, locked, m_szCurrentSubmap, release, repeat, mouse, nonConsuming, transparent, ignoreMods});
else if (KEY.starts_with("code:") && isNumber(KEY.substr(5)))
g_pKeybindManager->addKeybind(
SKeybind{"", std::stoi(KEY.substr(5)), MOD, HANDLER, COMMAND, locked, m_szCurrentSubmap, release, repeat, mouse, nonConsuming, transparent, ignoreMods});
else
g_pKeybindManager->addKeybind(SKeybind{KEY, 0, MOD, HANDLER, COMMAND, locked, m_szCurrentSubmap, release, repeat, mouse, nonConsuming, transparent, ignoreMods});
SParsedKey parsedKey = parseKey(KEY);

if (parsedKey.catchAll && m_szCurrentSubmap == "") {
Debug::log(ERR, "Catchall not allowed outside of submap!");
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});
}

return {};
Expand All @@ -1825,7 +1837,7 @@ std::optional<std::string> CConfigManager::handleUnbind(const std::string& comma

const auto MOD = g_pKeybindManager->stringToModMask(ARGS[0]);

const auto KEY = ARGS[1];
const auto KEY = parseKey(ARGS[1]);

g_pKeybindManager->removeKeybind(MOD, KEY);

Expand Down
8 changes: 5 additions & 3 deletions src/debug/HyprCtl.cpp
Expand Up @@ -718,8 +718,8 @@ std::string bindsRequest(eHyprCtlOutputFormat format, std::string request) {
if (kb.nonConsuming)
ret += "n";

ret += std::format("\n\tmodmask: {}\n\tsubmap: {}\n\tkey: {}\n\tkeycode: {}\n\tdispatcher: {}\n\targ: {}\n\n", kb.modmask, kb.submap, kb.key, kb.keycode, kb.handler,
kb.arg);
ret += std::format("\n\tmodmask: {}\n\tsubmap: {}\n\tkey: {}\n\tkeycode: {}\n\tcatchall: {}\n\tdispatcher: {}\n\targ: {}\n\n", kb.modmask, kb.submap, kb.key,
kb.keycode, kb.catchAll, kb.handler, kb.arg);
}
} else {
// json
Expand All @@ -737,11 +737,13 @@ std::string bindsRequest(eHyprCtlOutputFormat format, std::string request) {
"submap": "{}",
"key": "{}",
"keycode": {},
"catch_all": {},
"dispatcher": "{}",
"arg": "{}"
}},)#",
kb.locked ? "true" : "false", kb.mouse ? "true" : "false", kb.release ? "true" : "false", kb.repeat ? "true" : "false", kb.nonConsuming ? "true" : "false",
kb.modmask, escapeJSONStrings(kb.submap), escapeJSONStrings(kb.key), kb.keycode, escapeJSONStrings(kb.handler), escapeJSONStrings(kb.arg));
kb.modmask, escapeJSONStrings(kb.submap), escapeJSONStrings(kb.key), kb.keycode, kb.catchAll ? "true" : "false", escapeJSONStrings(kb.handler),
escapeJSONStrings(kb.arg));
}
trimTrailingComma(ret);
ret += "]";
Expand Down
16 changes: 5 additions & 11 deletions src/managers/KeybindManager.cpp
Expand Up @@ -95,18 +95,9 @@ void CKeybindManager::addKeybind(SKeybind kb) {
m_pActiveKeybind = nullptr;
}

void CKeybindManager::removeKeybind(uint32_t mod, const std::string& key) {
void CKeybindManager::removeKeybind(uint32_t mod, const SParsedKey& key) {
for (auto it = m_lKeybinds.begin(); it != m_lKeybinds.end(); ++it) {
if (isNumber(key) && std::stoi(key) > 9) {
const uint32_t KEYNUM = std::stoi(key);

if (it->modmask == mod && it->keycode == KEYNUM) {
it = m_lKeybinds.erase(it);

if (it == m_lKeybinds.end())
break;
}
} else if (it->modmask == mod && it->key == key) {
if (it->modmask == mod && it->key == key.key && it->keycode == key.keycode && it->catchAll == key.catchAll) {
it = m_lKeybinds.erase(it);

if (it == m_lKeybinds.end())
Expand Down Expand Up @@ -527,6 +518,9 @@ bool CKeybindManager::handleKeybinds(const uint32_t modmask, const SPressedKeyWi
} else if (k.keycode != 0) {
if (key.keycode != k.keycode)
continue;
} else if (k.catchAll) {
if (found)
continue;
} else {
// oMg such performance hit!!11!
// this little maneouver is gonna cost us 4µs
Expand Down
9 changes: 8 additions & 1 deletion src/managers/KeybindManager.hpp
Expand Up @@ -13,6 +13,7 @@ class CPluginSystem;
struct SKeybind {
std::string key = "";
uint32_t keycode = 0;
bool catchAll = false;
uint32_t modmask = 0;
std::string handler = "";
std::string arg = "";
Expand Down Expand Up @@ -44,6 +45,12 @@ struct SPressedKeyWithMods {
bool sent = false;
};

struct SParsedKey {
std::string key = "";
uint32_t keycode = 0;
bool catchAll = false;
};

class CKeybindManager {
public:
CKeybindManager();
Expand All @@ -57,7 +64,7 @@ class CKeybindManager {
void onSwitchOffEvent(const std::string&);

void addKeybind(SKeybind);
void removeKeybind(uint32_t, const std::string&);
void removeKeybind(uint32_t, const SParsedKey&);
uint32_t stringToModMask(std::string);
uint32_t keycodeToModifier(xkb_keycode_t);
void clearKeybinds();
Expand Down

0 comments on commit d461327

Please sign in to comment.