373 lines
12 KiB
C++
373 lines
12 KiB
C++
#include "LOGIC/Lgc_Core_Private.h"
|
|
|
|
#include "MID/Mid_Ble.h"
|
|
#include <QtCore/QDateTime>
|
|
#include <QtCore/QStringList>
|
|
#include <QtCore/QTimeZone>
|
|
#include <Windows.h>
|
|
|
|
namespace
|
|
{
|
|
|
|
QString Lgc_Core_GetTimeText()
|
|
{
|
|
return QDateTime::currentDateTimeUtc()
|
|
.toTimeZone(QTimeZone("Asia/Shanghai"))
|
|
.toString(QStringLiteral("HH:mm:ss.zzz"));
|
|
}
|
|
|
|
void Lgc_Core_AppendLog(Lgc_Core_Struct_State* p_State, const QString& Text)
|
|
{
|
|
if (Text.isEmpty())
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (p_State->TextLog.isEmpty())
|
|
{
|
|
p_State->TextLog = Text;
|
|
}
|
|
else
|
|
{
|
|
p_State->TextLog.append(QStringLiteral("\n\n"));
|
|
p_State->TextLog.append(Text);
|
|
}
|
|
|
|
if (p_State->TextLog.size() > 24000)
|
|
{
|
|
p_State->TextLog = p_State->TextLog.right(20000);
|
|
}
|
|
}
|
|
|
|
void Lgc_Core_CollectKeyboardUsage(
|
|
const QByteArray& UsageBitmap,
|
|
QVector<quint16>* p_UsageList,
|
|
QStringList* p_UsageTextList)
|
|
{
|
|
p_UsageList->clear();
|
|
p_UsageTextList->clear();
|
|
|
|
for (quint16 Usage = 0; Usage <= MID_CONST_KEYBOARD_USAGE_MAX; ++Usage)
|
|
{
|
|
const int ByteIndex = Usage / 8;
|
|
const quint8 BitMask = static_cast<quint8>(1U << (Usage % 8));
|
|
const quint8 ByteValue = static_cast<quint8>(UsageBitmap.at(ByteIndex));
|
|
if ((ByteValue & BitMask) == 0)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
p_UsageList->append(Usage);
|
|
p_UsageTextList->append(Mid_GetKeyboardUsageText(Usage));
|
|
}
|
|
}
|
|
|
|
bool Lgc_Core_HandleBleHidPacket(Lgc_Core_Struct_State* p_State, const Mid_Struct_RawPacket& Packet)
|
|
{
|
|
switch (Packet.Source)
|
|
{
|
|
case Mid_Enum_RawPacketSource_BleHidKeyboard:
|
|
case Mid_Enum_RawPacketSource_BleHidConsumer:
|
|
case Mid_Enum_RawPacketSource_BleHidVendor:
|
|
case Mid_Enum_RawPacketSource_BleHidVendorCommand:
|
|
break;
|
|
|
|
default:
|
|
return false;
|
|
}
|
|
|
|
if (Packet.ByteArray.isEmpty())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
switch (static_cast<quint8>(Packet.ByteArray.at(0)))
|
|
{
|
|
case Mid_Enum_ReportId_Nkro:
|
|
Lgc_Core_HandleNkroPacket(p_State, Packet);
|
|
return true;
|
|
|
|
case Mid_Enum_ReportId_Consumer:
|
|
Lgc_Core_HandleConsumerPacket(p_State, Packet);
|
|
return true;
|
|
|
|
case Mid_Enum_ReportId_Vendor:
|
|
case Mid_Enum_ReportId_VendorCommand:
|
|
Lgc_Core_HandleVendorPacket(p_State, Packet);
|
|
return true;
|
|
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
} // namespace
|
|
|
|
void Lgc_Core_UpdateSendTransportByPacket(
|
|
Lgc_Core_Struct_State* p_State,
|
|
Mid_Enum_RawPacketSource Source)
|
|
{
|
|
switch (Source)
|
|
{
|
|
case Mid_Enum_RawPacketSource_UsbNkroRaw:
|
|
case Mid_Enum_RawPacketSource_UsbConsumerHid:
|
|
case Mid_Enum_RawPacketSource_UsbVendorHid:
|
|
p_State->ActiveSendTransport = Lgc_Core_Enum_SendTransport_Usb;
|
|
break;
|
|
|
|
case Mid_Enum_RawPacketSource_BleGatt:
|
|
case Mid_Enum_RawPacketSource_BleHidKeyboard:
|
|
case Mid_Enum_RawPacketSource_BleHidConsumer:
|
|
case Mid_Enum_RawPacketSource_BleHidVendor:
|
|
case Mid_Enum_RawPacketSource_BleHidVendorCommand:
|
|
p_State->ActiveSendTransport = Lgc_Core_Enum_SendTransport_Ble;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void Lgc_Core_NormalizeSendTransport(Lgc_Core_Struct_State* p_State)
|
|
{
|
|
const bool HasUsb = p_State->DriVendorPort.ReadPort.IsOpened;
|
|
const bool HasBle = p_State->DriBlePort.IsConnected;
|
|
|
|
if ((p_State->ActiveSendTransport == Lgc_Core_Enum_SendTransport_Usb) && HasUsb)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if ((p_State->ActiveSendTransport == Lgc_Core_Enum_SendTransport_Ble) && HasBle)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (HasBle && !HasUsb)
|
|
{
|
|
p_State->ActiveSendTransport = Lgc_Core_Enum_SendTransport_Ble;
|
|
}
|
|
else if (HasUsb && !HasBle)
|
|
{
|
|
p_State->ActiveSendTransport = Lgc_Core_Enum_SendTransport_Usb;
|
|
}
|
|
else if (!HasUsb && !HasBle)
|
|
{
|
|
p_State->ActiveSendTransport = Lgc_Core_Enum_SendTransport_None;
|
|
}
|
|
}
|
|
|
|
void Lgc_Core_AppendStatusLog(Lgc_Core_Struct_State* p_State, const QString& Text)
|
|
{
|
|
if (!Text.isEmpty())
|
|
{
|
|
Lgc_Core_AppendLog(
|
|
p_State,
|
|
QStringLiteral("[%1] 状态\n%2").arg(Lgc_Core_GetTimeText(), Text));
|
|
}
|
|
}
|
|
|
|
void Lgc_Core_AppendPacketLog(
|
|
Lgc_Core_Struct_State* p_State,
|
|
const QString& ActionText,
|
|
const QString& PortName,
|
|
const QByteArray& Packet,
|
|
const QString& ExplainText)
|
|
{
|
|
QString Text = QStringLiteral("[%1] %2\n端口: %3\nHEX: %4")
|
|
.arg(Lgc_Core_GetTimeText(), ActionText, PortName, Mid_GetHexText(Packet));
|
|
if (!ExplainText.isEmpty())
|
|
{
|
|
Text.append(QLatin1Char('\n'));
|
|
Text.append(ExplainText);
|
|
}
|
|
Lgc_Core_AppendLog(p_State, Text);
|
|
}
|
|
|
|
void Lgc_Core_ClearAllKeyStates(Lgc_Core_Struct_State* p_State)
|
|
{
|
|
p_State->IsVisibleKeyStateValid = false;
|
|
p_State->VisibleUsageList.clear();
|
|
|
|
p_State->IsPhysicalKeyStateValid = false;
|
|
p_State->PhysicalUsageList.clear();
|
|
p_State->LastPhysicalUsageList.clear();
|
|
}
|
|
|
|
void Lgc_Core_CloseAllPorts(Lgc_Core_Struct_State* p_State)
|
|
{
|
|
Dri_Ble_Close(&p_State->DriBlePort);
|
|
Dri_NkroRaw_Close(&p_State->DriNkroPort);
|
|
Dri_Consumer_Close(&p_State->DriConsumerPort);
|
|
Dri_Vendor_Close(&p_State->DriVendorPort);
|
|
}
|
|
|
|
bool Lgc_Core_SyncSystemState(Lgc_Core_Struct_State* p_State)
|
|
{
|
|
const bool OldNumLock = p_State->IsSystemNumLockOn;
|
|
const bool OldConnected = p_State->IsConnected;
|
|
const QString OldConnection = p_State->TextConnection;
|
|
const Lgc_Core_Enum_SendTransport OldSendTransport = p_State->ActiveSendTransport;
|
|
|
|
p_State->IsSystemNumLockOn = (GetKeyState(VK_NUMLOCK) & 0x0001) != 0;
|
|
p_State->IsConnected = p_State->DriVendorPort.ReadPort.IsOpened || p_State->DriBlePort.IsConnected;
|
|
Lgc_Core_NormalizeSendTransport(p_State);
|
|
|
|
QStringList Lines;
|
|
if (p_State->DriVendorPort.ReadPort.IsOpened)
|
|
{
|
|
Lines.append(QStringLiteral("已连接 USB Vendor 接口。"));
|
|
}
|
|
if (!p_State->DriBlePort.TextEndpointSummary.isEmpty())
|
|
{
|
|
Lines.append(p_State->DriBlePort.TextEndpointSummary);
|
|
}
|
|
if (Lines.isEmpty())
|
|
{
|
|
Lines.append(QStringLiteral("未连接到目标设备。"));
|
|
}
|
|
|
|
p_State->TextConnection = Lines.join(QLatin1Char('\n'));
|
|
|
|
return (OldNumLock != p_State->IsSystemNumLockOn) ||
|
|
(OldConnected != p_State->IsConnected) ||
|
|
(OldConnection != p_State->TextConnection) ||
|
|
(OldSendTransport != p_State->ActiveSendTransport);
|
|
}
|
|
|
|
void Lgc_Core_HandleNkroPacket(Lgc_Core_Struct_State* p_State, const Mid_Struct_RawPacket& Packet)
|
|
{
|
|
Lgc_Core_UpdateSendTransportByPacket(p_State, Packet.Source);
|
|
|
|
QString ExplainText = QStringLiteral("NKRO");
|
|
if (!Packet.ByteArray.isEmpty() &&
|
|
(static_cast<quint8>(Packet.ByteArray.at(0)) == Mid_Enum_ReportId_Nkro) &&
|
|
(Packet.ByteArray.size() == MID_CONST_PACKET_SIZE_NKRO))
|
|
{
|
|
const quint8 Modifier = static_cast<quint8>(Packet.ByteArray.at(1));
|
|
const QByteArray UsageBitmap = Packet.ByteArray.mid(2, MID_CONST_USAGE_BITMAP_SIZE);
|
|
QVector<quint16> UsageList;
|
|
QStringList UsageTextList;
|
|
Lgc_Core_CollectKeyboardUsage(UsageBitmap, &UsageList, &UsageTextList);
|
|
|
|
p_State->IsVisibleKeyStateValid = true;
|
|
p_State->VisibleUsageList = UsageList;
|
|
ExplainText = QStringLiteral("NKRO %1 / %2")
|
|
.arg(Mid_GetModifierText(Modifier),
|
|
UsageTextList.isEmpty() ? QStringLiteral("无按键") : UsageTextList.join(QStringLiteral(", ")));
|
|
}
|
|
Lgc_Core_AppendPacketLog(p_State, QStringLiteral("收到"), Packet.PortName, Packet.ByteArray, ExplainText);
|
|
}
|
|
|
|
void Lgc_Core_HandleConsumerPacket(Lgc_Core_Struct_State* p_State, const Mid_Struct_RawPacket& Packet)
|
|
{
|
|
Lgc_Core_UpdateSendTransportByPacket(p_State, Packet.Source);
|
|
|
|
QString ExplainText = QStringLiteral("Consumer");
|
|
if (!Packet.ByteArray.isEmpty() &&
|
|
(static_cast<quint8>(Packet.ByteArray.at(0)) == Mid_Enum_ReportId_Consumer) &&
|
|
(Packet.ByteArray.size() == MID_CONST_PACKET_SIZE_CONSUMER))
|
|
{
|
|
const quint16 Usage =
|
|
static_cast<quint8>(Packet.ByteArray.at(1)) |
|
|
(static_cast<quint16>(static_cast<quint8>(Packet.ByteArray.at(2))) << 8);
|
|
ExplainText = QStringLiteral("Consumer %1").arg(Mid_GetConsumerUsageText(Usage));
|
|
}
|
|
Lgc_Core_AppendPacketLog(p_State, QStringLiteral("收到"), Packet.PortName, Packet.ByteArray, ExplainText);
|
|
}
|
|
|
|
void Lgc_Core_HandleVendorPacket(Lgc_Core_Struct_State* p_State, const Mid_Struct_RawPacket& Packet)
|
|
{
|
|
Lgc_Core_UpdateSendTransportByPacket(p_State, Packet.Source);
|
|
|
|
QString ExplainText = QStringLiteral("Vendor");
|
|
if (Packet.ByteArray.isEmpty())
|
|
{
|
|
Lgc_Core_AppendPacketLog(p_State, QStringLiteral("收到"), Packet.PortName, Packet.ByteArray, ExplainText);
|
|
return;
|
|
}
|
|
|
|
const quint8 ReportId = static_cast<quint8>(Packet.ByteArray.at(0));
|
|
if ((ReportId == Mid_Enum_ReportId_Vendor) && (Packet.ByteArray.size() == MID_CONST_PACKET_SIZE_VENDOR))
|
|
{
|
|
const quint8 Modifier = static_cast<quint8>(Packet.ByteArray.at(1));
|
|
const QByteArray UsageBitmap = Packet.ByteArray.mid(2, MID_CONST_USAGE_BITMAP_SIZE);
|
|
QVector<quint16> UsageList;
|
|
QStringList UsageTextList;
|
|
Lgc_Core_CollectKeyboardUsage(UsageBitmap, &UsageList, &UsageTextList);
|
|
p_State->IsPhysicalKeyStateValid = true;
|
|
p_State->PhysicalUsageList = UsageList;
|
|
ExplainText = QStringLiteral("Vendor %1 / %2")
|
|
.arg(Mid_GetModifierText(Modifier),
|
|
UsageTextList.isEmpty() ? QStringLiteral("无按键") : UsageTextList.join(QStringLiteral(", ")));
|
|
}
|
|
else if ((ReportId == Mid_Enum_ReportId_VendorCommand) && (Packet.ByteArray.size() >= 2))
|
|
{
|
|
ExplainText = QStringLiteral("VendorCmd 0x%1")
|
|
.arg(static_cast<quint8>(Packet.ByteArray.at(1)), 2, 16, QLatin1Char('0'))
|
|
.toUpper();
|
|
}
|
|
Lgc_Core_AppendPacketLog(p_State, QStringLiteral("收到"), Packet.PortName, Packet.ByteArray, ExplainText);
|
|
}
|
|
|
|
void Lgc_Core_HandleBlePacket(Lgc_Core_Struct_State* p_State, const Mid_Struct_RawPacket& Packet)
|
|
{
|
|
Lgc_Core_UpdateSendTransportByPacket(p_State, Packet.Source);
|
|
|
|
if (Lgc_Core_HandleBleHidPacket(p_State, Packet))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (Packet.Source != Mid_Enum_RawPacketSource_BleGatt)
|
|
{
|
|
Lgc_Core_AppendPacketLog(p_State, QStringLiteral("收到"), Packet.PortName, Packet.ByteArray, QStringLiteral("BLE"));
|
|
return;
|
|
}
|
|
|
|
const QString ExplainText = Packet.ByteArray.isEmpty()
|
|
? QStringLiteral("BLE")
|
|
: QStringLiteral("BLE %1").arg(Mid_GetBleOpcodeText(static_cast<quint8>(Packet.ByteArray.at(0))));
|
|
Lgc_Core_AppendPacketLog(p_State, QStringLiteral("收到"), Packet.PortName, Packet.ByteArray, ExplainText);
|
|
}
|
|
|
|
bool Lgc_Core_HandleFunctionButtons(Lgc_Core_Struct_State* p_State)
|
|
{
|
|
if (!p_State->IsPhysicalKeyStateValid)
|
|
{
|
|
p_State->LastPhysicalUsageList.clear();
|
|
return false;
|
|
}
|
|
|
|
if (p_State->IsFunctionSequenceRecording)
|
|
{
|
|
p_State->LastPhysicalUsageList = p_State->PhysicalUsageList;
|
|
return false;
|
|
}
|
|
|
|
bool IsChanged = false;
|
|
for (quint16 Usage : p_State->PhysicalUsageList)
|
|
{
|
|
if (p_State->LastPhysicalUsageList.contains(Usage) ||
|
|
!Lgc_FunctionButton_HasUsageFeature(p_State->FunctionButtonConfig, Usage))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
QString TextStatus;
|
|
if (!Lgc_FunctionButton_RunBinding(p_State, Usage, &TextStatus) || TextStatus.isEmpty())
|
|
{
|
|
continue;
|
|
}
|
|
|
|
p_State->TextFunctionStatus = TextStatus;
|
|
Lgc_Core_AppendStatusLog(p_State, TextStatus);
|
|
IsChanged = true;
|
|
}
|
|
|
|
p_State->LastPhysicalUsageList = p_State->PhysicalUsageList;
|
|
return IsChanged;
|
|
}
|
|
|
|
|