基本输入检测

This commit is contained in:
2026-09-03 17:08:55 +08:00
parent bd1f884187
commit 856baa697e
7 changed files with 531 additions and 48 deletions
+243
View File
@@ -0,0 +1,243 @@
#include "RawInputDetector.h"
#include <QCoreApplication>
#include <QDebug>
#include <QGuiApplication>
#ifdef _WIN32
#include <windows.h>
#include <hidusage.h>
namespace {
constexpr UINT kRidInput = 0x10000003;
constexpr USHORT kE0Flag = 0x02;
constexpr USHORT kMouseMoveAbsolute = 0x0001;
constexpr USHORT kMouseLeftDown = 0x0001;
constexpr USHORT kMouseLeftUp = 0x0002;
constexpr USHORT kMouseRightDown = 0x0004;
constexpr USHORT kMouseRightUp = 0x0008;
constexpr USHORT kMouseMiddleDown = 0x0010;
constexpr USHORT kMouseMiddleUp = 0x0020;
constexpr USHORT kMouseX1Down = 0x0040;
constexpr USHORT kMouseX1Up = 0x0080;
constexpr USHORT kMouseX2Down = 0x0100;
constexpr USHORT kMouseX2Up = 0x0200;
constexpr USHORT kMouseWheel = 0x0400;
}
#endif
namespace FancyInput {
RawInputDetector::RawInputDetector(QObject *parent)
: QObject(parent)
{
m_rawBuffer.resize(64);
}
RawInputDetector::~RawInputDetector()
{
stop();
}
bool RawInputDetector::start(QWindow *targetWindow)
{
#ifdef _WIN32
if (m_running || targetWindow == nullptr || targetWindow->winId() == 0)
return false;
if (!registerDevices(reinterpret_cast<HWND>(targetWindow->winId())))
return false;
m_targetWindow = targetWindow;
m_pressedKeys.clear();
QCoreApplication::instance()->installNativeEventFilter(this);
m_running = true;
return true;
#else
Q_UNUSED(targetWindow)
return false;
#endif
}
void RawInputDetector::stop()
{
#ifdef _WIN32
if (!m_running)
return;
QCoreApplication::instance()->removeNativeEventFilter(this);
unregisterDevices();
m_pressedKeys.clear();
m_targetWindow = nullptr;
m_running = false;
#endif
}
bool RawInputDetector::nativeEventFilter(const QByteArray &eventType,
void *message,
qintptr *result)
{
Q_UNUSED(result)
#ifdef _WIN32
if (!m_running || eventType != "windows_generic_MSG" || message == nullptr)
return false;
const MSG *msg = static_cast<const MSG *>(message);
if (msg->message == WM_INPUT)
handleRawInput(reinterpret_cast<HRAWINPUT>(msg->lParam));
#else
Q_UNUSED(eventType)
Q_UNUSED(message)
#endif
return false;
}
#ifdef _WIN32
bool RawInputDetector::registerDevices(HWND targetHandle)
{
RAWINPUTDEVICE devices[2] = {};
devices[0].usUsagePage = HID_USAGE_PAGE_GENERIC;
devices[0].usUsage = HID_USAGE_GENERIC_KEYBOARD;
devices[0].dwFlags = RIDEV_INPUTSINK;
devices[0].hwndTarget = targetHandle;
devices[1].usUsagePage = HID_USAGE_PAGE_GENERIC;
devices[1].usUsage = HID_USAGE_GENERIC_MOUSE;
devices[1].dwFlags = RIDEV_INPUTSINK;
devices[1].hwndTarget = targetHandle;
return RegisterRawInputDevices(devices, 2, sizeof(RAWINPUTDEVICE)) != FALSE;
}
void RawInputDetector::unregisterDevices()
{
RAWINPUTDEVICE devices[2] = {};
devices[0].usUsagePage = HID_USAGE_PAGE_GENERIC;
devices[0].usUsage = HID_USAGE_GENERIC_KEYBOARD;
devices[0].dwFlags = RIDEV_REMOVE;
devices[1].usUsagePage = HID_USAGE_PAGE_GENERIC;
devices[1].usUsage = HID_USAGE_GENERIC_MOUSE;
devices[1].dwFlags = RIDEV_REMOVE;
RegisterRawInputDevices(devices, 2, sizeof(RAWINPUTDEVICE));
}
void RawInputDetector::handleRawInput(HRAWINPUT rawInput)
{
UINT size = 0;
if (GetRawInputData(rawInput, RID_INPUT, nullptr, &size,
sizeof(RAWINPUTHEADER)) == static_cast<UINT>(-1) || size == 0)
return;
if (m_rawBuffer.size() < static_cast<qsizetype>(size))
m_rawBuffer.resize(static_cast<qsizetype>(size));
if (GetRawInputData(rawInput, RID_INPUT, m_rawBuffer.data(), &size,
sizeof(RAWINPUTHEADER)) == static_cast<UINT>(-1))
return;
const auto *input = reinterpret_cast<const RAWINPUT *>(m_rawBuffer.constData());
if (input->header.dwType == RIM_TYPEKEYBOARD)
handleKeyboard(input->data.keyboard);
else if (input->header.dwType == RIM_TYPEMOUSE)
handleMouse(input->data.mouse);
}
void RawInputDetector::handleKeyboard(const RAWKEYBOARD &keyboard)
{
const bool keyDown = keyboard.Message == WM_KEYDOWN || keyboard.Message == WM_SYSKEYDOWN;
const bool keyUp = keyboard.Message == WM_KEYUP || keyboard.Message == WM_SYSKEYUP;
if (!keyDown && !keyUp)
return;
const bool extended = (keyboard.Flags & kE0Flag) != 0;
const quint16 virtualKey = keyboard.VKey;
const FipKeys key = mapKey(virtualKey, keyboard.MakeCode, extended);
const FipRawKeys rawKey = mapRawKey(virtualKey, keyboard.MakeCode, extended);
if (keyDown) {
if (m_pressedKeys.contains(virtualKey))
return;
m_pressedKeys.insert(virtualKey);
}
if (keyUp)
m_pressedKeys.remove(virtualKey);
const bool ctrl = (GetAsyncKeyState(VK_LCONTROL) & 0x8000) || (GetAsyncKeyState(VK_RCONTROL) & 0x8000);
const bool shift = (GetAsyncKeyState(VK_LSHIFT) & 0x8000) || (GetAsyncKeyState(VK_RSHIFT) & 0x8000);
const bool alt = (GetAsyncKeyState(VK_LMENU) & 0x8000) || (GetAsyncKeyState(VK_RMENU) & 0x8000);
const bool win = (GetAsyncKeyState(VK_LWIN) & 0x8000) || (GetAsyncKeyState(VK_RWIN) & 0x8000);
const bool numLock = (GetKeyState(VK_NUMLOCK) & 1) != 0;
const bool capsLock = (GetKeyState(VK_CAPITAL) & 1) != 0;
const bool scrollLock = (GetKeyState(VK_SCROLL) & 1) != 0;
if (keyDown)
emit keyPressed(key, rawKey, virtualKey, keyboard.MakeCode, extended, ctrl, shift, alt, win, numLock, capsLock, scrollLock);
else
emit keyReleased(key, rawKey, virtualKey, keyboard.MakeCode, extended, ctrl, shift, alt, win, numLock, capsLock, scrollLock);
}
void RawInputDetector::handleMouse(const RAWMOUSE &mouse)
{
const USHORT flags = mouse.usButtonFlags;
if (flags & kMouseLeftDown) emit mouseButtonChanged(MouseCodeType::Left, true);
if (flags & kMouseLeftUp) emit mouseButtonChanged(MouseCodeType::Left, false);
if (flags & kMouseRightDown) emit mouseButtonChanged(MouseCodeType::Right, true);
if (flags & kMouseRightUp) emit mouseButtonChanged(MouseCodeType::Right, false);
if (flags & kMouseMiddleDown) emit mouseButtonChanged(MouseCodeType::Middle, true);
if (flags & kMouseMiddleUp) emit mouseButtonChanged(MouseCodeType::Middle, false);
if (flags & kMouseX1Down) emit mouseButtonChanged(MouseCodeType::XButton1, true);
if (flags & kMouseX1Up) emit mouseButtonChanged(MouseCodeType::XButton1, false);
if (flags & kMouseX2Down) emit mouseButtonChanged(MouseCodeType::XButton2, true);
if (flags & kMouseX2Up) emit mouseButtonChanged(MouseCodeType::XButton2, false);
if (flags & kMouseWheel)
emit mouseWheelMoved(static_cast<qint16>(mouse.usButtonData));
if ((mouse.usFlags & kMouseMoveAbsolute) == 0 && (mouse.lLastX != 0 || mouse.lLastY != 0))
emit mouseMoved(mouse.lLastX, mouse.lLastY);
}
#endif
FipKeys RawInputDetector::mapKey(quint16 virtualKey, quint16 scanCode, bool extended)
{
if (virtualKey == VK_PAUSE) return FipKeys::Pause;
if (virtualKey == VK_SNAPSHOT) return FipKeys::PrintScreen;
if (virtualKey == VK_RETURN && scanCode == 0x1C) return extended ? FipKeys::NumpadEnter : FipKeys::Enter;
if (virtualKey == VK_CONTROL && scanCode == 0x1D) return extended ? FipKeys::RControlKey : FipKeys::LControlKey;
if (virtualKey == VK_MENU && scanCode == 0x38) return extended ? FipKeys::RMenu : FipKeys::LMenu;
if (virtualKey == VK_SHIFT) {
if (scanCode == 0x2A) return FipKeys::LShiftKey;
if (scanCode == 0x36) return FipKeys::RShiftKey;
}
if (virtualKey == VK_LWIN) return FipKeys::LWin;
if (virtualKey == VK_RWIN) return FipKeys::RWin;
if (virtualKey == VK_APPS) return FipKeys::Apps;
if (extended) {
switch (scanCode) {
case 0x48: return FipKeys::Up;
case 0x4B: return FipKeys::Left;
case 0x4D: return FipKeys::Right;
case 0x50: return FipKeys::Down;
case 0x47: return FipKeys::Home;
case 0x4F: return FipKeys::End;
case 0x49: return FipKeys::PageUp;
case 0x51: return FipKeys::PageDown;
case 0x52: return FipKeys::Insert;
case 0x53: return FipKeys::Delete;
case 0x35: return FipKeys::Divide;
default: break;
}
}
return static_cast<FipKeys>(virtualKey);
}
FipRawKeys RawInputDetector::mapRawKey(quint16 virtualKey, quint16 scanCode, bool extended)
{
if (virtualKey == VK_PAUSE) return FipRawKeys::Pause;
if (virtualKey == VK_SNAPSHOT) return FipRawKeys::PrintScreen;
if (virtualKey == VK_CONTROL && scanCode == 0x1D) return extended ? FipRawKeys::RControlKey : FipRawKeys::LControlKey;
if (virtualKey == VK_MENU && scanCode == 0x38) return extended ? FipRawKeys::RMenu : FipRawKeys::LMenu;
if (virtualKey == VK_RETURN && scanCode == 0x1C) return extended ? FipRawKeys::NumpadEnter : FipRawKeys::Enter;
if (virtualKey == VK_LWIN) return FipRawKeys::LWin;
if (virtualKey == VK_RWIN) return FipRawKeys::RWin;
if (virtualKey == VK_APPS) return FipRawKeys::Apps;
const quint32 code = extended ? (0xE000u | scanCode) : scanCode;
return static_cast<FipRawKeys>(code);
}
} // namespace FancyInput