mirror of
https://github.com/hyprwm/Hyprland.git
synced 2025-10-29 19:34:47 +00:00
keybinds: fix repeat and long press keybinds release (#11863)
This commit is contained in:
parent
17e77e0407
commit
73f06434a4
@ -120,6 +120,7 @@ class CTestMouse : public IPointer {
|
||||
};
|
||||
|
||||
SP<CTestMouse> g_mouse;
|
||||
SP<CTestKeyboard> 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();
|
||||
}
|
||||
@ -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");
|
||||
|
||||
|
||||
420
hyprtester/src/tests/main/keybinds.cpp
Normal file
420
hyprtester/src/tests/main/keybinds.cpp
Normal file
@ -0,0 +1,420 @@
|
||||
#include <filesystem>
|
||||
#include <thread>
|
||||
#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<CProcess> 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)
|
||||
@ -9,10 +9,15 @@
|
||||
using namespace Hyprutils::OS;
|
||||
using namespace Hyprutils::Memory;
|
||||
|
||||
CUniquePointer<CProcess> Tests::spawnKitty(const std::string& class_) {
|
||||
CUniquePointer<CProcess> Tests::spawnKitty(const std::string& class_, const std::vector<std::string> args) {
|
||||
const auto COUNT_BEFORE = windowCount();
|
||||
|
||||
CUniquePointer<CProcess> kitty = makeUnique<CProcess>("kitty", class_.empty() ? std::vector<std::string>{} : std::vector<std::string>{"--class", class_});
|
||||
std::vector<std::string> programArgs = args;
|
||||
if (!class_.empty()) {
|
||||
programArgs.insert(programArgs.begin(), "--class");
|
||||
programArgs.insert(programArgs.begin() + 1, class_);
|
||||
}
|
||||
CUniquePointer<CProcess> kitty = makeUnique<CProcess>("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();
|
||||
}
|
||||
|
||||
@ -8,10 +8,11 @@
|
||||
|
||||
//NOLINTNEXTLINE
|
||||
namespace Tests {
|
||||
Hyprutils::Memory::CUniquePointer<Hyprutils::OS::CProcess> spawnKitty(const std::string& class_ = "");
|
||||
Hyprutils::Memory::CUniquePointer<Hyprutils::OS::CProcess> spawnKitty(const std::string& class_ = "", const std::vector<std::string> 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);
|
||||
};
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user