Files
0417_QT_code/MID/Mid_Def.cpp
2026-03-26 10:45:29 +08:00

242 lines
8.4 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#include "MID/Mid_Def.h"
#include <Windows.h>
#include <SetupAPI.h>
#include <hidsdi.h>
#include <hidpi.h>
#pragma comment(lib, "hid.lib")
#pragma comment(lib, "setupapi.lib")
/*
* MID 层:介于逻辑与 DRI 之间,负责描述 HID 设备匹配与数据格式。
* 高密注释用于教学,逐条解释如何把固件接口映射为 Win32 API 调用。
*/
/* 构造 NKRO 接口的匹配条件Usage Page / Usage 固定) */
Mid_Struct_DeviceMatch Mid_Func_GetNkroMatch(const Mid_Struct_DeviceConfig& DeviceConfig)
{
Mid_Struct_DeviceMatch Match;
Match.VendorId = DeviceConfig.VendorId;
Match.ProductId = DeviceConfig.ProductId;
Match.UsagePage = MID_CONST_USAGE_PAGE_NKRO;
Match.Usage = MID_CONST_USAGE_NKRO;
Match.Name = QStringLiteral("NKRO Keyboard Interface");
return Match;
}
/* 构造 Consumer 接口的匹配条件 */
Mid_Struct_DeviceMatch Mid_Func_GetConsumerMatch(const Mid_Struct_DeviceConfig& DeviceConfig)
{
Mid_Struct_DeviceMatch Match;
Match.VendorId = DeviceConfig.VendorId;
Match.ProductId = DeviceConfig.ProductId;
Match.UsagePage = MID_CONST_USAGE_PAGE_CONSUMER;
Match.Usage = MID_CONST_USAGE_CONSUMER;
Match.Name = QStringLiteral("Consumer Interface");
return Match;
}
/* 构造 Vendor状态镜像接口的匹配条件 */
Mid_Struct_DeviceMatch Mid_Func_GetVendorMatch(const Mid_Struct_DeviceConfig& DeviceConfig)
{
Mid_Struct_DeviceMatch Match;
Match.VendorId = DeviceConfig.VendorId;
Match.ProductId = DeviceConfig.ProductId;
Match.UsagePage = MID_CONST_USAGE_PAGE_VENDOR;
Match.Usage = MID_CONST_USAGE_VENDOR;
Match.Name = QStringLiteral("Vendor State Mirror Interface");
return Match;
}
/*
* Mid_Func_FindHidInterface按照匹配条件扫描系统中的 HID 接口。
* 步骤1) 获取 HID GUID 2) SetupAPI 列举接口 3) CreateFile 查询属性 4) 命中后返回路径和报文长度。
*/
bool Mid_Func_FindHidInterface(
const Mid_Struct_DeviceMatch& Match,
QString* p_DevicePath,
quint16* p_InputLength,
quint16* p_OutputLength)
{
/* Win32 提供的 HID GUID列举所有 HID 接口都靠它 */
GUID HidGuid;
HidD_GetHidGuid(&HidGuid);
/* 构建“当前存在 + 暴露接口”的设备集合 */
HDEVINFO DeviceInfoSet = SetupDiGetClassDevsW(&HidGuid, nullptr, nullptr, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
if (DeviceInfoSet == INVALID_HANDLE_VALUE)
{
return false;
}
bool IsFound = false;
SP_DEVICE_INTERFACE_DATA InterfaceData = {};
InterfaceData.cbSize = sizeof(InterfaceData);
for (DWORD Index = 0;
SetupDiEnumDeviceInterfaces(DeviceInfoSet, nullptr, &HidGuid, Index, &InterfaceData);
++Index)
{
/* Query 设备路径前先得到所需缓冲区长度 */
DWORD NeedLength = 0;
SetupDiGetDeviceInterfaceDetailW(DeviceInfoSet, &InterfaceData, nullptr, 0, &NeedLength, nullptr);
if (NeedLength == 0)
{
continue;
}
/* 申请一段缓冲区存储 SP_DEVICE_INTERFACE_DETAIL_DATA_W */
QByteArray Buffer(static_cast<int>(NeedLength), 0);
auto* p_Detail = reinterpret_cast<SP_DEVICE_INTERFACE_DETAIL_DATA_W*>(Buffer.data());
p_Detail->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_W);
if (!SetupDiGetDeviceInterfaceDetailW(DeviceInfoSet, &InterfaceData, p_Detail, NeedLength, nullptr, nullptr))
{
continue;
}
/* 只需 0 访问权限即可读取属性 */
HANDLE HandleQuery = CreateFileW(
p_Detail->DevicePath,
0,
FILE_SHARE_READ | FILE_SHARE_WRITE,
nullptr,
OPEN_EXISTING,
0,
nullptr);
if (HandleQuery == INVALID_HANDLE_VALUE)
{
continue;
}
HIDD_ATTRIBUTES Attributes = {};
Attributes.Size = sizeof(Attributes);
PHIDP_PREPARSED_DATA p_Preparsed = nullptr;
HIDP_CAPS Caps = {};
/* 通过 Attributes + Caps 对比 VID / PID / Usage Page / Usage */
const bool IsMatch =
HidD_GetAttributes(HandleQuery, &Attributes) &&
HidD_GetPreparsedData(HandleQuery, &p_Preparsed) &&
(HidP_GetCaps(p_Preparsed, &Caps) == HIDP_STATUS_SUCCESS) &&
(Attributes.VendorID == Match.VendorId) &&
(Attributes.ProductID == Match.ProductId) &&
(Caps.UsagePage == Match.UsagePage) &&
(Caps.Usage == Match.Usage);
if (p_Preparsed != nullptr)
{
HidD_FreePreparsedData(p_Preparsed);
}
CloseHandle(HandleQuery);
if (!IsMatch)
{
continue;
}
/* 命中:写回设备路径及报文长度 */
if (p_DevicePath != nullptr)
{
*p_DevicePath = QString::fromWCharArray(p_Detail->DevicePath);
}
if (p_InputLength != nullptr)
{
*p_InputLength = Caps.InputReportByteLength;
}
if (p_OutputLength != nullptr)
{
*p_OutputLength = Caps.OutputReportByteLength;
}
IsFound = true;
break;
}
/* 枚举结束释放句柄 */
SetupDiDestroyDeviceInfoList(DeviceInfoSet);
return IsFound;
}
/* 调试输出把字节数组格式化成“AA BB CC” */
QString Mid_Func_GetHexText(const QByteArray& ByteArray)
{
QStringList TextList;
for (int Index = 0; Index < ByteArray.size(); ++Index)
{
TextList.append(QStringLiteral("%1")
.arg(static_cast<quint8>(ByteArray.at(Index)), 2, 16, QLatin1Char('0'))
.toUpper());
}
return TextList.join(QLatin1Char(' '));
}
/* 把 Modifier 位图翻译成人类可读的组合 */
QString Mid_Func_GetModifierText(quint8 Modifier)
{
QStringList TextList;
if (Modifier == 0)
{
return QStringLiteral("");
}
if ((Modifier & 0x01) != 0) TextList.append(QStringLiteral("Left Ctrl"));
if ((Modifier & 0x02) != 0) TextList.append(QStringLiteral("Left Shift"));
if ((Modifier & 0x04) != 0) TextList.append(QStringLiteral("Left Alt"));
if ((Modifier & 0x08) != 0) TextList.append(QStringLiteral("Left GUI"));
if ((Modifier & 0x10) != 0) TextList.append(QStringLiteral("Right Ctrl"));
if ((Modifier & 0x20) != 0) TextList.append(QStringLiteral("Right Shift"));
if ((Modifier & 0x40) != 0) TextList.append(QStringLiteral("Right Alt"));
if ((Modifier & 0x80) != 0) TextList.append(QStringLiteral("Right GUI"));
return TextList.join(QStringLiteral(", "));
}
/* 键盘 Usage -> 中文/英文标签,方便 UI 展示 */
QString Mid_Func_GetKeyboardUsageText(quint16 Usage)
{
switch (Usage)
{
case 0x0053: return QStringLiteral("Num Lock");
case 0x0054: return QStringLiteral("小键盘 /");
case 0x0055: return QStringLiteral("小键盘 *");
case 0x0056: return QStringLiteral("小键盘 -");
case 0x0057: return QStringLiteral("小键盘 +");
case 0x0058: return QStringLiteral("小键盘 Enter");
case 0x0059: return QStringLiteral("小键盘 1");
case 0x005A: return QStringLiteral("小键盘 2");
case 0x005B: return QStringLiteral("小键盘 3");
case 0x005C: return QStringLiteral("小键盘 4");
case 0x005D: return QStringLiteral("小键盘 5");
case 0x005E: return QStringLiteral("小键盘 6");
case 0x005F: return QStringLiteral("小键盘 7");
case 0x0060: return QStringLiteral("小键盘 8");
case 0x0061: return QStringLiteral("小键盘 9");
case 0x0062: return QStringLiteral("小键盘 0");
case 0x0063: return QStringLiteral("小键盘 .");
case 0x00E0: return QStringLiteral("Left Ctrl");
case 0x00E1: return QStringLiteral("Left Shift");
case 0x00E2: return QStringLiteral("Left Alt");
case 0x00E3: return QStringLiteral("Left GUI");
case 0x00E4: return QStringLiteral("Right Ctrl");
case 0x00E5: return QStringLiteral("Right Shift");
case 0x00E6: return QStringLiteral("Right Alt");
case 0x00E7: return QStringLiteral("Right GUI");
default:
return QStringLiteral("未知 HID Usage");
}
}
/* Consumer Usage -> UI 标签 */
QString Mid_Func_GetConsumerUsageText(quint16 Usage)
{
switch (Usage)
{
case 0x0000: return QStringLiteral("释放");
case 0x00E2: return QStringLiteral("Mute");
case 0x00E9: return QStringLiteral("Volume Up");
case 0x00EA: return QStringLiteral("Volume Down");
default:
return QStringLiteral("未知 Consumer Usage");
}
}