#include "RawInputDetector.h" #include #include #include #ifdef _WIN32 #include #include 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(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(message); if (msg->message == WM_INPUT) handleRawInput(reinterpret_cast(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(-1) || size == 0) return; if (m_rawBuffer.size() < static_cast(size)) m_rawBuffer.resize(static_cast(size)); if (GetRawInputData(rawInput, RID_INPUT, m_rawBuffer.data(), &size, sizeof(RAWINPUTHEADER)) == static_cast(-1)) return; const auto *input = reinterpret_cast(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(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(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(code); } } // namespace FancyInput