添加项目文件。
This commit is contained in:
282
DRI/Dri_NkroRaw.cpp
Normal file
282
DRI/Dri_NkroRaw.cpp
Normal file
@@ -0,0 +1,282 @@
|
||||
#include "DRI/Dri_NkroRaw.h"
|
||||
|
||||
#include <QtCore/QVector>
|
||||
#include <Windows.h>
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
/* ---------- 设备过滤与 Usage 映射 ---------- */
|
||||
|
||||
QString Dri_NkroRaw_Func_GetDevicePath(HANDLE DeviceHandle)
|
||||
{
|
||||
if (DeviceHandle == nullptr)
|
||||
{
|
||||
return QString();
|
||||
}
|
||||
|
||||
UINT NeedChars = 0;
|
||||
GetRawInputDeviceInfoW(DeviceHandle, RIDI_DEVICENAME, nullptr, &NeedChars);
|
||||
if (NeedChars == 0)
|
||||
{
|
||||
return QString();
|
||||
}
|
||||
|
||||
QVector<wchar_t> Buffer(static_cast<int>(NeedChars) + 1, 0);
|
||||
if (GetRawInputDeviceInfoW(DeviceHandle, RIDI_DEVICENAME, Buffer.data(), &NeedChars) == static_cast<UINT>(-1))
|
||||
{
|
||||
return QString();
|
||||
}
|
||||
|
||||
return QString::fromWCharArray(Buffer.constData()).trimmed();
|
||||
}
|
||||
|
||||
bool Dri_NkroRaw_Func_IsTargetDevice(const QString& DevicePath, const Mid_Struct_DeviceConfig& DeviceConfig)
|
||||
{
|
||||
if (DevicePath.isEmpty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const QString UpperPath = DevicePath.toUpper();
|
||||
return UpperPath.contains(QStringLiteral("VID_%1").arg(DeviceConfig.VendorId, 4, 16, QLatin1Char('0')).toUpper()) &&
|
||||
UpperPath.contains(QStringLiteral("PID_%1").arg(DeviceConfig.ProductId, 4, 16, QLatin1Char('0')).toUpper());
|
||||
}
|
||||
|
||||
quint16 Dri_NkroRaw_Func_GetUsage(const RAWKEYBOARD& Keyboard)
|
||||
{
|
||||
const bool IsE0 = (Keyboard.Flags & RI_KEY_E0) != 0;
|
||||
const bool IsE1 = (Keyboard.Flags & RI_KEY_E1) != 0;
|
||||
const USHORT ScanCode = Keyboard.MakeCode;
|
||||
|
||||
if (IsE1)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (IsE0)
|
||||
{
|
||||
switch (ScanCode)
|
||||
{
|
||||
case 0x35: return 0x0054;
|
||||
case 0x1C: return 0x0058;
|
||||
case 0x1D: return 0x00E4;
|
||||
case 0x38: return 0x00E6;
|
||||
case 0x5B: return 0x00E3;
|
||||
case 0x5C: return 0x00E7;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
switch (ScanCode)
|
||||
{
|
||||
case 0x45: return 0x0053;
|
||||
case 0x37: return 0x0055;
|
||||
case 0x4A: return 0x0056;
|
||||
case 0x4E: return 0x0057;
|
||||
case 0x47: return 0x005F;
|
||||
case 0x48: return 0x0060;
|
||||
case 0x49: return 0x0061;
|
||||
case 0x4B: return 0x005C;
|
||||
case 0x4C: return 0x005D;
|
||||
case 0x4D: return 0x005E;
|
||||
case 0x4F: return 0x0059;
|
||||
case 0x50: return 0x005A;
|
||||
case 0x51: return 0x005B;
|
||||
case 0x52: return 0x0062;
|
||||
case 0x53: return 0x0063;
|
||||
case 0x1D: return 0x00E0;
|
||||
case 0x2A: return 0x00E1;
|
||||
case 0x36: return 0x00E5;
|
||||
case 0x38: return 0x00E2;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
/* ---------- 生命周期 ---------- */
|
||||
|
||||
void Dri_NkroRaw_Func_Close(Dri_NkroRaw_Struct_Port* p_Port)
|
||||
{
|
||||
*p_Port = Dri_NkroRaw_Struct_Port();
|
||||
}
|
||||
|
||||
bool Dri_NkroRaw_Func_Open(
|
||||
Dri_NkroRaw_Struct_Port* p_Port,
|
||||
const Mid_Struct_DeviceConfig& DeviceConfig,
|
||||
void* WindowHandle,
|
||||
QString* p_TextStatus)
|
||||
{
|
||||
Dri_NkroRaw_Func_Close(p_Port);
|
||||
|
||||
if (WindowHandle == nullptr)
|
||||
{
|
||||
if (p_TextStatus != nullptr)
|
||||
{
|
||||
*p_TextStatus = QStringLiteral("NKRO 原生输入链路打开失败:窗口句柄为空。");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
RAWINPUTDEVICE Device = {};
|
||||
Device.usUsagePage = MID_CONST_USAGE_PAGE_NKRO;
|
||||
Device.usUsage = MID_CONST_USAGE_NKRO;
|
||||
Device.dwFlags = RIDEV_INPUTSINK;
|
||||
Device.hwndTarget = reinterpret_cast<HWND>(WindowHandle);
|
||||
|
||||
if (!RegisterRawInputDevices(&Device, 1, sizeof(Device)))
|
||||
{
|
||||
if (p_TextStatus != nullptr)
|
||||
{
|
||||
*p_TextStatus = QStringLiteral("NKRO 原生输入链路注册失败:%1").arg(GetLastError());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
p_Port->IsOpened = true;
|
||||
p_Port->WindowHandle = WindowHandle;
|
||||
p_Port->DeviceConfig = DeviceConfig;
|
||||
|
||||
if (p_TextStatus != nullptr)
|
||||
{
|
||||
*p_TextStatus = QStringLiteral("已启用 NKRO 原生输入链路。");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/* ---------- RawInput 主流程 ---------- */
|
||||
|
||||
bool Dri_NkroRaw_Func_HandleNativeMessage(
|
||||
Dri_NkroRaw_Struct_Port* p_Port,
|
||||
void* p_Message,
|
||||
QString* p_TextStatus)
|
||||
{
|
||||
if (!p_Port->IsOpened || (p_Message == nullptr))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
MSG* p_Msg = reinterpret_cast<MSG*>(p_Message);
|
||||
if (p_Msg->message != WM_INPUT)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
UINT NeedSize = 0;
|
||||
GetRawInputData(reinterpret_cast<HRAWINPUT>(p_Msg->lParam), RID_INPUT, nullptr, &NeedSize, sizeof(RAWINPUTHEADER));
|
||||
if (NeedSize == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
QByteArray Buffer(static_cast<int>(NeedSize), 0);
|
||||
if (GetRawInputData(
|
||||
reinterpret_cast<HRAWINPUT>(p_Msg->lParam),
|
||||
RID_INPUT,
|
||||
Buffer.data(),
|
||||
&NeedSize,
|
||||
sizeof(RAWINPUTHEADER)) == static_cast<UINT>(-1))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
RAWINPUT* p_Input = reinterpret_cast<RAWINPUT*>(Buffer.data());
|
||||
if (p_Input->header.dwType != RIM_TYPEKEYBOARD)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const QString DevicePath = Dri_NkroRaw_Func_GetDevicePath(p_Input->header.hDevice);
|
||||
if (!Dri_NkroRaw_Func_IsTargetDevice(DevicePath, p_Port->DeviceConfig))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const quint16 Usage = Dri_NkroRaw_Func_GetUsage(p_Input->data.keyboard);
|
||||
if (Usage == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const bool IsPressed = (p_Input->data.keyboard.Flags & RI_KEY_BREAK) == 0;
|
||||
bool IsChanged = false;
|
||||
|
||||
if ((Usage >= 0x00E0) && (Usage <= 0x00E7))
|
||||
{
|
||||
const quint8 BitMask = static_cast<quint8>(1U << (Usage - 0x00E0));
|
||||
const quint8 OldModifier = p_Port->Modifier;
|
||||
if (IsPressed)
|
||||
{
|
||||
p_Port->Modifier = static_cast<quint8>(p_Port->Modifier | BitMask);
|
||||
}
|
||||
else
|
||||
{
|
||||
p_Port->Modifier = static_cast<quint8>(p_Port->Modifier & static_cast<quint8>(~BitMask));
|
||||
}
|
||||
IsChanged = (OldModifier != p_Port->Modifier);
|
||||
}
|
||||
else
|
||||
{
|
||||
const int ByteIndex = Usage / 8;
|
||||
const quint8 BitMask = static_cast<quint8>(1U << (Usage % 8));
|
||||
quint8 Value = static_cast<quint8>(p_Port->UsageBitmap.at(ByteIndex));
|
||||
const bool OldPressed = (Value & BitMask) != 0;
|
||||
if (OldPressed == IsPressed)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
Value = IsPressed ? static_cast<quint8>(Value | BitMask) : static_cast<quint8>(Value & static_cast<quint8>(~BitMask));
|
||||
p_Port->UsageBitmap[ByteIndex] = static_cast<char>(Value);
|
||||
IsChanged = true;
|
||||
}
|
||||
|
||||
if (!IsChanged)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (p_Port->DevicePath != DevicePath)
|
||||
{
|
||||
p_Port->DevicePath = DevicePath;
|
||||
if (p_TextStatus != nullptr)
|
||||
{
|
||||
*p_TextStatus = QStringLiteral("原生输入已命中目标设备:%1").arg(DevicePath);
|
||||
}
|
||||
}
|
||||
|
||||
Mid_Struct_RawPacket Packet;
|
||||
Packet.IsValid = true;
|
||||
Packet.PortName = QStringLiteral("NKRO(原生输入)");
|
||||
Packet.ByteArray = QByteArray(MID_CONST_PACKET_SIZE_NKRO, 0);
|
||||
Packet.ByteArray[0] = static_cast<char>(Mid_Enum_ReportId_Nkro);
|
||||
Packet.ByteArray[1] = static_cast<char>(p_Port->Modifier);
|
||||
|
||||
for (int Index = 0; Index < MID_CONST_USAGE_BITMAP_SIZE; ++Index)
|
||||
{
|
||||
Packet.ByteArray[2 + Index] = p_Port->UsageBitmap.at(Index);
|
||||
}
|
||||
|
||||
p_Port->PacketQueue.append(Packet);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Dri_NkroRaw_Func_Read(
|
||||
Dri_NkroRaw_Struct_Port* p_Port,
|
||||
Mid_Struct_RawPacket* p_Packet,
|
||||
QString*)
|
||||
{
|
||||
p_Packet->IsValid = false;
|
||||
p_Packet->ByteArray.clear();
|
||||
p_Packet->PortName = QStringLiteral("NKRO(原生输入)");
|
||||
|
||||
if (!p_Port->IsOpened || p_Port->PacketQueue.isEmpty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
*p_Packet = p_Port->PacketQueue.takeFirst();
|
||||
return p_Packet->IsValid;
|
||||
}
|
||||
Reference in New Issue
Block a user