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)));
|
|||
|
|
}
|
|||
|
|
|