添加项目文件。
This commit is contained in:
41
LOGIC/Lgc_Consumer.cpp
Normal file
41
LOGIC/Lgc_Consumer.cpp
Normal 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
31
LOGIC/Lgc_Consumer.h
Normal 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
606
LOGIC/Lgc_Core.cpp
Normal 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
85
LOGIC/Lgc_Core.h
Normal 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
191
LOGIC/Lgc_Func_Button.cpp
Normal 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
19
LOGIC/Lgc_Func_Button.h
Normal 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
70
LOGIC/Lgc_Nkro.cpp
Normal 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
40
LOGIC/Lgc_Nkro.h
Normal 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
71
LOGIC/Lgc_Vendor.cpp
Normal 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
31
LOGIC/Lgc_Vendor.h
Normal 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);
|
||||
Reference in New Issue
Block a user