first push
This commit is contained in:
49
MID/Mid_Ble.cpp
Normal file
49
MID/Mid_Ble.cpp
Normal 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
9
MID/Mid_Ble.h
Normal 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
243
MID/Mid_Def.cpp
Normal 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
81
MID/Mid_Def.h
Normal 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);
|
||||
|
||||
Reference in New Issue
Block a user