diff --git a/hyprtester/plugin/src/main.cpp b/hyprtester/plugin/src/main.cpp index 2b1d443a..07f90e9f 100644 --- a/hyprtester/plugin/src/main.cpp +++ b/hyprtester/plugin/src/main.cpp @@ -120,6 +120,7 @@ class CTestMouse : public IPointer { }; SP g_mouse; +SP g_keyboard; static SDispatchResult pressAlt(std::string in) { g_pInputManager->m_lastMods = in == "1" ? HL_MODIFIER_ALT : 0; @@ -220,6 +221,30 @@ static SDispatchResult scroll(std::string in) { return {}; } +static SDispatchResult keybind(std::string in) { + CVarList data(in); + // 0 = release, 1 = press + bool press; + // See src/devices/IKeyboard.hpp : eKeyboardModifiers for modifier bitmasks + // 0 = none, eKeyboardModifiers is shifted to start at 1 + uint32_t modifier; + // keycode + uint32_t key; + try { + press = std::stoul(data[0]) == 1; + modifier = std::stoul(data[1]); + key = std::stoul(data[2]) - 8; // xkb offset + } catch (...) { return {.success = false, .error = "invalid input"}; } + + uint32_t modifierMask = 0; + if (modifier > 0) + modifierMask = 1 << (modifier - 1); + g_pInputManager->m_lastMods = modifierMask; + g_keyboard->sendKey(key, press); + + return {}; +} + APICALL EXPORT PLUGIN_DESCRIPTION_INFO PLUGIN_INIT(HANDLE handle) { PHANDLE = handle; @@ -229,15 +254,22 @@ APICALL EXPORT PLUGIN_DESCRIPTION_INFO PLUGIN_INIT(HANDLE handle) { HyprlandAPI::addDispatcherV2(PHANDLE, "plugin:test:alt", ::pressAlt); HyprlandAPI::addDispatcherV2(PHANDLE, "plugin:test:gesture", ::simulateGesture); HyprlandAPI::addDispatcherV2(PHANDLE, "plugin:test:scroll", ::scroll); + HyprlandAPI::addDispatcherV2(PHANDLE, "plugin:test:keybind", ::keybind); // init mouse g_mouse = CTestMouse::create(false); g_pInputManager->newMouse(g_mouse); + // init keyboard + g_keyboard = CTestKeyboard::create(false); + g_pInputManager->newKeyboard(g_keyboard); + return {"hyprtestplugin", "hyprtestplugin", "Vaxry", "1.0"}; } APICALL EXPORT void PLUGIN_EXIT() { g_mouse->destroy(); g_mouse.reset(); -} + g_keyboard->destroy(); + g_keyboard.reset(); +} \ No newline at end of file diff --git a/hyprtester/src/tests/main/exec.cpp b/hyprtester/src/tests/main/exec.cpp index a475ac0d..fd42cf06 100644 --- a/hyprtester/src/tests/main/exec.cpp +++ b/hyprtester/src/tests/main/exec.cpp @@ -15,16 +15,6 @@ using namespace Hyprutils::Memory; #define UP CUniquePointer #define SP CSharedPointer -static std::string execAndGet(const std::string& cmd) { - CProcess proc("/bin/sh", {"-c", cmd}); - - if (!proc.runSync()) { - return "error"; - } - - return proc.stdOut(); -} - static bool test() { NLog::log("{}Testing process spawning", Colors::GREEN); @@ -33,7 +23,7 @@ static bool test() { OK(getFromSocket("/dispatch exec sleep 1")); // Ensure that sleep is our child - const std::string sleepPidS = execAndGet("pgrep sleep"); + const std::string sleepPidS = Tests::execAndGet("pgrep sleep"); pid_t sleepPid; try { sleepPid = std::stoull(sleepPidS); @@ -42,7 +32,7 @@ static bool test() { return false; } - const std::string sleepParentComm = execAndGet("cat \"/proc/$(ps -o ppid:1= -p " + sleepPidS + ")/comm\""); + const std::string sleepParentComm = Tests::execAndGet("cat \"/proc/$(ps -o ppid:1= -p " + sleepPidS + ")/comm\""); NLog::log("{}Expecting that sleep's parent is Hyprland", Colors::YELLOW); EXPECT_CONTAINS(sleepParentComm, "Hyprland"); diff --git a/hyprtester/src/tests/main/keybinds.cpp b/hyprtester/src/tests/main/keybinds.cpp new file mode 100644 index 00000000..4d5bd700 --- /dev/null +++ b/hyprtester/src/tests/main/keybinds.cpp @@ -0,0 +1,420 @@ +#include +#include +#include "../../shared.hpp" +#include "../../hyprctlCompat.hpp" +#include "../shared.hpp" +#include "tests.hpp" + +using namespace Hyprutils::OS; +using namespace Hyprutils::Memory; + +static int ret = 0; +static std::string flagFile = "/tmp/hyprtester-keybinds.txt"; + +static void clearFlag() { + std::filesystem::remove(flagFile); +} + +static bool checkFlag() { + bool exists = std::filesystem::exists(flagFile); + clearFlag(); + return exists; +} + +static std::string readKittyOutput() { + std::string output = Tests::execAndGet("kitten @ --to unix:/tmp/hyprtester-kitty.sock get-text --extent all"); + // chop off shell prompt + std::size_t pos = output.rfind("$"); + if (pos != std::string::npos) { + pos += 1; + if (pos < output.size()) + output.erase(0, pos); + } + // NLog::log("Kitty output: '{}'", output); + return output; +} + +static void awaitKittyPrompt() { + // wait until we see the shell prompt, meaning it's ready for test inputs + for (int i = 0; i < 10; i++) { + std::string output = Tests::execAndGet("kitten @ --to unix:/tmp/hyprtester-kitty.sock get-text --extent all"); + if (output.rfind("$") == std::string::npos) { + std::this_thread::sleep_for(std::chrono::milliseconds(200)); + continue; + } + return; + } + NLog::log("{}Error: timed out waiting for kitty prompt", Colors::RED); +} + +static CUniquePointer spawnRemoteControlKitty() { + auto kittyProc = Tests::spawnKitty("keybinds_test", {"-o", "allow_remote_control=yes", "--listen-on", "unix:/tmp/hyprtester-kitty.sock", "--config", "NONE", "/bin/sh"}); + // wait a bit to ensure shell prompt is sent, we are going to read the text after it + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + if (kittyProc) + awaitKittyPrompt(); + return kittyProc; +} + +static void testBind() { + EXPECT(checkFlag(), false); + EXPECT(getFromSocket("/keyword bind SUPER,Y,exec,touch " + flagFile), "ok"); + // press keybind + OK(getFromSocket("/dispatch plugin:test:keybind 1,7,29")); + // await flag + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + EXPECT(checkFlag(), true); + // release keybind + OK(getFromSocket("/dispatch plugin:test:keybind 0,0,29")); + EXPECT(getFromSocket("/keyword unbind SUPER,Y"), "ok"); +} + +static void testBindKey() { + EXPECT(checkFlag(), false); + EXPECT(getFromSocket("/keyword bind ,Y,exec,touch " + flagFile), "ok"); + // press keybind + OK(getFromSocket("/dispatch plugin:test:keybind 1,0,29")); + // await flag + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + EXPECT(checkFlag(), true); + // release keybind + OK(getFromSocket("/dispatch plugin:test:keybind 0,0,29")); + EXPECT(getFromSocket("/keyword unbind ,Y"), "ok"); +} + +static void testLongPress() { + EXPECT(checkFlag(), false); + EXPECT(getFromSocket("/keyword bindo SUPER,Y,exec,touch " + flagFile), "ok"); + EXPECT(getFromSocket("/keyword input:repeat_delay 100"), "ok"); + // press keybind + OK(getFromSocket("/dispatch plugin:test:keybind 1,7,29")); + // check no flag on short press + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + EXPECT(checkFlag(), false); + // await repeat delay + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + EXPECT(checkFlag(), true); + // release keybind + OK(getFromSocket("/dispatch plugin:test:keybind 0,0,29")); + EXPECT(getFromSocket("/keyword unbind SUPER,Y"), "ok"); +} + +static void testKeyLongPress() { + EXPECT(checkFlag(), false); + EXPECT(getFromSocket("/keyword bindo ,Y,exec,touch " + flagFile), "ok"); + EXPECT(getFromSocket("/keyword input:repeat_delay 100"), "ok"); + // press keybind + OK(getFromSocket("/dispatch plugin:test:keybind 1,0,29")); + // check no flag on short press + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + EXPECT(checkFlag(), false); + // await repeat delay + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + EXPECT(checkFlag(), true); + // release keybind + OK(getFromSocket("/dispatch plugin:test:keybind 0,0,29")); + EXPECT(getFromSocket("/keyword unbind ,Y"), "ok"); +} + +static void testLongPressRelease() { + EXPECT(checkFlag(), false); + EXPECT(getFromSocket("/keyword bindo SUPER,Y,exec,touch " + flagFile), "ok"); + EXPECT(getFromSocket("/keyword input:repeat_delay 100"), "ok"); + // press keybind + OK(getFromSocket("/dispatch plugin:test:keybind 1,7,29")); + // check no flag on short press + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + EXPECT(checkFlag(), false); + // release keybind + OK(getFromSocket("/dispatch plugin:test:keybind 0,0,29")); + // await repeat delay + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + EXPECT(checkFlag(), false); + EXPECT(getFromSocket("/keyword unbind SUPER,Y"), "ok"); +} + +static void testLongPressOnlyKeyRelease() { + EXPECT(checkFlag(), false); + EXPECT(getFromSocket("/keyword bindo SUPER,Y,exec,touch " + flagFile), "ok"); + EXPECT(getFromSocket("/keyword input:repeat_delay 100"), "ok"); + // press keybind + OK(getFromSocket("/dispatch plugin:test:keybind 1,7,29")); + // check no flag on short press + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + EXPECT(checkFlag(), false); + // release key, keep modifier + OK(getFromSocket("/dispatch plugin:test:keybind 0,7,29")); + // await repeat delay + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + EXPECT(checkFlag(), false); + OK(getFromSocket("/dispatch plugin:test:keybind 0,0,29")); + EXPECT(getFromSocket("/keyword unbind SUPER,Y"), "ok"); +} + +static void testRepeat() { + EXPECT(checkFlag(), false); + EXPECT(getFromSocket("/keyword binde SUPER,Y,exec,touch " + flagFile), "ok"); + EXPECT(getFromSocket("/keyword input:repeat_delay 100"), "ok"); + // press keybind + OK(getFromSocket("/dispatch plugin:test:keybind 1,7,29")); + // await flag + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + EXPECT(checkFlag(), true); + // await repeat delay + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + EXPECT(checkFlag(), true); + // check that it continues repeating + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + EXPECT(checkFlag(), true); + // release keybind + OK(getFromSocket("/dispatch plugin:test:keybind 0,0,29")); + EXPECT(getFromSocket("/keyword unbind SUPER,Y"), "ok"); +} + +static void testKeyRepeat() { + EXPECT(checkFlag(), false); + EXPECT(getFromSocket("/keyword binde ,Y,exec,touch " + flagFile), "ok"); + EXPECT(getFromSocket("/keyword input:repeat_delay 100"), "ok"); + // press keybind + OK(getFromSocket("/dispatch plugin:test:keybind 1,0,29")); + // await flag + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + EXPECT(checkFlag(), true); + // await repeat delay + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + EXPECT(checkFlag(), true); + // check that it continues repeating + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + EXPECT(checkFlag(), true); + // release keybind + OK(getFromSocket("/dispatch plugin:test:keybind 0,0,29")); + EXPECT(getFromSocket("/keyword unbind ,Y"), "ok"); +} + +static void testRepeatRelease() { + EXPECT(checkFlag(), false); + EXPECT(getFromSocket("/keyword binde SUPER,Y,exec,touch " + flagFile), "ok"); + EXPECT(getFromSocket("/keyword input:repeat_delay 100"), "ok"); + // press keybind + OK(getFromSocket("/dispatch plugin:test:keybind 1,7,29")); + // await flag + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + EXPECT(checkFlag(), true); + // release keybind + OK(getFromSocket("/dispatch plugin:test:keybind 0,0,29")); + // await repeat delay + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + EXPECT(checkFlag(), false); + // check that it is not repeating + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + EXPECT(checkFlag(), false); + EXPECT(getFromSocket("/keyword unbind SUPER,Y"), "ok"); +} + +static void testRepeatOnlyKeyRelease() { + EXPECT(checkFlag(), false); + EXPECT(getFromSocket("/keyword binde SUPER,Y,exec,touch " + flagFile), "ok"); + EXPECT(getFromSocket("/keyword input:repeat_delay 100"), "ok"); + // press keybind + OK(getFromSocket("/dispatch plugin:test:keybind 1,7,29")); + // await flag + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + EXPECT(checkFlag(), true); + // release key, keep modifier + OK(getFromSocket("/dispatch plugin:test:keybind 0,7,29")); + // await repeat delay + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + EXPECT(checkFlag(), false); + // check that it is not repeating + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + EXPECT(checkFlag(), false); + OK(getFromSocket("/dispatch plugin:test:keybind 0,0,29")); + EXPECT(getFromSocket("/keyword unbind SUPER,Y"), "ok"); +} + +static void testShortcutBind() { + auto kittyProc = spawnRemoteControlKitty(); + if (!kittyProc) { + NLog::log("{}Error: kitty did not spawn", Colors::RED); + ret = 1; + return; + } + EXPECT(getFromSocket("/dispatch focuswindow class:keybinds_test"), "ok"); + EXPECT(getFromSocket("/keyword bind SUPER,Y,sendshortcut,,q,"), "ok"); + // press keybind + OK(getFromSocket("/dispatch plugin:test:keybind 1,7,29")); + // release keybind + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + OK(getFromSocket("/dispatch plugin:test:keybind 0,0,29")); + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + const std::string output = readKittyOutput(); + EXPECT_COUNT_STRING(output, "y", 0); + EXPECT_COUNT_STRING(output, "q", 1); + EXPECT(getFromSocket("/keyword unbind SUPER,Y"), "ok"); + Tests::killAllWindows(); +} + +static void testShortcutBindKey() { + auto kittyProc = spawnRemoteControlKitty(); + if (!kittyProc) { + NLog::log("{}Error: kitty did not spawn", Colors::RED); + ret = 1; + return; + } + EXPECT(getFromSocket("/dispatch focuswindow class:keybinds_test"), "ok"); + EXPECT(getFromSocket("/keyword bind ,Y,sendshortcut,,q,"), "ok"); + // press keybind + OK(getFromSocket("/dispatch plugin:test:keybind 1,0,29")); + // release keybind + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + OK(getFromSocket("/dispatch plugin:test:keybind 0,0,29")); + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + const std::string output = readKittyOutput(); + EXPECT_COUNT_STRING(output, "y", 0); + EXPECT_COUNT_STRING(output, "q", 1); + EXPECT(getFromSocket("/keyword unbind ,Y"), "ok"); + Tests::killAllWindows(); +} + +static void testShortcutLongPress() { + auto kittyProc = spawnRemoteControlKitty(); + if (!kittyProc) { + NLog::log("{}Error: kitty did not spawn", Colors::RED); + ret = 1; + return; + } + EXPECT(getFromSocket("/dispatch focuswindow class:keybinds_test"), "ok"); + EXPECT(getFromSocket("/keyword bindo SUPER,Y,sendshortcut,,q,"), "ok"); + EXPECT(getFromSocket("/keyword input:repeat_delay 100"), "ok"); + EXPECT(getFromSocket("/keyword input:repeat_rate 10"), "ok"); + // press keybind + OK(getFromSocket("/dispatch plugin:test:keybind 1,7,29")); + // await repeat delay + std::this_thread::sleep_for(std::chrono::milliseconds(150)); + OK(getFromSocket("/dispatch plugin:test:keybind 0,0,29")); + std::this_thread::sleep_for(std::chrono::milliseconds(150)); + const std::string output = readKittyOutput(); + int yCount = Tests::countOccurrences(output, "y"); + // sometimes 1, sometimes 2, not sure why + // keybind press sends 1 y immediately + // then repeat triggers, sending 1 y + // final release stop repeats, and shouldn't send any more + EXPECT(true, yCount == 1 || yCount == 2); + EXPECT_COUNT_STRING(output, "q", 1); + EXPECT(getFromSocket("/keyword unbind SUPER,Y"), "ok"); + Tests::killAllWindows(); +} + +static void testShortcutLongPressKeyRelease() { + auto kittyProc = spawnRemoteControlKitty(); + if (!kittyProc) { + NLog::log("{}Error: kitty did not spawn", Colors::RED); + ret = 1; + return; + } + EXPECT(getFromSocket("/dispatch focuswindow class:keybinds_test"), "ok"); + EXPECT(getFromSocket("/keyword bindo SUPER,Y,sendshortcut,,q,"), "ok"); + EXPECT(getFromSocket("/keyword input:repeat_delay 100"), "ok"); + EXPECT(getFromSocket("/keyword input:repeat_rate 10"), "ok"); + // press keybind + OK(getFromSocket("/dispatch plugin:test:keybind 1,7,29")); + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + // release key, keep modifier + OK(getFromSocket("/dispatch plugin:test:keybind 0,7,29")); + // await repeat delay + std::this_thread::sleep_for(std::chrono::milliseconds(150)); + const std::string output = readKittyOutput(); + EXPECT_COUNT_STRING(output, "y", 1); + EXPECT_COUNT_STRING(output, "q", 0); + OK(getFromSocket("/dispatch plugin:test:keybind 0,0,29")); + EXPECT(getFromSocket("/keyword unbind SUPER,Y"), "ok"); + Tests::killAllWindows(); +} + +static void testShortcutRepeat() { + auto kittyProc = spawnRemoteControlKitty(); + if (!kittyProc) { + NLog::log("{}Error: kitty did not spawn", Colors::RED); + ret = 1; + return; + } + EXPECT(getFromSocket("/dispatch focuswindow class:keybinds_test"), "ok"); + EXPECT(getFromSocket("/keyword binde SUPER,Y,sendshortcut,,q,"), "ok"); + EXPECT(getFromSocket("/keyword input:repeat_rate 5"), "ok"); + EXPECT(getFromSocket("/keyword input:repeat_delay 200"), "ok"); + // press keybind + OK(getFromSocket("/dispatch plugin:test:keybind 1,7,29")); + // await repeat + std::this_thread::sleep_for(std::chrono::milliseconds(210)); + // release keybind + OK(getFromSocket("/dispatch plugin:test:keybind 0,0,29")); + std::this_thread::sleep_for(std::chrono::milliseconds(450)); + const std::string output = readKittyOutput(); + EXPECT_COUNT_STRING(output, "y", 0); + int qCount = Tests::countOccurrences(output, "q"); + // sometimes 2, sometimes 3, not sure why + // keybind press sends 1 q immediately + // then repeat triggers, sending 1 q + // final release stop repeats, and shouldn't send any more + EXPECT(true, qCount == 2 || qCount == 3); + EXPECT(getFromSocket("/keyword unbind SUPER,Y"), "ok"); + Tests::killAllWindows(); +} + +static void testShortcutRepeatKeyRelease() { + auto kittyProc = spawnRemoteControlKitty(); + if (!kittyProc) { + NLog::log("{}Error: kitty did not spawn", Colors::RED); + ret = 1; + return; + } + EXPECT(getFromSocket("/dispatch focuswindow class:keybinds_test"), "ok"); + EXPECT(getFromSocket("/keyword binde SUPER,Y,sendshortcut,,q,"), "ok"); + EXPECT(getFromSocket("/keyword input:repeat_rate 5"), "ok"); + EXPECT(getFromSocket("/keyword input:repeat_delay 200"), "ok"); + // press keybind + OK(getFromSocket("/dispatch plugin:test:keybind 1,7,29")); + std::this_thread::sleep_for(std::chrono::milliseconds(210)); + // release key, keep modifier + OK(getFromSocket("/dispatch plugin:test:keybind 0,7,29")); + // if repeat was still active, we'd get 2 more q's here + std::this_thread::sleep_for(std::chrono::milliseconds(450)); + // release modifier + const std::string output = readKittyOutput(); + EXPECT_COUNT_STRING(output, "y", 0); + int qCount = Tests::countOccurrences(output, "q"); + // sometimes 2, sometimes 3, not sure why + // keybind press sends 1 q immediately + // then repeat triggers, sending 1 q + // final release stop repeats, and shouldn't send any more + EXPECT(true, qCount == 2 || qCount == 3); + OK(getFromSocket("/dispatch plugin:test:keybind 0,0,29")); + EXPECT(getFromSocket("/keyword unbind SUPER,Y"), "ok"); + Tests::killAllWindows(); +} + +static bool test() { + NLog::log("{}Testing keybinds", Colors::GREEN); + + testBind(); + testBindKey(); + testLongPress(); + testKeyLongPress(); + testLongPressRelease(); + testLongPressOnlyKeyRelease(); + testRepeat(); + testKeyRepeat(); + testRepeatRelease(); + testRepeatOnlyKeyRelease(); + testShortcutBind(); + testShortcutBindKey(); + testShortcutLongPress(); + testShortcutLongPressKeyRelease(); + testShortcutRepeat(); + testShortcutRepeatKeyRelease(); + + clearFlag(); + return !ret; +} + +REGISTER_TEST_FN(test) diff --git a/hyprtester/src/tests/shared.cpp b/hyprtester/src/tests/shared.cpp index b7aa325c..8cdd648e 100644 --- a/hyprtester/src/tests/shared.cpp +++ b/hyprtester/src/tests/shared.cpp @@ -9,10 +9,15 @@ using namespace Hyprutils::OS; using namespace Hyprutils::Memory; -CUniquePointer Tests::spawnKitty(const std::string& class_) { +CUniquePointer Tests::spawnKitty(const std::string& class_, const std::vector args) { const auto COUNT_BEFORE = windowCount(); - CUniquePointer kitty = makeUnique("kitty", class_.empty() ? std::vector{} : std::vector{"--class", class_}); + std::vector programArgs = args; + if (!class_.empty()) { + programArgs.insert(programArgs.begin(), "--class"); + programArgs.insert(programArgs.begin() + 1, class_); + } + CUniquePointer kitty = makeUnique("kitty", programArgs); kitty->addEnv("WAYLAND_DISPLAY", WLDISPLAY); kitty->runAsync(); @@ -49,7 +54,7 @@ int Tests::countOccurrences(const std::string& in, const std::string& what) { auto pos = in.find(what); while (pos != std::string::npos) { cnt++; - pos = in.find(what, pos + what.length() - 1); + pos = in.find(what, pos + what.length()); } return cnt; @@ -90,3 +95,13 @@ void Tests::waitUntilWindowsN(int n) { } } } + +std::string Tests::execAndGet(const std::string& cmd) { + CProcess proc("/bin/sh", {"-c", cmd}); + + if (!proc.runSync()) { + return "error"; + } + + return proc.stdOut(); +} diff --git a/hyprtester/src/tests/shared.hpp b/hyprtester/src/tests/shared.hpp index ef0b9a60..fe28a69d 100644 --- a/hyprtester/src/tests/shared.hpp +++ b/hyprtester/src/tests/shared.hpp @@ -8,10 +8,11 @@ //NOLINTNEXTLINE namespace Tests { - Hyprutils::Memory::CUniquePointer spawnKitty(const std::string& class_ = ""); + Hyprutils::Memory::CUniquePointer spawnKitty(const std::string& class_ = "", const std::vector args = {}); bool processAlive(pid_t pid); int windowCount(); int countOccurrences(const std::string& in, const std::string& what); bool killAllWindows(); void waitUntilWindowsN(int n); + std::string execAndGet(const std::string& cmd); }; diff --git a/src/managers/KeybindManager.cpp b/src/managers/KeybindManager.cpp index 3120157f..58d451b3 100644 --- a/src/managers/KeybindManager.cpp +++ b/src/managers/KeybindManager.cpp @@ -757,7 +757,7 @@ SDispatchResult CKeybindManager::handleKeybinds(const uint32_t modmask, const SP continue; } - if (k->longPress) { + if (pressed && k->longPress) { const auto PACTIVEKEEB = g_pSeatManager->m_keyboard.lock(); m_longPressTimer->updateTimeout(std::chrono::milliseconds(PACTIVEKEEB->m_repeatDelay)); @@ -796,7 +796,7 @@ SDispatchResult CKeybindManager::handleKeybinds(const uint32_t modmask, const SP } } - if (k->repeat) { + if (pressed && k->repeat) { const auto KEEB = keyboard ? keyboard : g_pSeatManager->m_keyboard.lock(); m_repeatKeyRate = KEEB->m_repeatRate;