first push

This commit is contained in:
2026-04-03 09:26:10 +08:00
parent 2937a44e07
commit 025b88e366
41 changed files with 6842 additions and 0 deletions

49
MID/Mid_Ble.cpp Normal file
View File

@@ -0,0 +1,49 @@
#include "MID/Mid_Ble.h"
#include <QtCore/QStringList>
QString Mid_GetBleOpcodeText(quint8 Opcode)
{
return QStringLiteral("Opcode 0x%1")
.arg(Opcode, 2, 16, QLatin1Char('0'))
.toUpper();
}
QString Mid_NormalizeBleAddress(const QString& Text)
{
QString HexText;
HexText.reserve(Text.size());
for (const QChar Character : Text.trimmed())
{
if (Character.isDigit() ||
((Character >= QLatin1Char('a')) && (Character <= QLatin1Char('f'))) ||
((Character >= QLatin1Char('A')) && (Character <= QLatin1Char('F'))))
{
HexText.append(Character.toUpper());
}
}
return HexText.size() == 12 ? HexText : QString();
}
QString Mid_FormatBleAddress(const QString& Address)
{
const QString Normalized = Mid_NormalizeBleAddress(Address);
if (Normalized.isEmpty())
{
return QString();
}
QStringList PartList;
for (int Index = 0; Index < Normalized.size(); Index += 2)
{
PartList.append(Normalized.mid(Index, 2));
}
return PartList.join(QLatin1Char(':'));
}
bool Mid_IsBleAddressValid(const QString& Address)
{
return !Mid_NormalizeBleAddress(Address).isEmpty();
}

9
MID/Mid_Ble.h Normal file
View File

@@ -0,0 +1,9 @@
#pragma once
#include <QtCore/QString>
// BLE raw packet opcode text helpers.
QString Mid_GetBleOpcodeText(quint8 Opcode);
QString Mid_NormalizeBleAddress(const QString& Text);
QString Mid_FormatBleAddress(const QString& Address);
bool Mid_IsBleAddressValid(const QString& Address);

243
MID/Mid_Def.cpp Normal file
View File

@@ -0,0 +1,243 @@
#include "MID/Mid_Def.h"
#include <QtCore/QStringList>
#include <QtCore/QVector>
#include <Windows.h>
#include <SetupAPI.h>
#include <hidsdi.h>
#include <hidpi.h>
#pragma comment(lib, "hid.lib")
#pragma comment(lib, "setupapi.lib")
namespace
{
QString Mid_GetDeviceInstanceId(HDEVINFO DeviceInfoSet, SP_DEVINFO_DATA* p_DeviceInfoData)
{
DWORD NeedLength = 0;
SetupDiGetDeviceInstanceIdW(
DeviceInfoSet,
p_DeviceInfoData,
nullptr,
0,
&NeedLength);
if (NeedLength == 0)
{
return QString();
}
QVector<wchar_t> Buffer(static_cast<int>(NeedLength) + 1, 0);
return SetupDiGetDeviceInstanceIdW(
DeviceInfoSet,
p_DeviceInfoData,
Buffer.data(),
static_cast<DWORD>(Buffer.size()),
nullptr)
? QString::fromWCharArray(Buffer.constData()).trimmed()
: QString();
}
} // namespace
// Find the HID interface that matches VID/PID plus usage page/usage.
bool Mid_FindHidInterface(
const Mid_Struct_DeviceMatch& Match,
QString* p_DevicePath,
quint16* p_InputLength,
quint16* p_OutputLength,
QString* p_DeviceInstanceId)
{
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)
{
DWORD NeedLength = 0;
SP_DEVINFO_DATA DeviceInfoData = {};
DeviceInfoData.cbSize = sizeof(DeviceInfoData);
SetupDiGetDeviceInterfaceDetailW(
DeviceInfoSet,
&InterfaceData,
nullptr,
0,
&NeedLength,
&DeviceInfoData);
if (NeedLength == 0)
{
continue;
}
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,
&DeviceInfoData))
{
continue;
}
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 = {};
const bool IsExactMatch =
HidD_GetAttributes(HandleQuery, &Attributes) &&
HidD_GetPreparsedData(HandleQuery, &p_Preparsed) &&
(HidP_GetCaps(p_Preparsed, &Caps) == HIDP_STATUS_SUCCESS) &&
(Caps.UsagePage == Match.UsagePage) &&
(Caps.Usage == Match.Usage) &&
(Attributes.VendorID == Match.VendorId) &&
(Attributes.ProductID == Match.ProductId);
if (p_Preparsed != nullptr)
{
HidD_FreePreparsedData(p_Preparsed);
}
CloseHandle(HandleQuery);
if (IsExactMatch)
{
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;
}
if (p_DeviceInstanceId != nullptr)
{
*p_DeviceInstanceId = Mid_GetDeviceInstanceId(DeviceInfoSet, &DeviceInfoData);
}
IsFound = true;
break;
}
}
SetupDiDestroyDeviceInfoList(DeviceInfoSet);
return IsFound;
}
bool Mid_IsBluetoothHidInstanceId(const QString& DeviceInstanceId)
{
const QString UpperId = DeviceInstanceId.trimmed().toUpper();
return UpperId.contains(QStringLiteral("BTHLEDEVICE")) ||
UpperId.contains(QStringLiteral("{00001812-0000-1000-8000-00805F9B34FB}"));
}
QString Mid_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(' '));
}
QString Mid_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(", "));
}
QString Mid_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");
}
}
QString Mid_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");
}
}

81
MID/Mid_Def.h Normal file
View File

@@ -0,0 +1,81 @@
#pragma once
#include <QtCore/QByteArray>
#include <QtCore/QString>
// Shared protocol constants and raw packet definitions.
enum Mid_Enum_ReportId : quint8
{
Mid_Enum_ReportId_None = 0x00,
Mid_Enum_ReportId_Nkro = 0x01,
Mid_Enum_ReportId_Consumer = 0x03,
Mid_Enum_ReportId_Vendor = 0x04,
Mid_Enum_ReportId_VendorCommand = 0x05
};
enum Mid_Enum_RawPacketSource : quint8
{
Mid_Enum_RawPacketSource_None = 0,
Mid_Enum_RawPacketSource_UsbNkroRaw,
Mid_Enum_RawPacketSource_UsbConsumerHid,
Mid_Enum_RawPacketSource_UsbVendorHid,
Mid_Enum_RawPacketSource_BleGatt,
Mid_Enum_RawPacketSource_BleHidKeyboard,
Mid_Enum_RawPacketSource_BleHidConsumer,
Mid_Enum_RawPacketSource_BleHidVendor,
Mid_Enum_RawPacketSource_BleHidVendorCommand
};
const quint16 MID_CONST_VENDOR_ID_DEFAULT = 0x1209;
const quint16 MID_CONST_PRODUCT_ID_DEFAULT = 0x0001;
struct Mid_Struct_DeviceConfig
{
quint16 VendorId = MID_CONST_VENDOR_ID_DEFAULT;
quint16 ProductId = MID_CONST_PRODUCT_ID_DEFAULT;
};
struct Mid_Struct_DeviceMatch
{
quint16 VendorId = 0;
quint16 ProductId = 0;
quint16 UsagePage = 0;
quint16 Usage = 0;
};
struct Mid_Struct_RawPacket
{
bool IsValid = false;
Mid_Enum_RawPacketSource Source = Mid_Enum_RawPacketSource_None;
QByteArray ByteArray;
QString PortName;
};
const quint16 MID_CONST_USAGE_PAGE_NKRO = 0x0001;
const quint16 MID_CONST_USAGE_NKRO = 0x0006;
const quint16 MID_CONST_USAGE_PAGE_CONSUMER = 0x000C;
const quint16 MID_CONST_USAGE_CONSUMER = 0x0001;
const quint16 MID_CONST_USAGE_PAGE_VENDOR = 0xFF00;
const quint16 MID_CONST_USAGE_VENDOR = 0x0002;
const quint16 MID_CONST_USAGE_PAGE_VENDOR_COMMAND = 0xFF01;
const quint16 MID_CONST_USAGE_VENDOR_COMMAND = 0x0005;
const int MID_CONST_KEYBOARD_USAGE_MAX = 0x00E7;
const int MID_CONST_USAGE_BITMAP_SIZE = 29;
const int MID_CONST_PACKET_SIZE_NKRO = 31;
const int MID_CONST_PACKET_SIZE_VENDOR = 31;
const int MID_CONST_PACKET_SIZE_VENDOR_COMMAND = 10;
const int MID_CONST_PACKET_SIZE_VENDOR_COMMAND_DATA = 8;
const int MID_CONST_PACKET_SIZE_CONSUMER = 3;
bool Mid_FindHidInterface(
const Mid_Struct_DeviceMatch& Match,
QString* p_DevicePath,
quint16* p_InputLength,
quint16* p_OutputLength,
QString* p_DeviceInstanceId = nullptr);
bool Mid_IsBluetoothHidInstanceId(const QString& DeviceInstanceId);
QString Mid_GetHexText(const QByteArray& ByteArray);
QString Mid_GetModifierText(quint8 Modifier);
QString Mid_GetKeyboardUsageText(quint16 Usage);
QString Mid_GetConsumerUsageText(quint16 Usage);