Skip to content
Merged
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
8e17ff3
Write multikey config parsing.
The-Briel-Deal May 8, 2024
e53cda2
Made it through to handleKeybind
The-Briel-Deal May 8, 2024
ab956d8
Lots on progress on handling binds.
The-Briel-Deal May 9, 2024
dee92d1
Forgot to continue out of the second loop /:
The-Briel-Deal May 9, 2024
5d360c9
Fixed handler logic.
The-Briel-Deal May 9, 2024
5376595
Whoops, forgot to add string import.
The-Briel-Deal May 9, 2024
2893235
Formatting.
The-Briel-Deal May 9, 2024
71d42ce
Format KeybindManager header.
The-Briel-Deal May 9, 2024
a67cf42
Add modkey and count matching.
The-Briel-Deal May 9, 2024
d92cb6a
Fixed a bug with mod keys.
The-Briel-Deal May 9, 2024
c332cc5
Remove unnecessary log.
The-Briel-Deal May 9, 2024
38c2336
Clean up keysetting in ConfigManager.
The-Briel-Deal May 9, 2024
0a52a7d
Order change to minimize diff.
The-Briel-Deal May 9, 2024
80a7178
Merge branch 'hyprwm:main' into multi-key-binds-v2
The-Briel-Deal May 9, 2024
e88f379
Fix miscount with modkeys.
The-Briel-Deal May 10, 2024
3d37860
Add checks for a few edge cases add key comsumption.
The-Briel-Deal May 10, 2024
012f1a7
Fix dumb race condition when pressing keys simultaneously.
The-Briel-Deal May 11, 2024
24f21ee
Break multikey functionality into another function
The-Briel-Deal May 12, 2024
5087ed6
Commiting my test changes to another branch
The-Briel-Deal May 12, 2024
eacf35b
Fix split mod problem add flag.
The-Briel-Deal May 13, 2024
2ccfb7e
Remove unneeded check for &.
The-Briel-Deal May 13, 2024
5f0f15e
Handle Keybind Shadowing.
The-Briel-Deal May 14, 2024
25b34c5
Remove {} around short ifs.
The-Briel-Deal May 14, 2024
c906d54
Merge branch 'main' into multi-key-binds-v2
The-Briel-Deal May 14, 2024
a7fc7f8
Remove {} from short if.
The-Briel-Deal May 15, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 20 additions & 4 deletions src/config/ConfigManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include "config/ConfigDataValues.hpp"
#include "helpers/VarList.hpp"

#include <cstdint>
#include <string.h>
#include <string>
#include <sys/stat.h>
Expand All @@ -17,6 +18,7 @@
#include <iostream>
#include <sstream>
#include <ranges>
#include <xkbcommon/xkbcommon.h>

extern "C" char** environ;

Expand Down Expand Up @@ -1902,6 +1904,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) {
Expand All @@ -1919,6 +1922,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";
}
Expand All @@ -1937,10 +1942,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];

Expand All @@ -1964,16 +1980,16 @@ 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 == "") {
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});
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 {};
Expand Down
51 changes: 50 additions & 1 deletion src/managers/KeybindManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@
#include "../protocols/ShortcutsInhibit.hpp"
#include "../devices/IKeyboard.hpp"

#include <optional>
#include <regex>
#include <string>
#include <tuple>

#include <sys/ioctl.h>
Expand Down Expand Up @@ -536,9 +538,50 @@ 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;

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);
}
}
if (g_pCompositor->m_sSeat.exclusiveClient)
Debug::log(LOG, "Keybind handling only locked (inhibitor)");

Expand All @@ -563,7 +606,13 @@ bool CKeybindManager::handleKeybinds(const uint32_t modmask, const SPressedKeyWi
((modmask != k.modmask && !k.ignoreMods) || (g_pCompositor->m_sSeat.exclusiveClient && !k.locked) || 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) {
Expand Down
43 changes: 29 additions & 14 deletions src/managers/KeybindManager.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#include "../defines.hpp"
#include <deque>
#include <set>
#include "../Compositor.hpp"
#include <unordered_map>
#include <functional>
Expand All @@ -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;
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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);

Expand Down