基本输入检测
This commit is contained in:
+1
-1
@@ -361,7 +361,7 @@ $env:Path = "D:/Qt/6.8.3/mingw_64/bin;D:/Qt/Tools/mingw1310_64/bin;C:\Windows\Sy
|
|||||||
|
|
||||||
**目标**:先把所有"不依赖其他代码"的通用模块落地并单测,为输入识别打基础。
|
**目标**:先把所有"不依赖其他代码"的通用模块落地并单测,为输入识别打基础。
|
||||||
|
|
||||||
- [ ] `src\core\common\Enums.h`:**全部枚举逐字迁移**(`FipKeys`/`FipRawKeys`/`KeyBoardCodeType`/`InputDevice`/`ElementType`/`FIPGamepadButtonflags`/`MoveState`/`TriggerMode` 等,数值一致)
|
- [x] `src\core\common\Enums.h`:**全部枚举逐字迁移**(`FipKeys`/`FipRawKeys`/`KeyBoardCodeType`/`InputDevice`/`ElementType`/`FIPGamepadButtonflags`/`MoveState`/`TriggerMode` 等,数值一致)
|
||||||
- [ ] `src\core\common\InputEvent.h`:`InputEvent` 结构(对应 WPF `InputArgs`),Q_GADGET 便于 QML 消费
|
- [ ] `src\core\common\InputEvent.h`:`InputEvent` 结构(对应 WPF `InputArgs`),Q_GADGET 便于 QML 消费
|
||||||
- [ ] `src\core\image\XImage.h/.cpp`:QImage 重写(Crop/ReSize/Scale/EraseCircle/Merge/PackImages/ConcatImages/通道分离合并/GIF 帧),**与 WPF XImage.cs API 一一对应**
|
- [ ] `src\core\image\XImage.h/.cpp`:QImage 重写(Crop/ReSize/Scale/EraseCircle/Merge/PackImages/ConcatImages/通道分离合并/GIF 帧),**与 WPF XImage.cs API 一一对应**
|
||||||
- [ ] `src\core\overlay\OverlayParser.h/.cpp`:overlay JSON 读写(QJsonDocument),**字段名与 WPF 完全一致**(snake_case)
|
- [ ] `src\core\overlay\OverlayParser.h/.cpp`:overlay JSON 读写(QJsonDocument),**字段名与 WPF 完全一致**(snake_case)
|
||||||
|
|||||||
@@ -0,0 +1,121 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
namespace FancyInput {
|
||||||
|
|
||||||
|
enum class GamepadBackend : std::uint8_t { SDL = 0, XInput = 1 };
|
||||||
|
enum class MousMoveMode : std::uint8_t { Absolute = 0, Relative = 1 };
|
||||||
|
enum class MouseWheelDirection : std::uint8_t { Up = 0, Down = 1 };
|
||||||
|
enum class MacroAddDirection : std::uint8_t { Right = 0, Left = 1 };
|
||||||
|
|
||||||
|
enum class InputDevice : std::uint8_t {
|
||||||
|
Keyboard = 0, MouseButton, MouseWheel, MouseMove,
|
||||||
|
XInputButton, XInputTrigger, XInputStick,
|
||||||
|
SDLButton, SDLTrigger, SDLStick, SDLGuide
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class MoveState : std::uint8_t { StartMove = 0, Moving = 1, StopMove = 2 };
|
||||||
|
|
||||||
|
enum class FIPGamepadButtonflags : std::uint16_t {
|
||||||
|
None = 0, Up = 1, Down = 2, Left = 4, Right = 8,
|
||||||
|
Start = 0x10, Select = 0x20, LS = 0x40, RS = 0x80,
|
||||||
|
LB = 0x100, RB = 0x200, A = 0x1000, B = 0x2000,
|
||||||
|
X = 0x4000, Y = 0x8000
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class ElementType : std::uint8_t {
|
||||||
|
Texture = 0, KeyboardButton, GamepadButton, MouseButton, MouseWheel,
|
||||||
|
AnalogStick, GamepadTrigger, GamepadPlayerId, DPad, MouseMovement
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class GamepadCodeType : std::uint16_t {
|
||||||
|
A = 0, B = 1, X = 2, Y = 3, Select = 4, Start = 6,
|
||||||
|
LB = 9, RB = 10, Up = 11, Down = 12, Left = 13, Right = 14,
|
||||||
|
HitBox_B = 60416, HitBox_A = 60417, HitBox_Y = 60418, HitBox_X = 60419,
|
||||||
|
HitBox_LB = 60420, HitBox_RB = 60421, HitBox_Select = 60422,
|
||||||
|
HitBox_Start = 60423, HitBox_LS = 60425, HitBox_RS = 60426,
|
||||||
|
HitBox_Left = 60427, HitBox_Right = 60428, HitBox_Up = 60429,
|
||||||
|
HitBox_Down = 60430
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class GamepadTriggerType : std::uint8_t { Left = 0, Right = 1 };
|
||||||
|
enum class GamepadThumbType : std::uint8_t { LeftThumb = 0, RightThumb = 1 };
|
||||||
|
enum class GamepadUserIdx : std::uint8_t { One = 0, Two = 1, Three = 2, Four = 3 };
|
||||||
|
enum class MouseCodeType : std::uint8_t { None = 0, Left = 1, Right = 2, Middle = 3, XButton1 = 4, XButton2 = 5 };
|
||||||
|
enum class Direction : std::uint8_t { Up = 1, Down = 2, Left = 3, Right = 4 };
|
||||||
|
enum class Side : std::uint8_t { Left = 0, Right = 1 };
|
||||||
|
enum class ExportType : std::uint8_t { PngAndJson = 0, ProjectFile = 1 };
|
||||||
|
enum class MainWindowPanelType : std::uint8_t { Read = 0, Create = 1, Manage = 2, Workshop = 3, Settings = 4 };
|
||||||
|
enum class MouseMoveType : std::uint8_t { Dot = 0, Arrow = 1 };
|
||||||
|
enum class WindowCloseType : std::uint8_t { Save = 0, Discard = 1 };
|
||||||
|
enum class ButtonState : std::uint8_t { Released = 0, Pressed = 1 };
|
||||||
|
enum class KeyBoardMappingType : std::uint8_t { Windows = 0, BIOS = 1 };
|
||||||
|
|
||||||
|
enum class KeyBoardCodeType : std::uint16_t {
|
||||||
|
None = 0, Escape = 1, D1 = 2, D2 = 3, D3 = 4, D4 = 5, D5 = 6, D6 = 7, D7 = 8, D8 = 9, D9 = 10, D0 = 11,
|
||||||
|
OemMinus = 12, Oemplus = 13, Back = 14, Tab = 15, Q = 16, W = 17, E = 18, R = 19, T = 20, Y = 21, U = 22, I = 23, O = 24, P = 25,
|
||||||
|
OemOpenBrackets = 26, OemCloseBrackets = 27, Enter = 28, LControlKey = 29, A = 30, S = 31, D = 32, F = 33, G = 34, H = 35,
|
||||||
|
J = 36, K = 37, L = 38, Oem1 = 39, Oem7 = 40, Oem3 = 41, LShiftKey = 42, Oem5 = 43, Z = 44, X = 45, C = 46, V = 47, B = 48,
|
||||||
|
N = 49, M = 50, Oemcomma = 51, OemPeriod = 52, Oem2 = 53, RShiftKey = 54, Divide = 3637, Multiply = 55, LMenu = 56, Space = 57, CapsLock = 58,
|
||||||
|
F1 = 59, F2 = 60, F3 = 61, F4 = 62, F5 = 63, F6 = 64, F7 = 65, F8 = 66, F9 = 67, F10 = 68, NumLock = 69, ScrollLock = 70,
|
||||||
|
NumPad7 = 71, NumPad8 = 72, NumPad9 = 73, Subtract = 74, NumPad4 = 75, NumPad5 = 76, NumPad6 = 77, Add = 78, NumPad1 = 79, NumPad2 = 80,
|
||||||
|
NumPad3 = 81, NumPad0 = 82, Decimal = 83, NumpadEquals = 3597, F11 = 87, F12 = 88, F13 = 91, F14 = 92, F15 = 93,
|
||||||
|
F16 = 99, F17 = 100, F18 = 101, F19 = 102, F20 = 103, F21 = 104, F22 = 105, F23 = 106, F24 = 107,
|
||||||
|
PrintScreen = 3639, Oem102 = 3654, RMenu = 3640, NumpadEnter = 3612, RControlKey = 3613,
|
||||||
|
Pause = 3653, Home = 3655, PageUp = 3657, End = 3663, PageDown = 3665, Insert = 3666, Delete = 3667,
|
||||||
|
Up = 57416, Left = 57419, Right = 57421, Down = 57424, LWin = 3675, RWin = 3676, Apps = 3677,
|
||||||
|
Power = 57438, Sleep = 57439, Wake = 57443, MediaPlay = 57378, MediaStop = 57380,
|
||||||
|
MediaPrevious = 57360, MediaNext = 57369, MediaSelect = 57453, MediaEject = 57388,
|
||||||
|
VolumeMute = 57376, VolumeUp = 57392, VolumeDown = 57390, AppMail = 57452,
|
||||||
|
AppCalculator = 57377, AppMusic = 57404, AppPictures = 57444, BrowserSearch = 57445,
|
||||||
|
BrowserHome = 57394, BrowserBack = 57450, BrowserForward = 57449, BrowserStop = 57448,
|
||||||
|
BrowserRefresh = 57447, BrowserFavorites = 57446, Katakana = 112, Underscore = 115,
|
||||||
|
Furigana = 119, Kanji = 121, Hiragana = 123, Yen = 125, NumpadComma = 126,
|
||||||
|
SunHelp = 65397, SunStop = 65400, SunProps = 65398, SunFront = 65399, SunOpen = 65396,
|
||||||
|
SunFind = 65406, SunAgain = 65401, SunUndo = 65402, SunCopy = 65404, SunInsert = 65405, SunCut = 65403
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class TriggerMode : std::uint8_t { Once = 0, Hold = 1, Toggle = 2 };
|
||||||
|
enum class TriggerTiming : std::uint8_t { Press = 0, Release = 1 };
|
||||||
|
|
||||||
|
enum class FipKeys : std::uint16_t {
|
||||||
|
None = 0x00, Escape = 0x1B, D1 = 0x31, D2 = 0x32, D3 = 0x33, D4 = 0x34, D5 = 0x35, D6 = 0x36, D7 = 0x37, D8 = 0x38, D9 = 0x39, D0 = 0x30,
|
||||||
|
OemMinus = 0xBD, Oemplus = 0xBB, Back = 0x08, Tab = 0x09, Q = 0x51, W = 0x57, E = 0x45, R = 0x52, T = 0x54, Y = 0x59, U = 0x55, I = 0x49, O = 0x4F, P = 0x50,
|
||||||
|
OemOpenBrackets = 0xDB, OemCloseBrackets = 0xDD, Enter = 0x1000, LControlKey = 0xA2, A = 0x41, S = 0x53, D = 0x44, F = 0x46, G = 0x47, H = 0x48,
|
||||||
|
J = 0x4A, K = 0x4B, L = 0x4C, Oem1 = 0xBA, Oem7 = 0xDE, Oem3 = 0xC0, Oem102 = 0xE2, LShiftKey = 0xA0, Oem5 = 0xDC, Z = 0x5A, X = 0x58, C = 0x43, V = 0x56, B = 0x42,
|
||||||
|
N = 0x4E, M = 0x4D, Oemcomma = 0xBC, OemPeriod = 0xBE, Oem2 = 0xBF, RShiftKey = 0xA1, Divide = 0x6F, Multiply = 0x6A, LMenu = 0xA4, Space = 0x20, CapsLock = 0x14,
|
||||||
|
F1 = 0x70, F2 = 0x71, F3 = 0x72, F4 = 0x73, F5 = 0x74, F6 = 0x75, F7 = 0x76, F8 = 0x77, F9 = 0x78, F10 = 0x79, NumLock = 0x90, ScrollLock = 0x91,
|
||||||
|
NumPad7 = 0x67, NumPad8 = 0x68, NumPad9 = 0x69, Subtract = 0x6D, NumPad4 = 0x64, NumPad5 = 0x65, NumPad6 = 0x66, Add = 0x6B, NumPad1 = 0x61, NumPad2 = 0x62,
|
||||||
|
NumPad3 = 0x63, NumPad0 = 0x60, Decimal = 0x6E, NumpadEquals = 0x92, F11 = 0x7A, F12 = 0x7B, F13 = 0x7C, F14 = 0x7D, F15 = 0x7E,
|
||||||
|
F16 = 0x7F, F17 = 0x80, F18 = 0x81, F19 = 0x82, F20 = 0x83, F21 = 0x84, F22 = 0x85, F23 = 0x86, F24 = 0x87,
|
||||||
|
PrintScreen = 0x2C, RMenu = 0xA5, NumpadEnter = 0x1001, RControlKey = 0xA3, Pause = 0x13,
|
||||||
|
Home = 0x24, PageUp = 0x21, End = 0x23, PageDown = 0x22, Insert = 0x2D, Delete = 0x2E, Up = 0x26, Left = 0x25, Right = 0x27, Down = 0x28,
|
||||||
|
LWin = 0x5B, RWin = 0x5C, Apps = 0x5D, Sleep = 0x5F, Wake = 0xE3, MediaNext = 0xB0, MediaPrevious = 0xB1,
|
||||||
|
MediaStop = 0xB2, MediaPlay = 0xB3, MediaSelect = 0xB5, VolumeMute = 0xAD, VolumeUp = 0xAF, VolumeDown = 0xAE,
|
||||||
|
AppMail = 0xB4, AppMusic = 0xB6, AppPictures = 0xB7, AppCalculator = 0xB7, BrowserBack = 0xA6, BrowserForward = 0xA7,
|
||||||
|
BrowserRefresh = 0xA8, BrowserStop = 0xA9, BrowserSearch = 0xAA, BrowserFavorites = 0xAB, BrowserHome = 0xAC
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class FipRawKeys : std::uint32_t {
|
||||||
|
None = 0x0000, Escape = 0x01, D1 = 0x02, D2 = 0x03, D3 = 0x04, D4 = 0x05, D5 = 0x06, D6 = 0x07, D7 = 0x08, D8 = 0x09, D9 = 0x0A, D0 = 0x0B,
|
||||||
|
OemMinus = 0x0C, Oemplus = 0x0D, Back = 0x0E, Tab = 0x0F, Q = 0x10, W = 0x11, E = 0x12, R = 0x13, T = 0x14, Y = 0x15, U = 0x16, I = 0x17, O = 0x18, P = 0x19,
|
||||||
|
OemOpenBrackets = 0x1A, OemCloseBrackets = 0x1B, Enter = 0x1C, LControlKey = 0x1D, A = 0x1E, S = 0x1F, D = 0x20, F = 0x21, G = 0x22, H = 0x23,
|
||||||
|
J = 0x24, K = 0x25, L = 0x26, Oem1 = 0x27, Oem7 = 0x28, Oem3 = 0x29, Oem102 = 0x56, LShiftKey = 0x2A, Oem5 = 0x2B, Z = 0x2C, X = 0x2D, C = 0x2E, V = 0x2F, B = 0x30,
|
||||||
|
N = 0x31, M = 0x32, Oemcomma = 0x33, OemPeriod = 0x34, Oem2 = 0x35, RShiftKey = 0x36, Divide = 0xE035, Multiply = 0x37, LMenu = 0x38, Space = 0x39, CapsLock = 0x3A,
|
||||||
|
F1 = 0x3B, F2 = 0x3C, F3 = 0x3D, F4 = 0x3E, F5 = 0x3F, F6 = 0x40, F7 = 0x41, F8 = 0x42, F9 = 0x43, F10 = 0x44, NumLock = 0x45, ScrollLock = 0x46,
|
||||||
|
NumPad7 = 0x47, NumPad8 = 0x48, NumPad9 = 0x49, Subtract = 0x4A, NumPad4 = 0x4B, NumPad5 = 0x4C, NumPad6 = 0x4D, Add = 0x4E, NumPad1 = 0x4F, NumPad2 = 0x50,
|
||||||
|
NumPad3 = 0x51, NumPad0 = 0x52, Decimal = 0x53, NumpadEquals = 0xE00D, F11 = 0x57, F12 = 0x58, F13 = 0x5B, F14 = 0x5C, F15 = 0x5D,
|
||||||
|
F16 = 0x63, F17 = 0x64, F18 = 0x65, F19 = 0x66, F20 = 0x67, F21 = 0x68, F22 = 0x69, F23 = 0x6A, F24 = 0x6B,
|
||||||
|
PrintScreen = 0xE037, RMenu = 0xE038, NumpadEnter = 0xE01C, RControlKey = 0xE01D, Pause = 0xE11D45,
|
||||||
|
Home = 0xE047, PageUp = 0xE049, End = 0xE04F, PageDown = 0xE051, Insert = 0xE052, Delete = 0xE053, Up = 0xE048, Left = 0xE04B, Right = 0xE04D, Down = 0xE050,
|
||||||
|
LWin = 0xE05B, RWin = 0xE05C, Apps = 0xE05D, Power = 0xE05E, Sleep = 0xE05F, Wake = 0xE063,
|
||||||
|
MediaPlay = 0xE022, MediaStop = 0xE024, MediaPrevious = 0xE010, MediaNext = 0xE019, MediaSelect = 0xE06D, MediaEject = 0xE02C,
|
||||||
|
VolumeMute = 0xE020, VolumeUp = 0xE030, VolumeDown = 0xE02E, AppMail = 0xE06C, AppCalculator = 0xE021, AppMusic = 0xE03C, AppPictures = 0xE064,
|
||||||
|
BrowserSearch = 0xE065, BrowserHome = 0xE032, BrowserBack = 0xE06A, BrowserForward = 0xE069, BrowserStop = 0xE068, BrowserRefresh = 0xE067, BrowserFavorites = 0xE066,
|
||||||
|
Katakana = 0x70, Underscore = 0x73, Furigana = 0x77, Kanji = 0x79, Hiragana = 0x7B, Yen = 0x7D, NumpadComma = 0x7E,
|
||||||
|
SunHelp = 0xFF75, SunStop = 0xFF78, SunProps = 0xFF76, SunFront = 0xFF77, SunOpen = 0xFF74, SunFind = 0xFF7E,
|
||||||
|
SunAgain = 0xFF79, SunUndo = 0xFF7A, SunCopy = 0xFF7C, SunInsert = 0xFF7D, SunCut = 0xFF7B
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace FancyInput
|
||||||
@@ -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
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "../common/Enums.h"
|
||||||
|
#include <QObject>
|
||||||
|
#include <QAbstractNativeEventFilter>
|
||||||
|
#include <QByteArray>
|
||||||
|
#include <QSet>
|
||||||
|
#include <QWindow>
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include <windows.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace FancyInput {
|
||||||
|
|
||||||
|
class RawInputDetector final : public QObject, public QAbstractNativeEventFilter
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit RawInputDetector(QObject *parent = nullptr);
|
||||||
|
~RawInputDetector() override;
|
||||||
|
|
||||||
|
bool start(QWindow *targetWindow);
|
||||||
|
void stop();
|
||||||
|
bool isRunning() const { return m_running; }
|
||||||
|
|
||||||
|
bool nativeEventFilter(const QByteArray &eventType, void *message,
|
||||||
|
qintptr *result) override;
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void keyPressed(FipKeys key, FipRawKeys rawKey, quint16 virtualKey,
|
||||||
|
quint16 scanCode, bool extended, bool ctrlDown,
|
||||||
|
bool shiftDown, bool altDown, bool winDown,
|
||||||
|
bool numLockOn, bool capsLockOn, bool scrollLockOn);
|
||||||
|
void keyReleased(FipKeys key, FipRawKeys rawKey, quint16 virtualKey,
|
||||||
|
quint16 scanCode, bool extended, bool ctrlDown,
|
||||||
|
bool shiftDown, bool altDown, bool winDown,
|
||||||
|
bool numLockOn, bool capsLockOn, bool scrollLockOn);
|
||||||
|
void mouseButtonChanged(MouseCodeType button, bool pressed);
|
||||||
|
void mouseWheelMoved(int delta);
|
||||||
|
void mouseMoved(int deltaX, int deltaY);
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool registerDevices(HWND targetHandle);
|
||||||
|
void unregisterDevices();
|
||||||
|
void handleRawInput(HRAWINPUT rawInput);
|
||||||
|
void handleKeyboard(const RAWKEYBOARD &keyboard);
|
||||||
|
void handleMouse(const RAWMOUSE &mouse);
|
||||||
|
|
||||||
|
static FipKeys mapKey(quint16 virtualKey, quint16 scanCode, bool extended);
|
||||||
|
static FipRawKeys mapRawKey(quint16 virtualKey, quint16 scanCode, bool extended);
|
||||||
|
|
||||||
|
QWindow *m_targetWindow = nullptr;
|
||||||
|
QSet<quint16> m_pressedKeys;
|
||||||
|
bool m_running = false;
|
||||||
|
QByteArray m_rawBuffer;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace FancyInput
|
||||||
@@ -1,6 +1,13 @@
|
|||||||
#include <QGuiApplication>
|
#include <QGuiApplication>
|
||||||
#include <QQmlApplicationEngine>
|
#include <QQmlApplicationEngine>
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
#include <QWindow>
|
||||||
|
#include "core/common/Enums.h"
|
||||||
|
#include "core/input/RawInputDetector.h"
|
||||||
|
|
||||||
|
static_assert(static_cast<std::uint16_t>(FancyInput::FipKeys::Enter) == 0x1000);
|
||||||
|
static_assert(static_cast<std::uint32_t>(FancyInput::FipRawKeys::NumpadEnter) == 0xE01C);
|
||||||
|
static_assert(static_cast<std::uint16_t>(FancyInput::FIPGamepadButtonflags::Y) == 0x8000);
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
@@ -16,5 +23,46 @@ int main(int argc, char *argv[])
|
|||||||
Qt::QueuedConnection);
|
Qt::QueuedConnection);
|
||||||
engine.loadFromModule("FancyInputQt", "MainWindow");
|
engine.loadFromModule("FancyInputQt", "MainWindow");
|
||||||
|
|
||||||
|
FancyInput::RawInputDetector rawInputDetector(&app);
|
||||||
|
if (!engine.rootObjects().isEmpty()) {
|
||||||
|
if (auto *window = qobject_cast<QWindow *>(engine.rootObjects().constFirst())) {
|
||||||
|
rawInputDetector.start(window);
|
||||||
|
QObject::connect(&rawInputDetector, &FancyInput::RawInputDetector::keyPressed,
|
||||||
|
[](FancyInput::FipKeys key, FancyInput::FipRawKeys rawKey,
|
||||||
|
quint16 virtualKey, quint16 scanCode, bool extended,
|
||||||
|
bool ctrl, bool shift, bool alt, bool win,
|
||||||
|
bool numLock, bool capsLock, bool scrollLock) {
|
||||||
|
qInfo().nospace() << "KEY DOWN key=" << static_cast<quint16>(key)
|
||||||
|
<< " raw=" << static_cast<quint32>(rawKey)
|
||||||
|
<< " vk=0x" << Qt::hex << virtualKey
|
||||||
|
<< " scan=0x" << scanCode << Qt::dec
|
||||||
|
<< " e0=" << extended << " modifiers(ctrl=" << ctrl
|
||||||
|
<< ",shift=" << shift << ",alt=" << alt << ",win=" << win
|
||||||
|
<< ") locks(num=" << numLock << ",caps=" << capsLock
|
||||||
|
<< ",scroll=" << scrollLock << ')';
|
||||||
|
});
|
||||||
|
QObject::connect(&rawInputDetector, &FancyInput::RawInputDetector::keyReleased,
|
||||||
|
[](FancyInput::FipKeys key, FancyInput::FipRawKeys rawKey,
|
||||||
|
quint16 virtualKey, quint16 scanCode, bool extended,
|
||||||
|
bool, bool, bool, bool, bool, bool, bool) {
|
||||||
|
qInfo().nospace() << "KEY UP key=" << static_cast<quint16>(key)
|
||||||
|
<< " raw=" << static_cast<quint32>(rawKey)
|
||||||
|
<< " vk=0x" << Qt::hex << virtualKey
|
||||||
|
<< " scan=0x" << scanCode << Qt::dec
|
||||||
|
<< " e0=" << extended;
|
||||||
|
});
|
||||||
|
QObject::connect(&rawInputDetector, &FancyInput::RawInputDetector::mouseButtonChanged,
|
||||||
|
[](FancyInput::MouseCodeType button, bool pressed) {
|
||||||
|
qInfo() << (pressed ? "MOUSE DOWN" : "MOUSE UP")
|
||||||
|
<< static_cast<int>(button);
|
||||||
|
});
|
||||||
|
QObject::connect(&rawInputDetector, &FancyInput::RawInputDetector::mouseWheelMoved,
|
||||||
|
[](int delta) { qInfo() << "MOUSE WHEEL" << delta; });
|
||||||
|
QObject::connect(&rawInputDetector, &FancyInput::RawInputDetector::mouseMoved,
|
||||||
|
[](int deltaX, int deltaY) { qInfo() << "MOUSE MOVE" << deltaX << deltaY; });
|
||||||
|
qInfo() << "Raw Input detection enabled on the main window.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return app.exec();
|
return app.exec();
|
||||||
}
|
}
|
||||||
|
|||||||
+8
-47
@@ -1,58 +1,19 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
import FancyInput.Controls
|
import FancyInput.Controls
|
||||||
import FancyInput.Windows
|
import FancyInput.Windows
|
||||||
|
|
||||||
// 主界面(根元素为 Rectangle —— Qt Creator 设计模式可可视化编辑)
|
|
||||||
// 由 main.cpp 用 QQuickWindow 包装成主窗口。
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: root
|
id: root
|
||||||
width: 600
|
width: 600
|
||||||
height: 480
|
height: 480
|
||||||
color: Theme.background
|
color: Theme.background
|
||||||
property int clickCount: 0
|
property int clickCount: 0
|
||||||
property Window settingsWindow: null
|
PopupWindows {
|
||||||
property Window overlayWindow: null
|
id: popupWindows
|
||||||
|
owner: root
|
||||||
function openSettingsWindow(modal) {
|
|
||||||
settingsWindow = settingsWindowComp.createObject(modal ? root : null, { modal: modal })
|
|
||||||
if (settingsWindow) settingsWindow.show()
|
|
||||||
}
|
|
||||||
function openOverlayWindow() {
|
|
||||||
overlayWindow = overlayWindowComp.createObject(null, {})
|
|
||||||
if (overlayWindow) overlayWindow.show()
|
|
||||||
}
|
|
||||||
// 设置窗口
|
|
||||||
Component {
|
|
||||||
id: settingsWindowComp
|
|
||||||
FancyWindow {
|
|
||||||
width: 480
|
|
||||||
height: 360
|
|
||||||
windowTitle: qsTr("设置 · FancyInput")
|
|
||||||
SettingsWindow {
|
|
||||||
anchors.fill: parent
|
|
||||||
onCloseRequested: hostWindow.close()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 悬浮窗
|
|
||||||
Component {
|
|
||||||
id: overlayWindowComp
|
|
||||||
FancyWindow {
|
|
||||||
width: 280
|
|
||||||
height: 180
|
|
||||||
frameless: true
|
|
||||||
transparent: true
|
|
||||||
alwaysOnTop: true
|
|
||||||
toolWindow: true
|
|
||||||
|
|
||||||
OverlayWindow {
|
|
||||||
anchors.fill: parent
|
|
||||||
onCloseRequested: hostWindow.close()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
@@ -110,7 +71,7 @@ Rectangle {
|
|||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
text: qsTr("打开设置窗口")
|
text: qsTr("打开设置窗口")
|
||||||
font.family: Theme.fontFamily
|
font.family: Theme.fontFamily
|
||||||
onClicked: root.openSettingsWindow(false)
|
onClicked: popupWindows.openSettingsWindow(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 打开模态窗口(WPF Window.ShowDialog 等价)
|
// 打开模态窗口(WPF Window.ShowDialog 等价)
|
||||||
@@ -118,7 +79,7 @@ Rectangle {
|
|||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
text: qsTr("打开模态窗口")
|
text: qsTr("打开模态窗口")
|
||||||
font.family: Theme.fontFamily
|
font.family: Theme.fontFamily
|
||||||
onClicked: root.openSettingsWindow(true)
|
onClicked: popupWindows.openSettingsWindow(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 打开无边框透明悬浮窗(WPF 悬浮窗等价)
|
// 打开无边框透明悬浮窗(WPF 悬浮窗等价)
|
||||||
@@ -126,7 +87,7 @@ Rectangle {
|
|||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
text: qsTr("打开悬浮窗")
|
text: qsTr("打开悬浮窗")
|
||||||
font.family: Theme.fontFamily
|
font.family: Theme.fontFamily
|
||||||
onClicked: root.openOverlayWindow()
|
onClicked: popupWindows.openOverlayWindow()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,50 @@
|
|||||||
|
import QtQuick
|
||||||
|
|
||||||
|
QtObject {
|
||||||
|
id: manager
|
||||||
|
|
||||||
|
property Item owner
|
||||||
|
property Window settingsWindow: null
|
||||||
|
property Window overlayWindow: null
|
||||||
|
|
||||||
|
function openSettingsWindow(modal) {
|
||||||
|
settingsWindow = settingsWindowComp.createObject(modal ? owner : null, { modal: modal })
|
||||||
|
if (settingsWindow) settingsWindow.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
function openOverlayWindow() {
|
||||||
|
overlayWindow = overlayWindowComp.createObject(null, {})
|
||||||
|
if (overlayWindow) overlayWindow.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
property Component settingsWindowComp: Component {
|
||||||
|
FancyWindow {
|
||||||
|
id: settingsHostWindow
|
||||||
|
width: 480
|
||||||
|
height: 360
|
||||||
|
windowTitle: qsTr("设置 · FancyInput")
|
||||||
|
|
||||||
|
SettingsWindow {
|
||||||
|
anchors.fill: parent
|
||||||
|
onCloseRequested: settingsHostWindow.hostWindow.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
property Component overlayWindowComp: Component {
|
||||||
|
FancyWindow {
|
||||||
|
id: overlayHostWindow
|
||||||
|
width: 280
|
||||||
|
height: 180
|
||||||
|
frameless: true
|
||||||
|
transparent: true
|
||||||
|
alwaysOnTop: true
|
||||||
|
toolWindow: true
|
||||||
|
|
||||||
|
OverlayWindow {
|
||||||
|
anchors.fill: parent
|
||||||
|
onCloseRequested: overlayHostWindow.hostWindow.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user