Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow Multiple Simultaneous keys in a Keybind and add Left and Right mod distinction. #5966

Merged
merged 25 commits into from
May 16, 2024
Merged
Show file tree
Hide file tree
Changes from all 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 @@ -6,6 +6,7 @@
#include "helpers/VarList.hpp"
#include "../protocols/LayerShell.hpp"

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

extern "C" char** environ;

Expand Down Expand Up @@ -1904,6 +1906,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 @@ -1921,6 +1924,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 @@ -1939,10 +1944,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 @@ -1966,16 +1982,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
84 changes: 68 additions & 16 deletions src/managers/KeybindManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@
#include "../devices/IKeyboard.hpp"
#include "../managers/SeatManager.hpp"

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

#include <sys/ioctl.h>
Expand Down Expand Up @@ -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");

Expand All @@ -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) {
Expand Down Expand Up @@ -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;
}
}
}
}
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
Loading