321 lines
8.8 KiB
C++
321 lines
8.8 KiB
C++
#include "LOGIC/Lgc_Core_Private.h"
|
||
|
||
#include <QtCore/QDateTime>
|
||
#include <QtCore/QStringList>
|
||
#include <QtCore/QTimeZone>
|
||
|
||
namespace
|
||
{
|
||
|
||
struct Lgc_Core_Struct_ThemeColor
|
||
{
|
||
quint8 Red;
|
||
quint8 Green;
|
||
quint8 Blue;
|
||
};
|
||
|
||
QString Lgc_Core_FormatThemeColor(quint8 Red, quint8 Green, quint8 Blue)
|
||
{
|
||
return QStringLiteral("%1 %2 %3")
|
||
.arg(Red, 2, 16, QLatin1Char('0'))
|
||
.arg(Green, 2, 16, QLatin1Char('0'))
|
||
.arg(Blue, 2, 16, QLatin1Char('0'))
|
||
.toUpper();
|
||
}
|
||
|
||
Lgc_Core_Struct_ThemeColor Lgc_Core_GetNextThemeColor(Lgc_Core_Struct_State* p_State)
|
||
{
|
||
p_State->IsAltThemeEnabled = !p_State->IsAltThemeEnabled;
|
||
|
||
if (p_State->IsAltThemeEnabled)
|
||
{
|
||
return { 0xF7, 0x25, 0x85 };
|
||
}
|
||
|
||
return { 0x4C, 0xC9, 0xF0 };
|
||
}
|
||
|
||
bool Lgc_Core_IsUsageInRange(quint16 Usage)
|
||
{
|
||
return Usage <= MID_CONST_KEYBOARD_USAGE_MAX;
|
||
}
|
||
|
||
bool Lgc_Core_IsUsageEnabled(const QByteArray& Bitmap, quint16 Usage)
|
||
{
|
||
if (!Lgc_Core_IsUsageInRange(Usage) || (Bitmap.size() < MID_CONST_USAGE_BITMAP_SIZE))
|
||
{
|
||
return true;
|
||
}
|
||
|
||
const int ByteIndex = Usage / 8;
|
||
const quint8 BitMask = static_cast<quint8>(1U << (Usage % 8));
|
||
const quint8 ByteValue = static_cast<quint8>(Bitmap.at(ByteIndex));
|
||
return (ByteValue & BitMask) != 0;
|
||
}
|
||
|
||
void Lgc_Core_SetUsageEnabled(QByteArray* p_Bitmap, quint16 Usage, bool IsEnabled)
|
||
{
|
||
if (!Lgc_Core_IsUsageInRange(Usage))
|
||
{
|
||
return;
|
||
}
|
||
|
||
if (p_Bitmap->size() < MID_CONST_USAGE_BITMAP_SIZE)
|
||
{
|
||
Lgc_Core_FillMaskAllEnabled(p_Bitmap);
|
||
}
|
||
|
||
const int ByteIndex = Usage / 8;
|
||
const quint8 BitMask = static_cast<quint8>(1U << (Usage % 8));
|
||
quint8 ByteValue = static_cast<quint8>(p_Bitmap->at(ByteIndex));
|
||
|
||
if (IsEnabled)
|
||
{
|
||
ByteValue = static_cast<quint8>(ByteValue | BitMask);
|
||
}
|
||
else
|
||
{
|
||
ByteValue = static_cast<quint8>(ByteValue & ~BitMask);
|
||
}
|
||
|
||
(*p_Bitmap)[ByteIndex] = static_cast<char>(ByteValue);
|
||
}
|
||
|
||
void Lgc_Core_RebuildKeyboardMask(Lgc_Core_Struct_State* p_State)
|
||
{
|
||
if (p_State->FunctionMaskBitmap.size() < MID_CONST_USAGE_BITMAP_SIZE)
|
||
{
|
||
Lgc_Core_FillMaskAllEnabled(&p_State->FunctionMaskBitmap);
|
||
}
|
||
|
||
p_State->KeyboardMaskBitmap.resize(MID_CONST_USAGE_BITMAP_SIZE);
|
||
for (int Index = 0; Index < MID_CONST_USAGE_BITMAP_SIZE; ++Index)
|
||
{
|
||
p_State->KeyboardMaskBitmap[Index] = p_State->FunctionMaskBitmap.at(Index);
|
||
}
|
||
}
|
||
|
||
QByteArray Lgc_Core_BuildMaskPacket(const Lgc_Core_Struct_State* p_State)
|
||
{
|
||
QByteArray Packet(MID_CONST_PACKET_SIZE_VENDOR, 0);
|
||
Packet[0] = static_cast<char>(Mid_Enum_ReportId_Vendor);
|
||
for (int Index = 0; (Index < MID_CONST_USAGE_BITMAP_SIZE) && (Index < p_State->KeyboardMaskBitmap.size()); ++Index)
|
||
{
|
||
Packet[Index + 2] = p_State->KeyboardMaskBitmap.at(Index);
|
||
}
|
||
return Packet;
|
||
}
|
||
|
||
QByteArray Lgc_Core_BuildCommandPacket(quint8 CommandId, const QByteArray& Payload)
|
||
{
|
||
QByteArray Packet(MID_CONST_PACKET_SIZE_VENDOR_COMMAND, 0);
|
||
Packet[0] = static_cast<char>(Mid_Enum_ReportId_VendorCommand);
|
||
Packet[1] = static_cast<char>(CommandId);
|
||
for (int Index = 0; (Index < MID_CONST_PACKET_SIZE_VENDOR_COMMAND_DATA) && (Index < Payload.size()); ++Index)
|
||
{
|
||
Packet[Index + 2] = Payload.at(Index);
|
||
}
|
||
return Packet;
|
||
}
|
||
|
||
bool Lgc_Core_SendPacket(
|
||
Lgc_Core_Struct_State* p_State,
|
||
const QByteArray& Packet,
|
||
const QString& ExplainText)
|
||
{
|
||
QString RouteText;
|
||
QString TextStatus;
|
||
const auto AppendStatus = [&TextStatus](const QString& StatusText)
|
||
{
|
||
if (StatusText.isEmpty())
|
||
{
|
||
return;
|
||
}
|
||
|
||
if (!TextStatus.isEmpty())
|
||
{
|
||
TextStatus.append(QLatin1Char('\n'));
|
||
}
|
||
TextStatus.append(StatusText);
|
||
};
|
||
const auto AppendRoute = [&RouteText](const QString& RouteName)
|
||
{
|
||
if (RouteName.isEmpty())
|
||
{
|
||
return;
|
||
}
|
||
|
||
if (!RouteText.isEmpty())
|
||
{
|
||
RouteText.append(QStringLiteral(" -> "));
|
||
}
|
||
RouteText.append(RouteName);
|
||
};
|
||
const auto TrySendUsb = [&]() -> bool
|
||
{
|
||
if (!p_State->DriVendorPort.ReadPort.IsOpened)
|
||
{
|
||
return false;
|
||
}
|
||
|
||
QString UsbStatus;
|
||
const QString RouteName = p_State->DriVendorPort.ReadPort.PortName;
|
||
if (Dri_Vendor_Write(&p_State->DriVendorPort, Packet, &UsbStatus))
|
||
{
|
||
Lgc_Core_AppendPacketLog(
|
||
p_State,
|
||
QStringLiteral("发送"),
|
||
RouteName,
|
||
Packet,
|
||
ExplainText);
|
||
Lgc_Core_AppendStatusLog(p_State, UsbStatus);
|
||
return true;
|
||
}
|
||
|
||
AppendRoute(RouteName);
|
||
AppendStatus(UsbStatus);
|
||
return false;
|
||
};
|
||
const auto TrySendBle = [&]() -> bool
|
||
{
|
||
if (!p_State->DriBlePort.IsConnected)
|
||
{
|
||
return false;
|
||
}
|
||
|
||
QString BleStatus;
|
||
if (Dri_Ble_Write(&p_State->DriBlePort, Packet, &BleStatus))
|
||
{
|
||
Lgc_Core_AppendPacketLog(
|
||
p_State,
|
||
QStringLiteral("发送"),
|
||
QStringLiteral("Bluetooth/HID Vendor"),
|
||
Packet,
|
||
ExplainText);
|
||
Lgc_Core_AppendStatusLog(p_State, BleStatus);
|
||
return true;
|
||
}
|
||
|
||
AppendRoute(QStringLiteral("Bluetooth/HID Vendor"));
|
||
AppendStatus(BleStatus);
|
||
return false;
|
||
};
|
||
|
||
if (p_State->DriBlePort.IsConnected)
|
||
{
|
||
if (TrySendBle() || TrySendUsb())
|
||
{
|
||
return true;
|
||
}
|
||
}
|
||
else if (TrySendUsb())
|
||
{
|
||
return true;
|
||
}
|
||
|
||
if (RouteText.isEmpty())
|
||
{
|
||
RouteText = QStringLiteral("Vendor");
|
||
}
|
||
if (TextStatus.isEmpty())
|
||
{
|
||
TextStatus = QStringLiteral("No connected USB/Bluetooth device was found.");
|
||
}
|
||
|
||
Lgc_Core_AppendPacketLog(
|
||
p_State,
|
||
QStringLiteral("发送失败"),
|
||
RouteText,
|
||
Packet,
|
||
ExplainText);
|
||
Lgc_Core_AppendStatusLog(p_State, TextStatus);
|
||
return false;
|
||
}
|
||
|
||
qint64 Lgc_Core_GetBeijingTimestampMs()
|
||
{
|
||
return QDateTime::currentDateTimeUtc()
|
||
.toTimeZone(QTimeZone("Asia/Shanghai"))
|
||
.toMSecsSinceEpoch() + (8LL * 60LL * 60LL * 1000LL);
|
||
}
|
||
|
||
QString Lgc_Core_GetBeijingTimeText(qint64 TimestampMs)
|
||
{
|
||
const QTimeZone BeijingTimeZone("Asia/Shanghai");
|
||
return QDateTime::fromMSecsSinceEpoch(TimestampMs, Qt::UTC)
|
||
.toTimeZone(BeijingTimeZone)
|
||
.toString(QStringLiteral("yyyy-MM-dd HH:mm:ss.zzz"));
|
||
}
|
||
|
||
} // namespace
|
||
|
||
void Lgc_Core_FillMaskAllEnabled(QByteArray* p_UsageBitmap)
|
||
{
|
||
*p_UsageBitmap = QByteArray(MID_CONST_USAGE_BITMAP_SIZE, static_cast<char>(0xFF));
|
||
}
|
||
|
||
bool Lgc_Core_SendCurrentMask(Lgc_Core_Struct_State* p_State)
|
||
{
|
||
Lgc_Core_RebuildKeyboardMask(p_State);
|
||
return Lgc_Core_SendPacket(
|
||
p_State,
|
||
Lgc_Core_BuildMaskPacket(p_State),
|
||
QStringLiteral("0x04 键盘掩码同步"));
|
||
}
|
||
|
||
bool Lgc_Core_ApplyFunctionConfig(Lgc_Core_Struct_State* p_State)
|
||
{
|
||
Lgc_Core_FillMaskAllEnabled(&p_State->FunctionMaskBitmap);
|
||
|
||
const QVector<quint16> UsageList = Lgc_FunctionButton_GetConfigurableUsages();
|
||
for (quint16 Usage : UsageList)
|
||
{
|
||
if (Lgc_FunctionButton_HasUsageFeature(p_State->FunctionButtonConfig, Usage))
|
||
{
|
||
Lgc_Core_SetUsageEnabled(&p_State->FunctionMaskBitmap, Usage, false);
|
||
}
|
||
}
|
||
|
||
if (p_State->IsStarted &&
|
||
(p_State->DriVendorPort.ReadPort.IsOpened || p_State->DriBlePort.IsConnected))
|
||
{
|
||
Lgc_Core_SendCurrentMask(p_State);
|
||
}
|
||
return true;
|
||
}
|
||
|
||
bool Lgc_Core_SendTimeSync(Lgc_Core_Struct_State* p_State)
|
||
{
|
||
const qint64 TimestampMs = Lgc_Core_GetBeijingTimestampMs();
|
||
QByteArray Payload(MID_CONST_PACKET_SIZE_VENDOR_COMMAND_DATA, 0);
|
||
for (int Index = 0; Index < MID_CONST_PACKET_SIZE_VENDOR_COMMAND_DATA; ++Index)
|
||
{
|
||
Payload[Index] = static_cast<char>((static_cast<quint64>(TimestampMs) >> (Index * 8)) & 0xFF);
|
||
}
|
||
|
||
return Lgc_Core_SendPacket(
|
||
p_State,
|
||
Lgc_Core_BuildCommandPacket(0x02, Payload),
|
||
QStringLiteral("0x05 0x02 时间同步(北京时间毫秒值:%1,北京时间:%2)")
|
||
.arg(QString::number(TimestampMs), Lgc_Core_GetBeijingTimeText(TimestampMs)));
|
||
}
|
||
|
||
bool Lgc_Core_SendThemeSwitch(Lgc_Core_Struct_State* p_State)
|
||
{
|
||
const Lgc_Core_Struct_ThemeColor ThemeColor = Lgc_Core_GetNextThemeColor(p_State);
|
||
QByteArray Payload(MID_CONST_PACKET_SIZE_VENDOR_COMMAND_DATA, 0);
|
||
Payload[0] = static_cast<char>(ThemeColor.Red);
|
||
Payload[1] = static_cast<char>(ThemeColor.Green);
|
||
Payload[2] = static_cast<char>(ThemeColor.Blue);
|
||
|
||
return Lgc_Core_SendPacket(
|
||
p_State,
|
||
Lgc_Core_BuildCommandPacket(0x01, Payload),
|
||
QStringLiteral("0x05 0x01 theme switch (RGB:%1)")
|
||
.arg(Lgc_Core_FormatThemeColor(
|
||
ThemeColor.Red,
|
||
ThemeColor.Green,
|
||
ThemeColor.Blue)));
|
||
}
|
||
|