添加项目文件。

This commit is contained in:
2026-03-26 10:45:29 +08:00
parent 10441f488c
commit b576d3f19d
48 changed files with 4812 additions and 0 deletions

41
LOGIC/Lgc_Consumer.cpp Normal file
View File

@@ -0,0 +1,41 @@
#include "LOGIC/Lgc_Consumer.h"
void Lgc_Consumer_Func_Parse(const QByteArray& ByteArray, Lgc_Consumer_Struct_Result* p_Result)
{
// 当前调用链内部固定传有效结果对象,这里直接清成默认状态。
*p_Result = Lgc_Consumer_Struct_Result();
// DRI 这轮没有交上来数据时,直接给出提示。
if (ByteArray.isEmpty())
{
p_Result->TextExplain = QStringLiteral("Consumer 端口没有收到数据。");
return;
}
// 第 0 字节不是 0x03就说明这不是 Consumer 包。
if (static_cast<quint8>(ByteArray.at(0)) != Mid_Enum_ReportId_Consumer)
{
p_Result->TextExplain = QStringLiteral("这不是 report id 0x03。");
return;
}
p_Result->IsMatch = true;
// 当前固件里的 0x03 包固定就是 3 字节。
if (ByteArray.size() != MID_CONST_PACKET_SIZE_CONSUMER)
{
p_Result->TextExplain = QStringLiteral("0x03 包长度不对。");
return;
}
p_Result->IsLengthOk = true;
// 第 1、2 字节按 little-endian 规则还原成 16 位 usage。
p_Result->Usage =
static_cast<quint8>(ByteArray.at(1)) |
(static_cast<quint16>(static_cast<quint8>(ByteArray.at(2))) << 8);
// 当前项目里只保留简短解释,避免调试日志过于冗长。
p_Result->TextExplain = QStringLiteral("0x03 Consumer%1")
.arg(Mid_Func_GetConsumerUsageText(p_Result->Usage));
}

31
LOGIC/Lgc_Consumer.h Normal file
View File

@@ -0,0 +1,31 @@
#pragma once
#include "MID/Mid_Def.h"
/*
* 这份文件负责解析 report id 0x03 的 Consumer 包。
*
* 当前下位机真实结构很简单:
* 1. 第 0 字节是 report id固定为 0x03
* 2. 第 1、2 字节拼成一个 16 位 consumer usage
* 3. 整包固定长度 3 字节
*
* 所以这里不需要像 0x01 / 0x04 那样去拆 modifier 和 usage 位图。
*/
struct Lgc_Consumer_Struct_Result
{
// 这一包是否匹配 report id 0x03。
bool IsMatch = false;
// 如果匹配 0x03长度是否也正确。
bool IsLengthOk = false;
// 解析得到的 consumer usage。
quint16 Usage = 0;
// 给调试窗口显示的简短中文说明。
QString TextExplain;
};
void Lgc_Consumer_Func_Parse(const QByteArray& ByteArray, Lgc_Consumer_Struct_Result* p_Result);

606
LOGIC/Lgc_Core.cpp Normal file
View File

@@ -0,0 +1,606 @@
#include "LOGIC/Lgc_Core.h"
#include "MID/Mid_Def.h"
#include <QtCore/QDateTime>
#include <QtCore/QStringList>
#include <Windows.h>
namespace
{
/* ---------- 日志与状态文本 ---------- */
QString Lgc_Core_Func_GetTimeText()
{
return QDateTime::currentDateTime().toString(QStringLiteral("HH:mm:ss.zzz"));
}
void Lgc_Core_Func_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_Func_AppendStatusLog(Lgc_Core_Struct_State* p_State, const QString& Text)
{
if (!Text.isEmpty())
{
Lgc_Core_Func_AppendLog(
p_State,
QStringLiteral("[%1] 状态\n%2").arg(Lgc_Core_Func_GetTimeText(), Text));
}
}
void Lgc_Core_Func_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_Func_GetTimeText(), ActionText, PortName, Mid_Func_GetHexText(Packet));
if (!ExplainText.isEmpty())
{
Text.append(QLatin1Char('\n'));
Text.append(ExplainText);
}
Lgc_Core_Func_AppendLog(p_State, Text);
}
struct Lgc_Core_Struct_MaskStateBackup
{
QByteArray FunctionMaskBitmap;
QByteArray SwapMaskBitmap;
QByteArray KeyboardMaskBitmap;
bool IsSwapModeOn = false;
quint16 SwapUsageLeft = 0;
quint16 SwapUsageRight = 0;
bool IsSwapLeftPhysicalPressed = false;
bool IsSwapRightPhysicalPressed = false;
};
/* ---------- 按键状态与掩码 ---------- */
void Lgc_Core_Func_ClearAllKeyStates(Lgc_Core_Struct_State* p_State)
{
p_State->IsVisibleKeyStateValid = false;
p_State->VisibleModifier = 0;
p_State->VisibleUsageList.clear();
p_State->IsPhysicalKeyStateValid = false;
p_State->PhysicalModifier = 0;
p_State->PhysicalUsageList.clear();
p_State->LastPhysicalUsageList.clear();
p_State->IsSwapLeftPhysicalPressed = false;
p_State->IsSwapRightPhysicalPressed = false;
}
void Lgc_Core_Func_CloseAllPorts(Lgc_Core_Struct_State* p_State)
{
Dri_NkroRaw_Func_Close(&p_State->DriNkroPort);
Dri_Consumer_Func_Close(&p_State->DriConsumerPort);
Dri_Vendor_Func_Close(&p_State->DriVendorPort);
}
Lgc_Core_Struct_MaskStateBackup Lgc_Core_Func_BackupMaskState(const Lgc_Core_Struct_State* p_State)
{
return {
p_State->FunctionMaskBitmap,
p_State->SwapMaskBitmap,
p_State->KeyboardMaskBitmap,
p_State->IsSwapModeOn,
p_State->SwapUsageLeft,
p_State->SwapUsageRight,
p_State->IsSwapLeftPhysicalPressed,
p_State->IsSwapRightPhysicalPressed
};
}
void Lgc_Core_Func_RestoreMaskState(
Lgc_Core_Struct_State* p_State,
const Lgc_Core_Struct_MaskStateBackup& Backup)
{
p_State->FunctionMaskBitmap = Backup.FunctionMaskBitmap;
p_State->SwapMaskBitmap = Backup.SwapMaskBitmap;
p_State->KeyboardMaskBitmap = Backup.KeyboardMaskBitmap;
p_State->IsSwapModeOn = Backup.IsSwapModeOn;
p_State->SwapUsageLeft = Backup.SwapUsageLeft;
p_State->SwapUsageRight = Backup.SwapUsageRight;
p_State->IsSwapLeftPhysicalPressed = Backup.IsSwapLeftPhysicalPressed;
p_State->IsSwapRightPhysicalPressed = Backup.IsSwapRightPhysicalPressed;
}
bool Lgc_Core_Func_IsUsageEnabledInMask(const QByteArray& UsageBitmap, quint16 Usage)
{
const int ByteIndex = Usage / 8;
if (ByteIndex >= UsageBitmap.size())
{
return false;
}
return (static_cast<quint8>(UsageBitmap.at(ByteIndex)) &
static_cast<quint8>(1U << (Usage % 8))) != 0;
}
void Lgc_Core_Func_SetUsageEnabledInMask(QByteArray* p_UsageBitmap, quint16 Usage, bool IsEnabled)
{
const int ByteIndex = Usage / 8;
if (ByteIndex >= p_UsageBitmap->size())
{
return;
}
const quint8 BitMask = static_cast<quint8>(1U << (Usage % 8));
quint8 Value = static_cast<quint8>(p_UsageBitmap->at(ByteIndex));
Value = IsEnabled ? static_cast<quint8>(Value | BitMask) : static_cast<quint8>(Value & static_cast<quint8>(~BitMask));
(*p_UsageBitmap)[ByteIndex] = static_cast<char>(Value);
}
void Lgc_Core_Func_FillMaskAllEnabled(QByteArray* p_UsageBitmap)
{
*p_UsageBitmap = QByteArray(MID_CONST_USAGE_BITMAP_SIZE, static_cast<char>(0xFF));
}
void Lgc_Core_Func_RebuildKeyboardMask(Lgc_Core_Struct_State* p_State)
{
if (p_State->FunctionMaskBitmap.size() != MID_CONST_USAGE_BITMAP_SIZE)
{
Lgc_Core_Func_FillMaskAllEnabled(&p_State->FunctionMaskBitmap);
}
if (p_State->SwapMaskBitmap.size() != MID_CONST_USAGE_BITMAP_SIZE)
{
Lgc_Core_Func_FillMaskAllEnabled(&p_State->SwapMaskBitmap);
}
p_State->KeyboardMaskBitmap = QByteArray(MID_CONST_USAGE_BITMAP_SIZE, static_cast<char>(0xFF));
for (int Index = 0; Index < MID_CONST_USAGE_BITMAP_SIZE; ++Index)
{
p_State->KeyboardMaskBitmap[Index] = static_cast<char>(
static_cast<quint8>(p_State->FunctionMaskBitmap.at(Index)) &
static_cast<quint8>(p_State->SwapMaskBitmap.at(Index)));
}
}
QByteArray Lgc_Core_Func_BuildVendorMaskPacket(const Lgc_Core_Struct_State* p_State)
{
QByteArray Packet(MID_CONST_PACKET_SIZE_VENDOR, 0);
Packet[0] = static_cast<char>(Mid_Enum_ReportId_Vendor);
Packet[1] = static_cast<char>(0xFF);
for (int Index = 0; Index < MID_CONST_USAGE_BITMAP_SIZE; ++Index)
{
Packet[2 + Index] = p_State->KeyboardMaskBitmap.at(Index);
}
return Packet;
}
bool Lgc_Core_Func_WriteCurrentMask(
Lgc_Core_Struct_State* p_State,
QString* p_TextStatus,
QByteArray* p_Packet)
{
Lgc_Core_Func_RebuildKeyboardMask(p_State);
if (p_Packet != nullptr)
{
*p_Packet = Lgc_Core_Func_BuildVendorMaskPacket(p_State);
}
if (!p_State->DriVendorPort.IsOpened)
{
if (p_TextStatus != nullptr)
{
*p_TextStatus = QStringLiteral("Vendor 接口未打开,无法同步当前掩码。");
}
return false;
}
const QByteArray Packet = (p_Packet != nullptr) ? *p_Packet : Lgc_Core_Func_BuildVendorMaskPacket(p_State);
return Dri_Vendor_Func_Write(&p_State->DriVendorPort, Packet, p_TextStatus);
}
bool Lgc_Core_Func_CommitMaskChange(
Lgc_Core_Struct_State* p_State,
const Lgc_Core_Struct_MaskStateBackup& Backup,
const QString& ExplainText)
{
QString TextStatus;
QByteArray Packet;
if (!Lgc_Core_Func_WriteCurrentMask(p_State, &TextStatus, &Packet))
{
Lgc_Core_Func_RestoreMaskState(p_State, Backup);
Lgc_Core_Func_AppendStatusLog(p_State, TextStatus);
return false;
}
Lgc_Core_Func_AppendStatusLog(p_State, TextStatus);
Lgc_Core_Func_AppendPacketLog(p_State, QStringLiteral("鍙戦€?"), QStringLiteral("Vendor"), Packet, ExplainText);
return true;
}
/* ---------- 数据包解析 ---------- */
bool Lgc_Core_Func_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;
p_State->IsSystemNumLockOn = (GetKeyState(VK_NUMLOCK) & 0x0001) != 0;
p_State->IsConnected = p_State->DriVendorPort.IsOpened;
p_State->TextConnection = p_State->IsConnected
? QStringLiteral("已连接到 0xFF00 / 0x0002 Vendor 接口。")
: QStringLiteral("未连接到目标 Vendor 接口。");
return (OldNumLock != p_State->IsSystemNumLockOn) ||
(OldConnected != p_State->IsConnected) ||
(OldConnection != p_State->TextConnection);
}
void Lgc_Core_Func_HandleNkroPacket(Lgc_Core_Struct_State* p_State, const Mid_Struct_RawPacket& Packet)
{
Lgc_Nkro_Struct_Result Result;
Lgc_Nkro_Func_Parse(Packet.ByteArray, &Result);
if (Result.IsMatch && Result.IsLengthOk)
{
p_State->IsVisibleKeyStateValid = true;
p_State->VisibleModifier = Result.Modifier;
p_State->VisibleUsageList = Result.UsageList;
}
Lgc_Core_Func_AppendPacketLog(p_State, QStringLiteral("收到"), Packet.PortName, Packet.ByteArray, Result.TextExplain);
}
void Lgc_Core_Func_HandleConsumerPacket(Lgc_Core_Struct_State* p_State, const Mid_Struct_RawPacket& Packet)
{
Lgc_Consumer_Struct_Result Result;
Lgc_Consumer_Func_Parse(Packet.ByteArray, &Result);
Lgc_Core_Func_AppendPacketLog(p_State, QStringLiteral("收到"), Packet.PortName, Packet.ByteArray, Result.TextExplain);
}
void Lgc_Core_Func_HandleVendorPacket(Lgc_Core_Struct_State* p_State, const Mid_Struct_RawPacket& Packet)
{
Lgc_Vendor_Struct_Result Result;
Lgc_Vendor_Func_Parse(Packet.ByteArray, &Result);
if (Result.IsMatch && Result.IsLengthOk && Result.VendorState.IsValid)
{
p_State->IsPhysicalKeyStateValid = true;
p_State->PhysicalModifier = Result.VendorState.Modifier;
p_State->PhysicalUsageList = Result.VendorState.UsageList;
}
Lgc_Core_Func_AppendPacketLog(p_State, QStringLiteral("收到"), Packet.PortName, Packet.ByteArray, Result.TextExplain);
}
/* ---------- 功能键与交换逻辑 ---------- */
bool Lgc_Core_Func_HandleFunctionButtons(Lgc_Core_Struct_State* p_State)
{
if (!p_State->IsPhysicalKeyStateValid)
{
p_State->LastPhysicalUsageList.clear();
return false;
}
bool IsChanged = false;
for (quint16 Usage : p_State->PhysicalUsageList)
{
if (p_State->LastPhysicalUsageList.contains(Usage) || !Lgc_Core_Func_IsUsageFunctionMode(p_State, Usage))
{
continue;
}
QString TextStatus;
if (!Lgc_Func_Button_Func_HandlePressedUsage(p_State, Usage, &TextStatus) || TextStatus.isEmpty())
{
continue;
}
p_State->TextFunctionStatus = TextStatus;
Lgc_Core_Func_AppendStatusLog(p_State, TextStatus);
IsChanged = true;
}
p_State->LastPhysicalUsageList = p_State->PhysicalUsageList;
return IsChanged;
}
void Lgc_Core_Func_ReleaseSwapOutputs(
quint16 UsageLeft,
quint16 UsageRight,
bool IsSwapLeftPhysicalPressed,
bool IsSwapRightPhysicalPressed)
{
if (IsSwapLeftPhysicalPressed)
{
Lgc_Func_Button_Func_SendUsageToWindows(UsageRight, false);
}
if (IsSwapRightPhysicalPressed)
{
Lgc_Func_Button_Func_SendUsageToWindows(UsageLeft, false);
}
}
void Lgc_Core_Func_ReleaseSwapOutputs(Lgc_Core_Struct_State* p_State)
{
Lgc_Core_Func_ReleaseSwapOutputs(
p_State->SwapUsageLeft,
p_State->SwapUsageRight,
p_State->IsSwapLeftPhysicalPressed,
p_State->IsSwapRightPhysicalPressed);
p_State->IsSwapLeftPhysicalPressed = false;
p_State->IsSwapRightPhysicalPressed = false;
}
bool Lgc_Core_Func_HandleSwapSource(
Lgc_Core_Struct_State* p_State,
quint16 PhysicalUsage,
quint16 TargetUsage,
bool* p_IsPhysicalPressed)
{
const bool IsPressedNow = p_State->PhysicalUsageList.contains(PhysicalUsage);
if (IsPressedNow == *p_IsPhysicalPressed)
{
return false;
}
if (!Lgc_Func_Button_Func_SendUsageToWindows(TargetUsage, IsPressedNow))
{
p_State->TextFunctionStatus = QStringLiteral("按键交换补发失败。");
Lgc_Core_Func_AppendStatusLog(p_State, p_State->TextFunctionStatus);
return true;
}
*p_IsPhysicalPressed = IsPressedNow;
return true;
}
bool Lgc_Core_Func_HandleSwapMode(Lgc_Core_Struct_State* p_State)
{
const bool HadInjectedKey = p_State->IsSwapLeftPhysicalPressed || p_State->IsSwapRightPhysicalPressed;
if (!p_State->IsSwapModeOn || !p_State->IsPhysicalKeyStateValid)
{
Lgc_Core_Func_ReleaseSwapOutputs(p_State);
return HadInjectedKey;
}
bool IsChanged = false;
IsChanged |= Lgc_Core_Func_HandleSwapSource(
p_State,
p_State->SwapUsageLeft,
p_State->SwapUsageRight,
&p_State->IsSwapLeftPhysicalPressed);
IsChanged |= Lgc_Core_Func_HandleSwapSource(
p_State,
p_State->SwapUsageRight,
p_State->SwapUsageLeft,
&p_State->IsSwapRightPhysicalPressed);
return IsChanged;
}
} // namespace
/* ---------- 对外接口 ---------- */
void Lgc_Core_Func_Init(Lgc_Core_Struct_State* p_State)
{
p_State->DriNkroPort = Dri_NkroRaw_Struct_Port();
p_State->DriConsumerPort = Dri_Consumer_Struct_Port();
p_State->DriVendorPort = Dri_Vendor_Struct_Port();
p_State->DeviceConfig = Mid_Struct_DeviceConfig();
p_State->TextConnection = QStringLiteral("未连接,等待枚举设备。");
p_State->TextLog.clear();
p_State->TextFunctionStatus = QStringLiteral("等待功能键动作。");
Lgc_Core_Func_ClearAllKeyStates(p_State);
p_State->IsSystemNumLockOn = (GetKeyState(VK_NUMLOCK) & 0x0001) != 0;
Lgc_Core_Func_FillMaskAllEnabled(&p_State->FunctionMaskBitmap);
Lgc_Core_Func_FillMaskAllEnabled(&p_State->SwapMaskBitmap);
Lgc_Core_Func_FillMaskAllEnabled(&p_State->KeyboardMaskBitmap);
p_State->FunctionButtonConfig = Lgc_Func_Button_Struct_Config();
p_State->SwapUsageLeft = p_State->FunctionButtonConfig.SwapUsageLeft;
p_State->SwapUsageRight = p_State->FunctionButtonConfig.SwapUsageRight;
p_State->WindowHandle = nullptr;
p_State->IsConnected = false;
p_State->IsStarted = false;
}
void Lgc_Core_Func_SetWindowHandle(Lgc_Core_Struct_State* p_State, void* WindowHandle)
{
p_State->WindowHandle = WindowHandle;
}
void Lgc_Core_Func_HandleNativeMessage(Lgc_Core_Struct_State* p_State, void* p_Message)
{
QString TextStatus;
Dri_NkroRaw_Func_HandleNativeMessage(&p_State->DriNkroPort, p_Message, &TextStatus);
Lgc_Core_Func_AppendStatusLog(p_State, TextStatus);
}
void Lgc_Core_Func_Start(Lgc_Core_Struct_State* p_State)
{
if (p_State->IsStarted)
{
return;
}
p_State->IsStarted = true;
Lgc_Core_Func_RefreshDevice(p_State);
}
void Lgc_Core_Func_Close(Lgc_Core_Struct_State* p_State)
{
Lgc_Core_Func_ReleaseSwapOutputs(p_State);
Lgc_Core_Func_CloseAllPorts(p_State);
p_State->TextConnection = QStringLiteral("未连接,等待枚举设备。");
p_State->TextFunctionStatus = QStringLiteral("等待功能键动作。");
p_State->IsConnected = false;
Lgc_Core_Func_ClearAllKeyStates(p_State);
}
void Lgc_Core_Func_RefreshDevice(Lgc_Core_Struct_State* p_State)
{
Lgc_Core_Func_ReleaseSwapOutputs(p_State);
Lgc_Core_Func_CloseAllPorts(p_State);
Lgc_Core_Func_ClearAllKeyStates(p_State);
const auto OpenPort = [p_State](auto OpenFunc, auto* p_Port, auto... Args)
{
QString TextStatus;
OpenFunc(p_Port, Args..., &TextStatus);
Lgc_Core_Func_AppendStatusLog(p_State, TextStatus);
};
OpenPort(Dri_NkroRaw_Func_Open, &p_State->DriNkroPort, p_State->DeviceConfig, p_State->WindowHandle);
OpenPort(Dri_Consumer_Func_Open, &p_State->DriConsumerPort, p_State->DeviceConfig);
OpenPort(Dri_Vendor_Func_Open, &p_State->DriVendorPort, p_State->DeviceConfig);
QString TextStatus;
TextStatus.clear();
Lgc_Core_Func_WriteCurrentMask(p_State, &TextStatus, nullptr);
Lgc_Core_Func_AppendStatusLog(p_State, TextStatus);
Lgc_Core_Func_SyncSystemState(p_State);
}
void Lgc_Core_Func_ClearLog(Lgc_Core_Struct_State* p_State)
{
p_State->TextLog.clear();
}
bool Lgc_Core_Func_Poll(Lgc_Core_Struct_State* p_State)
{
bool IsChanged = false;
Mid_Struct_RawPacket Packet;
const auto PollPort = [p_State, &Packet, &IsChanged](auto ReadFunc, auto* p_Port, auto HandlePacket)
{
QString TextStatus;
if (ReadFunc(p_Port, &Packet, &TextStatus))
{
HandlePacket(p_State, Packet);
IsChanged = true;
}
if (!TextStatus.isEmpty())
{
Lgc_Core_Func_AppendStatusLog(p_State, TextStatus);
IsChanged = true;
}
};
PollPort(Dri_NkroRaw_Func_Read, &p_State->DriNkroPort, Lgc_Core_Func_HandleNkroPacket);
PollPort(Dri_Consumer_Func_Read, &p_State->DriConsumerPort, Lgc_Core_Func_HandleConsumerPacket);
PollPort(Dri_Vendor_Func_Read, &p_State->DriVendorPort, Lgc_Core_Func_HandleVendorPacket);
IsChanged |= Lgc_Core_Func_HandleFunctionButtons(p_State);
IsChanged |= Lgc_Core_Func_HandleSwapMode(p_State);
IsChanged |= Lgc_Core_Func_SyncSystemState(p_State);
return IsChanged;
}
bool Lgc_Core_Func_SetUsageFunctionMode(Lgc_Core_Struct_State* p_State, quint16 Usage, bool IsEnabled)
{
if (p_State->FunctionMaskBitmap.size() != MID_CONST_USAGE_BITMAP_SIZE)
{
Lgc_Core_Func_FillMaskAllEnabled(&p_State->FunctionMaskBitmap);
}
if (Lgc_Core_Func_IsUsageFunctionMode(p_State, Usage) == IsEnabled)
{
return true;
}
const Lgc_Core_Struct_MaskStateBackup Backup = Lgc_Core_Func_BackupMaskState(p_State);
Lgc_Core_Func_SetUsageEnabledInMask(&p_State->FunctionMaskBitmap, Usage, !IsEnabled);
const QString ExplainText = IsEnabled
? QStringLiteral("已把 %1 切到功能键模式。").arg(Mid_Func_GetKeyboardUsageText(Usage))
: QStringLiteral("已恢复 %1 的普通键模式。").arg(Mid_Func_GetKeyboardUsageText(Usage));
return Lgc_Core_Func_CommitMaskChange(p_State, Backup, ExplainText);
}
bool Lgc_Core_Func_SetSwapMode(
Lgc_Core_Struct_State* p_State,
quint16 UsageLeft,
quint16 UsageRight,
bool IsEnabled)
{
if ((UsageLeft == 0) || (UsageRight == 0) || (UsageLeft == UsageRight))
{
return false;
}
if ((p_State->IsSwapModeOn == IsEnabled) &&
(p_State->SwapUsageLeft == UsageLeft) &&
(p_State->SwapUsageRight == UsageRight))
{
return true;
}
const Lgc_Core_Struct_MaskStateBackup Backup = Lgc_Core_Func_BackupMaskState(p_State);
p_State->IsSwapModeOn = IsEnabled;
p_State->SwapUsageLeft = UsageLeft;
p_State->SwapUsageRight = UsageRight;
Lgc_Core_Func_FillMaskAllEnabled(&p_State->SwapMaskBitmap);
if (IsEnabled)
{
Lgc_Core_Func_SetUsageEnabledInMask(&p_State->SwapMaskBitmap, UsageLeft, false);
Lgc_Core_Func_SetUsageEnabledInMask(&p_State->SwapMaskBitmap, UsageRight, false);
}
p_State->IsSwapLeftPhysicalPressed = false;
p_State->IsSwapRightPhysicalPressed = false;
const QString ExplainText = IsEnabled
? QStringLiteral("已开启按键交换:%1 <-> %2")
.arg(Mid_Func_GetKeyboardUsageText(UsageLeft))
.arg(Mid_Func_GetKeyboardUsageText(UsageRight))
: QStringLiteral("已关闭按键交换:%1 <-> %2")
.arg(Mid_Func_GetKeyboardUsageText(UsageLeft))
.arg(Mid_Func_GetKeyboardUsageText(UsageRight));
if (!Lgc_Core_Func_CommitMaskChange(p_State, Backup, ExplainText))
{
return false;
}
if (Backup.IsSwapModeOn &&
(!IsEnabled || (Backup.SwapUsageLeft != UsageLeft) || (Backup.SwapUsageRight != UsageRight)))
{
Lgc_Core_Func_ReleaseSwapOutputs(
Backup.SwapUsageLeft,
Backup.SwapUsageRight,
Backup.IsSwapLeftPhysicalPressed,
Backup.IsSwapRightPhysicalPressed);
}
return true;
}
bool Lgc_Core_Func_IsUsageFunctionMode(const Lgc_Core_Struct_State* p_State, quint16 Usage)
{
if (p_State->FunctionMaskBitmap.size() != MID_CONST_USAGE_BITMAP_SIZE)
{
return false;
}
return !Lgc_Core_Func_IsUsageEnabledInMask(p_State->FunctionMaskBitmap, Usage);
}

85
LOGIC/Lgc_Core.h Normal file
View File

@@ -0,0 +1,85 @@
#pragma once
#include "DRI/Dri_Consumer.h"
#include "DRI/Dri_NkroRaw.h"
#include "DRI/Dri_Vendor.h"
#include "LOGIC/Lgc_Consumer.h"
#include "LOGIC/Lgc_Func_Button.h"
#include "LOGIC/Lgc_Nkro.h"
#include "LOGIC/Lgc_Vendor.h"
#include <QtCore/QByteArray>
/*
* Lgc_Core贯穿 UI / LOGIC / DRI 的单一状态容器。
* 高密注释使得学生在不翻源码的情况下即可看懂所有成员职责。
*/
struct Lgc_Core_Struct_State
{
/* DRI 端口:分别承接 RAW NKRO / Consumer / Vendor 通道 */
Dri_NkroRaw_Struct_Port DriNkroPort;
Dri_Consumer_Struct_Port DriConsumerPort;
Dri_Vendor_Struct_Port DriVendorPort;
/* 设备与 UI 文本状态 */
Mid_Struct_DeviceConfig DeviceConfig;
QString TextConnection;
QString TextLog;
QString TextFunctionStatus;
/* 可视化按键UI 模型(来自解析 NKRO/Consumer 结果) */
bool IsVisibleKeyStateValid = false;
quint8 VisibleModifier = 0;
QVector<quint16> VisibleUsageList;
/* 物理按键:直接来自硬件,供 Swap/功能逻辑判断 */
bool IsPhysicalKeyStateValid = false;
quint8 PhysicalModifier = 0;
QVector<quint16> PhysicalUsageList;
QVector<quint16> LastPhysicalUsageList;
/* 键盘模式控制NumLock、功能掩码、Swap 掩码等 */
bool IsSystemNumLockOn = false;
QByteArray FunctionMaskBitmap;
QByteArray SwapMaskBitmap;
QByteArray KeyboardMaskBitmap;
/* 功能键配置及 Swap 对应的运行期状态 */
Lgc_Func_Button_Struct_Config FunctionButtonConfig;
bool IsSwapModeOn = false;
quint16 SwapUsageLeft = 0;
quint16 SwapUsageRight = 0;
bool IsSwapLeftPhysicalPressed = false;
bool IsSwapRightPhysicalPressed = false;
/* 互操作:窗口句柄与核心运行状态 */
void* WindowHandle = nullptr;
bool IsConnected = false;
bool IsStarted = false;
};
/* 初始化核心:清空状态并准备 DRI 端口。 */
void Lgc_Core_Func_Init(Lgc_Core_Struct_State* p_State);
/* 告诉 LGC 使用哪个 HWND 接收 RAWINPUT。 */
void Lgc_Core_Func_SetWindowHandle(Lgc_Core_Struct_State* p_State, void* WindowHandle);
/* 每个 Windows 消息都交给核心处理。 */
void Lgc_Core_Func_HandleNativeMessage(Lgc_Core_Struct_State* p_State, void* p_Message);
/* 启动:打开设备、刷新按键状态并进入轮询。 */
void Lgc_Core_Func_Start(Lgc_Core_Struct_State* p_State);
/* 关闭:释放 DRI 端口与所有资源。 */
void Lgc_Core_Func_Close(Lgc_Core_Struct_State* p_State);
/* 当 VID/PID 变化或用户点击刷新时调用。 */
void Lgc_Core_Func_RefreshDevice(Lgc_Core_Struct_State* p_State);
/* 清空 LOG 文本UI 立即同步。 */
void Lgc_Core_Func_ClearLog(Lgc_Core_Struct_State* p_State);
/* 轮询 DRI 队列,返回“是否发生变化”以供 UI 决定是否刷新。 */
bool Lgc_Core_Func_Poll(Lgc_Core_Struct_State* p_State);
/* 设置单个 Usage 是否是“功能模式” */
bool Lgc_Core_Func_SetUsageFunctionMode(Lgc_Core_Struct_State* p_State, quint16 Usage, bool IsEnabled);
/* 打开/关闭 Swap 模式,并指定左右 Usage */
bool Lgc_Core_Func_SetSwapMode(
Lgc_Core_Struct_State* p_State,
quint16 UsageLeft,
quint16 UsageRight,
bool IsEnabled);
/* 查询给定 Usage 当前是否处于功能模式 */
bool Lgc_Core_Func_IsUsageFunctionMode(const Lgc_Core_Struct_State* p_State, quint16 Usage);

191
LOGIC/Lgc_Func_Button.cpp Normal file
View File

@@ -0,0 +1,191 @@
#include "LOGIC/Lgc_Func_Button.h"
#include "LOGIC/Lgc_Core.h"
#include <QtCore/QUrl>
#include <QtCore/QVector>
#include <QtGui/QDesktopServices>
#include <Windows.h>
namespace
{
QString Lgc_Func_Button_Func_GetUsageShortText(quint16 Usage)
{
switch (Usage)
{
case 0x0056: return QStringLiteral("-");
case 0x0057: return QStringLiteral("+");
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");
default:
return Mid_Func_GetKeyboardUsageText(Usage);
}
}
WORD Lgc_Func_Button_Func_GetWindowsVirtualKey(quint16 Usage)
{
switch (Usage)
{
case 0x0056: return VK_SUBTRACT;
case 0x0057: return VK_ADD;
case 0x0059: return VK_NUMPAD1;
case 0x005A: return VK_NUMPAD2;
case 0x005B: return VK_NUMPAD3;
case 0x005C: return VK_NUMPAD4;
case 0x005D: return VK_NUMPAD5;
case 0x005E: return VK_NUMPAD6;
case 0x005F: return VK_NUMPAD7;
case 0x0060: return VK_NUMPAD8;
case 0x0061: return VK_NUMPAD9;
case 0x0062: return VK_NUMPAD0;
default:
return 0;
}
}
bool Lgc_Func_Button_Func_SendUnicodeText(const QString& Text)
{
if (Text.isEmpty())
{
return false;
}
QVector<INPUT> InputList;
InputList.reserve(Text.size() * 2);
for (QChar Character : Text)
{
INPUT InputDown = {};
InputDown.type = INPUT_KEYBOARD;
InputDown.ki.wScan = Character.unicode();
InputDown.ki.dwFlags = KEYEVENTF_UNICODE;
InputList.append(InputDown);
INPUT InputUp = InputDown;
InputUp.ki.dwFlags = KEYEVENTF_UNICODE | KEYEVENTF_KEYUP;
InputList.append(InputUp);
}
return SendInput(static_cast<UINT>(InputList.size()), InputList.data(), sizeof(INPUT)) ==
static_cast<UINT>(InputList.size());
}
void Lgc_Func_Button_Func_RunMacroText(Lgc_Core_Struct_State* p_State, QString* p_TextStatus)
{
const QString Text = p_State->FunctionButtonConfig.MacroText.trimmed();
if (Text.isEmpty())
{
*p_TextStatus = QStringLiteral("功能键 0 未配置输出文本。");
return;
}
*p_TextStatus = Lgc_Func_Button_Func_SendUnicodeText(Text)
? QStringLiteral("功能键 0 已输出文本:%1").arg(Text)
: QStringLiteral("功能键 0 输出文本失败。");
}
void Lgc_Func_Button_Func_RunSwapKey(Lgc_Core_Struct_State* p_State, QString* p_TextStatus)
{
const quint16 UsageLeft = p_State->FunctionButtonConfig.SwapUsageLeft;
const quint16 UsageRight = p_State->FunctionButtonConfig.SwapUsageRight;
if ((UsageLeft == 0) || (UsageRight == 0) || (UsageLeft == UsageRight))
{
*p_TextStatus = QStringLiteral("功能键 1 的交换配置无效。");
return;
}
if (!Lgc_Core_Func_SetSwapMode(p_State, UsageLeft, UsageRight, !p_State->IsSwapModeOn))
{
*p_TextStatus = QStringLiteral("功能键 1 切换按键交换失败。");
return;
}
*p_TextStatus = p_State->IsSwapModeOn
? QStringLiteral("功能键 1 已开启按键交换:%1 <-> %2")
.arg(Lgc_Func_Button_Func_GetUsageShortText(UsageLeft))
.arg(Lgc_Func_Button_Func_GetUsageShortText(UsageRight))
: QStringLiteral("功能键 1 已关闭按键交换:%1 <-> %2")
.arg(Lgc_Func_Button_Func_GetUsageShortText(UsageLeft))
.arg(Lgc_Func_Button_Func_GetUsageShortText(UsageRight));
}
void Lgc_Func_Button_Func_RunOpenWebsite(Lgc_Core_Struct_State* p_State, QString* p_TextStatus)
{
const QString UrlText = p_State->FunctionButtonConfig.WebsiteUrl.trimmed();
const QUrl Url = QUrl::fromUserInput(UrlText);
if (UrlText.isEmpty() || !Url.isValid() || Url.isEmpty())
{
*p_TextStatus = QStringLiteral("功能键 2 的网址配置无效。");
return;
}
*p_TextStatus = QDesktopServices::openUrl(Url)
? QStringLiteral("功能键 2 已打开网址:%1").arg(Url.toString())
: QStringLiteral("功能键 2 打开网址失败。");
}
} // namespace
bool Lgc_Func_Button_Func_SendUsageToWindows(quint16 Usage, bool IsPressed)
{
const WORD VirtualKey = Lgc_Func_Button_Func_GetWindowsVirtualKey(Usage);
if (VirtualKey == 0)
{
return false;
}
INPUT InputData = {};
InputData.type = INPUT_KEYBOARD;
InputData.ki.wVk = VirtualKey;
if (!IsPressed)
{
InputData.ki.dwFlags = KEYEVENTF_KEYUP;
}
return SendInput(1, &InputData, sizeof(INPUT)) == 1;
}
bool Lgc_Func_Button_Func_HandlePressedUsage(
Lgc_Core_Struct_State* p_State,
quint16 Usage,
QString* p_TextStatus)
{
p_TextStatus->clear();
switch (Usage)
{
case 0x0062:
Lgc_Func_Button_Func_RunMacroText(p_State, p_TextStatus);
return true;
case 0x0059:
Lgc_Func_Button_Func_RunSwapKey(p_State, p_TextStatus);
return true;
case 0x005A:
Lgc_Func_Button_Func_RunOpenWebsite(p_State, p_TextStatus);
return true;
case 0x0056:
case 0x0057:
case 0x005B:
case 0x005C:
case 0x005D:
case 0x005E:
case 0x005F:
case 0x0060:
case 0x0061:
*p_TextStatus = QStringLiteral("功能键 %1 还没有绑定具体功能。")
.arg(Lgc_Func_Button_Func_GetUsageShortText(Usage));
return true;
default:
return false;
}
}

19
LOGIC/Lgc_Func_Button.h Normal file
View File

@@ -0,0 +1,19 @@
#pragma once
#include "MID/Mid_Def.h"
struct Lgc_Core_Struct_State;
struct Lgc_Func_Button_Struct_Config
{
QString MacroText = QStringLiteral("HELLO WORLD!");
quint16 SwapUsageLeft = 0x005C;
quint16 SwapUsageRight = 0x005D;
QString WebsiteUrl = QStringLiteral("https://www.deepseek.com/");
};
bool Lgc_Func_Button_Func_SendUsageToWindows(quint16 Usage, bool IsPressed);
bool Lgc_Func_Button_Func_HandlePressedUsage(
Lgc_Core_Struct_State* p_State,
quint16 Usage,
QString* p_TextStatus);

70
LOGIC/Lgc_Nkro.cpp Normal file
View File

@@ -0,0 +1,70 @@
#include "LOGIC/Lgc_Nkro.h"
void Lgc_Nkro_Func_Parse(const QByteArray& ByteArray, Lgc_Nkro_Struct_Result* p_Result)
{
// 当前调用链内部固定传有效结果对象,这里直接回到默认状态。
*p_Result = Lgc_Nkro_Struct_Result();
// 没有新包时直接返回提示。
if (ByteArray.isEmpty())
{
p_Result->TextExplain = QStringLiteral("NKRO 端口没有收到数据。");
return;
}
// 先校验 report id。
if (static_cast<quint8>(ByteArray.at(0)) != Mid_Enum_ReportId_Nkro)
{
p_Result->TextExplain = QStringLiteral("这不是 report id 0x01。");
return;
}
p_Result->IsMatch = true;
// 当前固件里的 0x01 包固定长度是 31 字节。
if (ByteArray.size() != MID_CONST_PACKET_SIZE_NKRO)
{
p_Result->TextExplain = QStringLiteral("0x01 包长度不对。");
return;
}
p_Result->IsLengthOk = true;
// 第 1 字节是 modifier。
p_Result->Modifier = static_cast<quint8>(ByteArray.at(1));
// 后面 29 字节是 usage 位图。
p_Result->UsageBitmap = ByteArray.mid(2, MID_CONST_USAGE_BITMAP_SIZE);
// 逐 bit 扫描位图,得到所有按下的 HID usage。
for (quint16 Usage = 0; Usage <= MID_CONST_KEYBOARD_USAGE_MAX; ++Usage)
{
const int ByteIndex = Usage / 8;
const int BitIndex = Usage % 8;
const quint8 Value = static_cast<quint8>(p_Result->UsageBitmap.at(ByteIndex));
if ((Value & static_cast<quint8>(1U << BitIndex)) == 0)
{
continue;
}
p_Result->UsageList.append(Usage);
p_Result->UsageTextList.append(Mid_Func_GetKeyboardUsageText(Usage));
}
// 这里整理的是适合调试窗口直接阅读的教学化文本。
QStringList ExplainList;
ExplainList.append(QStringLiteral("0x01 NKRO 可见键盘态"));
ExplainList.append(QStringLiteral("修饰键:%1").arg(Mid_Func_GetModifierText(p_Result->Modifier)));
if (p_Result->UsageTextList.isEmpty())
{
ExplainList.append(QStringLiteral("按下:无"));
}
else
{
ExplainList.append(QStringLiteral("按下:%1").arg(p_Result->UsageTextList.join(QStringLiteral(", "))));
}
p_Result->TextExplain = ExplainList.join(QLatin1Char('\n'));
}

40
LOGIC/Lgc_Nkro.h Normal file
View File

@@ -0,0 +1,40 @@
#pragma once
#include "MID/Mid_Def.h"
/*
* 这份文件负责解析 report id 0x01 的 NKRO 包。
*
* 这里解析的是“Windows 最终可见的键盘态”:
* - 会受下位机遮罩影响
* - 不是 0x04 那种物理镜像流
*
* 当前下位机结构固定为:
* [report_id(1) | modifier(1) | usage_bitmap(29)]
* 总长度 31 字节
*/
struct Lgc_Nkro_Struct_Result
{
// 首字节是否匹配 0x01。
bool IsMatch = false;
// 长度是否符合 31 字节。
bool IsLengthOk = false;
// 第 1 字节修饰键位图。
quint8 Modifier = 0;
// 第 2~30 字节 usage 位图原文。
QByteArray UsageBitmap;
// 展开后的 HID usage 列表。
QVector<quint16> UsageList;
// 展开后的按键名称列表。
QStringList UsageTextList;
// 给调试窗口用的简短中文说明。
QString TextExplain;
};
void Lgc_Nkro_Func_Parse(const QByteArray& ByteArray, Lgc_Nkro_Struct_Result* p_Result);

71
LOGIC/Lgc_Vendor.cpp Normal file
View File

@@ -0,0 +1,71 @@
#include "LOGIC/Lgc_Vendor.h"
void Lgc_Vendor_Func_Parse(const QByteArray& ByteArray, Lgc_Vendor_Struct_Result* p_Result)
{
// 当前调用链内部固定传有效结果对象,这里直接回到默认状态。
*p_Result = Lgc_Vendor_Struct_Result();
// 没有包时直接返回提示。
if (ByteArray.isEmpty())
{
p_Result->TextExplain = QStringLiteral("Vendor 端口没有收到数据。");
return;
}
// 第 0 字节不是 0x04就不是当前要解析的 Vendor 镜像包。
if (static_cast<quint8>(ByteArray.at(0)) != Mid_Enum_ReportId_Vendor)
{
p_Result->TextExplain = QStringLiteral("这不是 report id 0x04。");
return;
}
p_Result->IsMatch = true;
// 当前固件里的 0x04 固定长度也是 31 字节。
if (ByteArray.size() != MID_CONST_PACKET_SIZE_VENDOR)
{
p_Result->TextExplain = QStringLiteral("0x04 包长度不对。");
return;
}
p_Result->IsLengthOk = true;
p_Result->VendorState.IsValid = true;
// 第 1 字节是物理 modifier。
p_Result->VendorState.Modifier = static_cast<quint8>(ByteArray.at(1));
// 第 2~30 字节是物理 usage 位图。
p_Result->VendorState.UsageBitmap = ByteArray.mid(2, MID_CONST_USAGE_BITMAP_SIZE);
// 展开位图,得到当前物理按下的 usage 列表。
for (quint16 Usage = 0; Usage <= MID_CONST_KEYBOARD_USAGE_MAX; ++Usage)
{
const int ByteIndex = Usage / 8;
const int BitIndex = Usage % 8;
const quint8 Value = static_cast<quint8>(p_Result->VendorState.UsageBitmap.at(ByteIndex));
if ((Value & static_cast<quint8>(1U << BitIndex)) == 0)
{
continue;
}
p_Result->VendorState.UsageList.append(Usage);
p_Result->VendorState.UsageTextList.append(Mid_Func_GetKeyboardUsageText(Usage));
}
// 这里整理的是面向教学和调试窗口的中文解释。
QStringList ExplainList;
ExplainList.append(QStringLiteral("0x04 Vendor 物理镜像态"));
ExplainList.append(QStringLiteral("物理修饰键:%1").arg(Mid_Func_GetModifierText(p_Result->VendorState.Modifier)));
if (p_Result->VendorState.UsageTextList.isEmpty())
{
ExplainList.append(QStringLiteral("物理按下:无"));
}
else
{
ExplainList.append(QStringLiteral("物理按下:%1").arg(p_Result->VendorState.UsageTextList.join(QStringLiteral(", "))));
}
p_Result->TextExplain = ExplainList.join(QLatin1Char('\n'));
}

31
LOGIC/Lgc_Vendor.h Normal file
View File

@@ -0,0 +1,31 @@
#pragma once
#include "MID/Mid_Def.h"
/*
* 这份文件负责解析 report id 0x04 的 Vendor 包。
*
* 当前 0x04 不是旧 VIA 命令协议,而是“键盘物理状态镜像流”。
* 它的结构固定为:
* [report_id(1) | modifier(1) | usage_bitmap(29)]
*
* 也就是说:
* - 0x01 代表主机最终可见键盘态
* - 0x04 代表下位机物理键盘态
*/
struct Lgc_Vendor_Struct_Result
{
// 拆出来的 Vendor 状态。
Mid_Struct_VendorState VendorState;
// 是否匹配到 report id 0x04。
bool IsMatch = false;
// 长度是否符合 31 字节。
bool IsLengthOk = false;
// 给调试窗口看的中文解释。
QString TextExplain;
};
void Lgc_Vendor_Func_Parse(const QByteArray& ByteArray, Lgc_Vendor_Struct_Result* p_Result);