如何在没有定时器的低级键盘挂钩中检测单个空格键按下

问题描述 投票:0回答:1

我的手臂骨折了,正在为 Windows 开发一款单手键盘应用程序,该应用程序会在按住空格键时镜像键盘布局,从而允许单手打字。镜像功能可以使用计时器来操作,但这并不理想 - 如果在按住空格键后快速按下镜像键,镜像功能会变得缓慢并且会留下空格。

有更好的方法来处理这个用例吗?我尝试使用 lParam 的 KBDLLHOOKSTRUCT 结构 中的标志,但我找不到区分单次按键和重复按键的方法。我还尝试过忽略空格键按下的输入,并在只需按一下时发送输入,但我遇到了它不起作用的问题,可能是由于无限循环。

任何正确方向的帮助或指示将不胜感激。

#include <windows.h>
#include <iostream>
#include <chrono>
#include <map>

using Clock = std::chrono::high_resolution_clock;
std::chrono::time_point<Clock> spacePressedTime;
bool spaceHeld = false;
std::map<DWORD, DWORD> keyMappings;

bool SendMirroredKeyPress(UINT vkCode);
void InitializeKeyMappings();

LRESULT CALLBACK LowLevelKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam) {
    if (nCode == HC_ACTION) {
        auto kbdStruct = *((KBDLLHOOKSTRUCT*)lParam);
        if (kbdStruct.vkCode == VK_SPACE) {
            switch (wParam) {
            case WM_KEYDOWN:
                if (!spaceHeld) { // Check if this is the first event of space bar press
                    spacePressedTime = Clock::now();
                    spaceHeld = true; // Prevent re-entry for this press
                }
                return 1; // Block this event to prevent default space input
            case WM_KEYUP:
                if (spaceHeld) {
                    auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(Clock::now() - spacePressedTime).count();
                    if (elapsed < 200) { // Threshold for distinguishing tap from hold
                        // It was a tap, simulate a space key press
                        INPUT input[2] = {};
                        input[0].type = INPUT_KEYBOARD;
                        input[0].ki.wVk = VK_SPACE;
                        input[1].type = INPUT_KEYBOARD;
                        input[1].ki.wVk = VK_SPACE;
                        input[1].ki.dwFlags = KEYEVENTF_KEYUP;
                        SendInput(2, input, sizeof(INPUT));
                        std::cout << "Space pressed normally.\n";
                    }
                    else {
                        // It was a hold, so enter or stay in mirror mode without inserting a space
                        std::cout << "Leaving mirror mode.\n";
                    }
                    spaceHeld = false; // Reset for the next press
                    return 1; // Block the event to prevent default handling
                }
                break;
            }
        }
        else {
            // For other keys, check if space is held (mirror mode active) and handle accordingly
            if (spaceHeld && (wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN)) {
                if (SendMirroredKeyPress(kbdStruct.vkCode)) {
                    return 1; // Block the original key press
                }
            }
        }
    }
    return CallNextHookEx(NULL, nCode, wParam, lParam);
}

bool SendMirroredKeyPress(UINT vkCode) {
    auto it = keyMappings.find(vkCode);
    if (it != keyMappings.end()) {
        INPUT input[1] = {};
        input[0].type = INPUT_KEYBOARD;
        input[0].ki.wVk = it->second; // Mirrored virtual key code

        // Send the mirrored key press
        SendInput(1, input, sizeof(INPUT));

        // Indicate that the original key press should be blocked
        return true;
    }
    // No mirroring needed for this key
    return false;
}

static void InitializeKeyMappings() {
    keyMappings[0x4E] = 0x42; // N to B as example
}

int main() {
    InitializeKeyMappings();

    HHOOK hhkLowLevelKybd = SetWindowsHookEx(WH_KEYBOARD_LL, LowLevelKeyboardProc, 0, 0);
    std::cout << "Hook set. Press and hold space to enter mirror mode. Tap space for normal input. N should switch to b in mirror mode.\n";

    MSG msg;
    while (!GetMessage(&msg, NULL, 0, 0)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    UnhookWindowsHookEx(hhkLowLevelKybd);
    return 0;
}
c++ windows winapi
1个回答
0
投票

正如我在评论中所说,区分单次按键和重复按键的一种方法是维护一个变量,指示最后按下的键是否是空格键。当按下其他键时该变量将被重置,并且您可以在按下空格键时检查该变量是否重复。例如,请参阅

我的答案
的变量static BOOL H = FALSE;

© www.soinside.com 2019 - 2024. All rights reserved.