#include "LOGIC/Lgc_Core_Private.h" #include #include namespace { constexpr qint64 kCdcRefreshIntervalMs = 1000; constexpr qint64 kPendingCommandTimeoutMs = 1800; constexpr int kTestLogMaxLineCount = 240; quint64 Lgc_Core_ProtocolTypeBit(Com_Enum_ProtocolType Type) { switch (Type) { case Com_Enum_ProtocolType_Bitmap: case Com_Enum_ProtocolType_TimeSync: case Com_Enum_ProtocolType_ThemeRgb: break; default: return 0; } const quint32 RawType = static_cast(Type); return RawType < 64U ? (1ULL << RawType) : 0ULL; } bool Lgc_Core_TryRefreshCdcPort(Lgc_Core_Struct_State* p_State, QString* p_TextStatus) { if ((p_State == nullptr) || !p_State->IsStarted || p_State->DeviceReady || p_State->DriCdcPort.IsOpened) { return false; } const qint64 NowMs = QDateTime::currentMSecsSinceEpoch(); if ((p_State->LastCdcRefreshAttemptMs != 0) && ((NowMs - p_State->LastCdcRefreshAttemptMs) < kCdcRefreshIntervalMs)) { return false; } p_State->LastCdcRefreshAttemptMs = NowMs; const bool IsOpened = Dri_Cdc_Init(&p_State->DriCdcPort, p_State->DeviceConfig, p_TextStatus); if (IsOpened) { p_State->IsUsbHelloSent = false; p_State->LastUsbHelloAttemptMs = 0; } return IsOpened; } bool Lgc_Core_LogNusSummaryIfChanged(Lgc_Core_Struct_State* p_State) { if (p_State == nullptr) { return false; } const QString CurrentSummary = p_State->DriNusPort.TextEndpointSummary.trimmed(); if (CurrentSummary.isEmpty() || (CurrentSummary == p_State->LastLoggedNusEndpointSummary)) { return false; } p_State->LastLoggedNusEndpointSummary = CurrentSummary; Lgc_Core_TestAppendLog( p_State, QStringLiteral("BLE status: %1").arg(CurrentSummary)); return true; } bool Lgc_Core_UpdateNusConnectionWindow(Lgc_Core_Struct_State* p_State) { if (p_State == nullptr) { return false; } const bool IsConnectedNow = p_State->DriNusPort.IsConnected; if (IsConnectedNow == p_State->WasNusConnectedLastPoll) { return false; } p_State->WasNusConnectedLastPoll = IsConnectedNow; if (IsConnectedNow) { p_State->NusReadySinceMs = QDateTime::currentMSecsSinceEpoch(); p_State->NusHelloRetryCount = 0; p_State->LastNusHelloAttemptMs = 0; p_State->HasLoggedNusWriteAck = false; p_State->HasLoggedNusHelloTimeout = false; Lgc_Core_TestAppendLog( p_State, QStringLiteral("BLE handshake window opened. Wait briefly before the first HelloReq.")); } else { p_State->NusReadySinceMs = 0; p_State->NusHelloRetryCount = 0; p_State->LastNusHelloAttemptMs = 0; p_State->IsNusHelloSent = false; p_State->HasLoggedNusWriteAck = false; p_State->HasLoggedNusHelloTimeout = false; } return true; } QString Lgc_Core_FormatPendingCommandBits(quint64 PendingBits) { QStringList TypeList; if ((PendingBits & Lgc_Core_ProtocolTypeBit(Com_Enum_ProtocolType_Bitmap)) != 0) { TypeList.append(QStringLiteral("Bitmap")); } if ((PendingBits & Lgc_Core_ProtocolTypeBit(Com_Enum_ProtocolType_TimeSync)) != 0) { TypeList.append(QStringLiteral("TimeSync")); } if ((PendingBits & Lgc_Core_ProtocolTypeBit(Com_Enum_ProtocolType_ThemeRgb)) != 0) { TypeList.append(QStringLiteral("ThemeRgb")); } return TypeList.isEmpty() ? QStringLiteral("Unknown") : TypeList.join(QStringLiteral(", ")); } bool Lgc_Core_ExpirePendingCommands(Lgc_Core_Struct_State* p_State) { if (p_State == nullptr) { return false; } const qint64 NowMs = QDateTime::currentMSecsSinceEpoch(); bool IsChanged = false; const auto TryExpire = [&](Com_Enum_RawPacketSource Source) { quint64* p_PendingBits = nullptr; qint64* p_PendingSinceMs = nullptr; switch (Source) { case Com_Enum_RawPacketSource_UsbCdc: p_PendingBits = &p_State->PendingUsbCommandBits; p_PendingSinceMs = &p_State->PendingUsbCommandSinceMs; break; case Com_Enum_RawPacketSource_BleNus: p_PendingBits = &p_State->PendingNusCommandBits; p_PendingSinceMs = &p_State->PendingNusCommandSinceMs; break; default: return; } if ((*p_PendingBits == 0) || (*p_PendingSinceMs == 0) || ((NowMs - *p_PendingSinceMs) < kPendingCommandTimeoutMs)) { return; } const quint64 ExpiredBits = *p_PendingBits; *p_PendingBits = 0; *p_PendingSinceMs = 0; if ((ExpiredBits & Lgc_Core_ProtocolTypeBit(Com_Enum_ProtocolType_Bitmap)) != 0) { p_State->BitmapSent = false; p_State->BitmapDirty = true; p_State->BitmapNextSendMs = NowMs; } p_State->TextFunctionStatus = QStringLiteral("%1 ACK timeout for %2. Pending state was cleared.") .arg(Lgc_Core_GetTransportText(Source)) .arg(Lgc_Core_FormatPendingCommandBits(ExpiredBits)); Lgc_Core_TestAppendLog(p_State, p_State->TextFunctionStatus); IsChanged = true; }; TryExpire(Com_Enum_RawPacketSource_UsbCdc); TryExpire(Com_Enum_RawPacketSource_BleNus); return IsChanged; } } // namespace QString Lgc_Core_GetTransportText(Com_Enum_RawPacketSource Source) { switch (Source) { case Com_Enum_RawPacketSource_UsbCdc: return QStringLiteral("USB CDC"); case Com_Enum_RawPacketSource_BleNus: return QStringLiteral("BLE NUS"); default: return QStringLiteral("Unknown transport"); } } QString Lgc_Core_GetProtocolTypeText(Com_Enum_ProtocolType Type) { switch (Type) { case Com_Enum_ProtocolType_HelloReq: return QStringLiteral("HelloReq"); case Com_Enum_ProtocolType_HelloRsp: return QStringLiteral("HelloRsp"); case Com_Enum_ProtocolType_Bitmap: return QStringLiteral("Bitmap"); case Com_Enum_ProtocolType_FunctionKeyEvent: return QStringLiteral("FunctionKeyEvent"); case Com_Enum_ProtocolType_LedState: return QStringLiteral("LedState"); case Com_Enum_ProtocolType_TimeSync: return QStringLiteral("TimeSync"); case Com_Enum_ProtocolType_ThemeRgb: return QStringLiteral("ThemeRgb"); case Com_Enum_ProtocolType_Ack: return QStringLiteral("Ack"); case Com_Enum_ProtocolType_Error: return QStringLiteral("Error"); default: return QStringLiteral("Unknown"); } } QString Lgc_Core_GetProtocolErrorText(quint32 ErrorCode) { switch (ErrorCode) { case Com_Enum_ProtocolErrorCode_None: return QStringLiteral("NONE"); case Com_Enum_ProtocolErrorCode_UnknownType: return QStringLiteral("UNKNOWN_TYPE"); case Com_Enum_ProtocolErrorCode_InvalidLength: return QStringLiteral("INVALID_LENGTH"); case Com_Enum_ProtocolErrorCode_InvalidParam: return QStringLiteral("INVALID_PARAM"); case Com_Enum_ProtocolErrorCode_NotReady: return QStringLiteral("NOT_READY"); default: return QStringLiteral("UNKNOWN_ERROR"); } } QString Lgc_Core_FormatPacketHex(const QByteArray& PacketBody) { return PacketBody.isEmpty() ? QStringLiteral("(empty)") : QString::fromLatin1(PacketBody.toHex(' ').toUpper()); } void Lgc_Core_TestAppendLog(Lgc_Core_Struct_State* p_State, const QString& LineText) { if ((p_State == nullptr) || LineText.trimmed().isEmpty()) { return; } const QString TimePrefix = QDateTime::currentDateTime().toString(QStringLiteral("HH:mm:ss.zzz")); p_State->TestLogLines.append(QStringLiteral("[%1] %2").arg(TimePrefix, LineText.trimmed())); while (p_State->TestLogLines.size() > kTestLogMaxLineCount) { p_State->TestLogLines.removeFirst(); } } void Lgc_Core_TestRecordTxPacket( Lgc_Core_Struct_State* p_State, Com_Enum_RawPacketSource Source, Com_Enum_ProtocolType Type, const QByteArray& PacketBody, const QString& NoteText) { if (p_State == nullptr) { return; } switch (Type) { case Com_Enum_ProtocolType_HelloReq: ++p_State->TestTxHelloReqCount; break; case Com_Enum_ProtocolType_Bitmap: ++p_State->TestTxBitmapCount; break; case Com_Enum_ProtocolType_TimeSync: ++p_State->TestTxTimeSyncCount; break; case Com_Enum_ProtocolType_ThemeRgb: ++p_State->TestTxThemeRgbCount; break; default: break; } p_State->TestLastTxBytes = PacketBody; p_State->TestLastTxSummary = QStringLiteral("%1 TX %2") .arg(Lgc_Core_GetTransportText(Source), Lgc_Core_GetProtocolTypeText(Type)); if (!NoteText.trimmed().isEmpty()) { p_State->TestLastTxSummary += QStringLiteral(" - %1").arg(NoteText.trimmed()); } Lgc_Core_TestAppendLog( p_State, QStringLiteral("%1 | %2") .arg(p_State->TestLastTxSummary, Lgc_Core_FormatPacketHex(PacketBody))); } void Lgc_Core_TestRecordRxPacket( Lgc_Core_Struct_State* p_State, const Com_Struct_RawPacket& Packet) { if (p_State == nullptr) { return; } switch (Packet.ProtocolType) { case Com_Enum_ProtocolType_HelloRsp: ++p_State->TestRxHelloRspCount; break; case Com_Enum_ProtocolType_FunctionKeyEvent: ++p_State->TestRxFunctionKeyEventCount; break; case Com_Enum_ProtocolType_LedState: ++p_State->TestRxLedStateCount; break; case Com_Enum_ProtocolType_Ack: ++p_State->TestRxAckCount; break; case Com_Enum_ProtocolType_Error: ++p_State->TestRxErrorCount; break; default: break; } p_State->TestLastRxBytes = Packet.ByteArray; p_State->TestLastRxSummary = QStringLiteral("%1 RX %2") .arg(Lgc_Core_GetTransportText(Packet.Source)) .arg(Lgc_Core_GetProtocolTypeText(Packet.ProtocolType)); Lgc_Core_TestAppendLog( p_State, QStringLiteral("%1 | %2") .arg(p_State->TestLastRxSummary, Lgc_Core_FormatPacketHex(Packet.ByteArray))); } void Lgc_Core_ResetProtocolState(Lgc_Core_Struct_State* p_State) { if (p_State == nullptr) { return; } p_State->IsUsbProtocolReady = false; p_State->IsNusProtocolReady = false; p_State->IsUsbHelloSent = false; p_State->IsNusHelloSent = false; p_State->LastUsbHelloAttemptMs = 0; p_State->LastNusHelloAttemptMs = 0; p_State->NusReadySinceMs = 0; p_State->NusHelloRetryCount = 0; p_State->WasNusConnectedLastPoll = false; p_State->HasLoggedNusWriteAck = false; p_State->HasLoggedNusHelloTimeout = false; p_State->HelloResponse = Com_Struct_ProtocolHelloRsp(); p_State->DeviceLedMask = 0; p_State->LastAckedType = 0; p_State->LastErrorType = 0; p_State->LastErrorCode = 0; p_State->PendingUsbCommandBits = 0; p_State->PendingUsbCommandSinceMs = 0; p_State->PendingNusCommandBits = 0; p_State->PendingNusCommandSinceMs = 0; p_State->DeviceReady = false; p_State->BitmapSent = false; p_State->BitmapDirty = true; p_State->BitmapRetryCount = 0; p_State->BitmapNextSendMs = 0; Lgc_Core_ClearDeviceReportedKeyStates(p_State); } void Lgc_Core_ResetProtocolStateForSource( Lgc_Core_Struct_State* p_State, Com_Enum_RawPacketSource Source) { if (p_State == nullptr) { return; } switch (Source) { case Com_Enum_RawPacketSource_UsbCdc: p_State->IsUsbProtocolReady = false; p_State->IsUsbHelloSent = false; p_State->LastUsbHelloAttemptMs = 0; p_State->PendingUsbCommandBits = 0; p_State->PendingUsbCommandSinceMs = 0; break; case Com_Enum_RawPacketSource_BleNus: p_State->IsNusProtocolReady = false; p_State->IsNusHelloSent = false; p_State->LastNusHelloAttemptMs = 0; p_State->NusReadySinceMs = p_State->DriNusPort.IsConnected ? QDateTime::currentMSecsSinceEpoch() : 0; p_State->NusHelloRetryCount = 0; p_State->WasNusConnectedLastPoll = p_State->DriNusPort.IsConnected; p_State->HasLoggedNusWriteAck = false; p_State->HasLoggedNusHelloTimeout = false; p_State->PendingNusCommandBits = 0; p_State->PendingNusCommandSinceMs = 0; break; default: break; } p_State->DeviceReady = p_State->IsUsbProtocolReady || p_State->IsNusProtocolReady; if (!p_State->DeviceReady) { p_State->HelloResponse = Com_Struct_ProtocolHelloRsp(); p_State->DeviceLedMask = 0; p_State->LastAckedType = 0; p_State->LastErrorType = 0; p_State->LastErrorCode = 0; } p_State->BitmapSent = false; p_State->BitmapDirty = true; p_State->BitmapRetryCount = 0; p_State->BitmapNextSendMs = 0; Lgc_Core_ClearDeviceReportedKeyStates(p_State); } bool Lgc_Core_DeviceSupportsPacketType( const Lgc_Core_Struct_State* p_State, Com_Enum_ProtocolType Type) { if (p_State == nullptr) { return false; } switch (Type) { case Com_Enum_ProtocolType_Bitmap: return (p_State->HelloResponse.CapabilityFlags & (1U << 0)) != 0; case Com_Enum_ProtocolType_TimeSync: return (p_State->HelloResponse.CapabilityFlags & (1U << 1)) != 0; case Com_Enum_ProtocolType_ThemeRgb: return (p_State->HelloResponse.CapabilityFlags & (1U << 2)) != 0; case Com_Enum_ProtocolType_LedState: return (p_State->HelloResponse.CapabilityFlags & (1U << 3)) != 0; case Com_Enum_ProtocolType_FunctionKeyEvent: return (p_State->HelloResponse.CapabilityFlags & (1U << 4)) != 0; default: return true; } } void Lgc_Core_AddPendingCommand( Lgc_Core_Struct_State* p_State, Com_Enum_RawPacketSource Source, Com_Enum_ProtocolType Type) { const quint64 TypeBit = Lgc_Core_ProtocolTypeBit(Type); if ((p_State == nullptr) || (TypeBit == 0)) { return; } switch (Source) { case Com_Enum_RawPacketSource_UsbCdc: if (p_State->PendingUsbCommandBits == 0) { p_State->PendingUsbCommandSinceMs = QDateTime::currentMSecsSinceEpoch(); } p_State->PendingUsbCommandBits |= TypeBit; break; case Com_Enum_RawPacketSource_BleNus: if (p_State->PendingNusCommandBits == 0) { p_State->PendingNusCommandSinceMs = QDateTime::currentMSecsSinceEpoch(); } p_State->PendingNusCommandBits |= TypeBit; break; default: break; } } bool Lgc_Core_ClearPendingCommand( Lgc_Core_Struct_State* p_State, Com_Enum_RawPacketSource Source, Com_Enum_ProtocolType Type) { const quint64 TypeBit = Lgc_Core_ProtocolTypeBit(Type); if ((p_State == nullptr) || (TypeBit == 0)) { return false; } switch (Source) { case Com_Enum_RawPacketSource_UsbCdc: { const bool WasPending = (p_State->PendingUsbCommandBits & TypeBit) != 0; p_State->PendingUsbCommandBits &= ~TypeBit; if (p_State->PendingUsbCommandBits == 0) { p_State->PendingUsbCommandSinceMs = 0; } return WasPending; } case Com_Enum_RawPacketSource_BleNus: { const bool WasPending = (p_State->PendingNusCommandBits & TypeBit) != 0; p_State->PendingNusCommandBits &= ~TypeBit; if (p_State->PendingNusCommandBits == 0) { p_State->PendingNusCommandSinceMs = 0; } return WasPending; } default: return false; } } bool Lgc_Core_HasPendingCommand( const Lgc_Core_Struct_State* p_State, Com_Enum_RawPacketSource Source, Com_Enum_ProtocolType Type) { const quint64 TypeBit = Lgc_Core_ProtocolTypeBit(Type); if ((p_State == nullptr) || (TypeBit == 0)) { return false; } switch (Source) { case Com_Enum_RawPacketSource_UsbCdc: return (p_State->PendingUsbCommandBits & TypeBit) != 0; case Com_Enum_RawPacketSource_BleNus: return (p_State->PendingNusCommandBits & TypeBit) != 0; default: return false; } } void Lgc_Core_Init(Lgc_Core_Struct_State* p_State) { if (p_State == nullptr) { return; } *p_State = Lgc_Core_Struct_State(); p_State->TextFunctionStatus = QStringLiteral("Waiting for function-key activity."); Lgc_Core_ClearAllKeyStates(p_State); p_State->IsSystemNumLockOn = (GetKeyState(VK_NUMLOCK) & 0x0001) != 0; Lgc_Core_FillMaskAllEnabled(&p_State->FunctionMaskBitmap); Lgc_Core_FillMaskAllEnabled(&p_State->KeyboardMaskBitmap); p_State->FunctionButtonConfig = Lgc_FunctionButton_Config(); Lgc_Core_ApplyFunctionConfig(p_State); } void Lgc_Core_SetWindowHandle(Lgc_Core_Struct_State* p_State, void* WindowHandle) { if (p_State != nullptr) { p_State->WindowHandle = WindowHandle; } } void Lgc_Core_HandleNativeMessage(Lgc_Core_Struct_State* p_State, void* p_Message) { Q_UNUSED(p_State); Q_UNUSED(p_Message); } void Lgc_Core_Start(Lgc_Core_Struct_State* p_State) { if ((p_State == nullptr) || p_State->IsStarted) { return; } p_State->IsStarted = true; Lgc_Core_RefreshDevice(p_State); } void Lgc_Core_Close(Lgc_Core_Struct_State* p_State) { if (p_State == nullptr) { return; } Lgc_Core_CloseAllPorts(p_State); Lgc_Core_ClearAllKeyStates(p_State); Lgc_Core_ResetProtocolState(p_State); p_State->LastCdcRefreshAttemptMs = 0; p_State->LastNusRefreshAttemptMs = 0; p_State->TextFunctionStatus = QStringLiteral("Waiting for function-key activity."); p_State->IsConnected = false; p_State->IsStarted = false; } void Lgc_Core_RefreshDevice(Lgc_Core_Struct_State* p_State) { if (p_State == nullptr) { return; } Lgc_Core_CloseAllPorts(p_State); Lgc_Core_ClearAllKeyStates(p_State); QString CdcTextStatus; QString NusTextStatus; Dri_Cdc_Init(&p_State->DriCdcPort, p_State->DeviceConfig, &CdcTextStatus); Dri_Nus_Init(&p_State->DriNusPort, p_State->DeviceConfig, &NusTextStatus); Lgc_Core_ResetProtocolState(p_State); const qint64 NowMs = QDateTime::currentMSecsSinceEpoch(); p_State->LastCdcRefreshAttemptMs = NowMs; p_State->LastNusRefreshAttemptMs = NowMs; p_State->IsConnected = false; if (!CdcTextStatus.isEmpty()) { p_State->TextFunctionStatus = CdcTextStatus; } else if (!NusTextStatus.isEmpty()) { p_State->TextFunctionStatus = NusTextStatus; } Lgc_Core_SendHello(p_State); Lgc_Core_SyncSystemState(p_State); } bool Lgc_Core_Poll(Lgc_Core_Struct_State* p_State) { if (p_State == nullptr) { return false; } bool IsChanged = false; Com_Struct_RawPacket Packet; QString TextStatus; if (Lgc_Core_TryRefreshCdcPort(p_State, &TextStatus)) { IsChanged = true; } if (!TextStatus.isEmpty()) { p_State->TextFunctionStatus = TextStatus; IsChanged = true; } TextStatus.clear(); if (p_State->IsStarted && !p_State->DeviceReady && !p_State->DriNusPort.IsOpened) { const qint64 NowMs = QDateTime::currentMSecsSinceEpoch(); if ((p_State->LastNusRefreshAttemptMs == 0) || ((NowMs - p_State->LastNusRefreshAttemptMs) >= kCdcRefreshIntervalMs)) { p_State->LastNusRefreshAttemptMs = NowMs; if (Dri_Nus_Init(&p_State->DriNusPort, p_State->DeviceConfig, &TextStatus)) { p_State->IsNusHelloSent = false; p_State->LastNusHelloAttemptMs = 0; p_State->NusReadySinceMs = 0; p_State->NusHelloRetryCount = 0; p_State->WasNusConnectedLastPoll = false; p_State->HasLoggedNusHelloTimeout = false; IsChanged = true; } } } if (!TextStatus.isEmpty()) { p_State->TextFunctionStatus = TextStatus; IsChanged = true; } IsChanged |= Lgc_Core_UpdateNusConnectionWindow(p_State); Lgc_Core_SendHello(p_State); IsChanged |= Lgc_Core_LogNusSummaryIfChanged(p_State); TextStatus.clear(); if (Dri_Cdc_Read(&p_State->DriCdcPort, &Packet, &TextStatus)) { Lgc_Core_HandleCdcPacket(p_State, Packet); IsChanged = true; } else if (!TextStatus.isEmpty()) { p_State->TextFunctionStatus = TextStatus; IsChanged = true; } TextStatus.clear(); if (Dri_Nus_Read(&p_State->DriNusPort, &Packet, &TextStatus)) { Lgc_Core_HandleNusPacket(p_State, Packet); IsChanged = true; } else if (!TextStatus.isEmpty()) { p_State->TextFunctionStatus = TextStatus; IsChanged = true; } IsChanged |= Lgc_Core_ExpirePendingCommands(p_State); IsChanged |= Lgc_Core_ProcessBitmapSend(p_State); IsChanged |= Lgc_Core_HandleFunctionButtons(p_State); IsChanged |= Lgc_Core_SyncSystemState(p_State); return IsChanged; } Lgc_Core_Struct_View Lgc_Core_GetView(const Lgc_Core_Struct_State* p_State) { Lgc_Core_Struct_View View; if (p_State == nullptr) { return View; } View.TextFunctionStatus = p_State->TextFunctionStatus; View.IsConnected = p_State->IsConnected; View.HasOpenTransport = p_State->DriCdcPort.IsOpened || p_State->DriNusPort.IsOpened || p_State->DriNusPort.IsConnected; View.IsSystemNumLockOn = p_State->IsSystemNumLockOn; View.IsVisibleKeyStateValid = p_State->IsVisibleKeyStateValid; View.VisibleUsageList = p_State->VisibleUsageList; View.IsPhysicalKeyStateValid = p_State->IsPhysicalKeyStateValid; View.PhysicalUsageList = p_State->PhysicalUsageList; View.IsFunctionSequenceRecording = p_State->IsFunctionSequenceRecording; return View; } void Lgc_Core_SetStatusText(Lgc_Core_Struct_State* p_State, const QString& TextStatus) { if (p_State != nullptr) { p_State->TextFunctionStatus = TextStatus; } } QVector Lgc_Core_GetFeatureIdList(const Lgc_Core_Struct_State* p_State) { return p_State == nullptr ? QVector() : Lgc_FunctionButton_GetFeatureIdList(p_State->FunctionButtonConfig); } Lgc_FunctionFeature_Definition Lgc_Core_GetFeature( const Lgc_Core_Struct_State* p_State, int FeatureId) { return p_State == nullptr ? Lgc_FunctionFeature_Definition() : Lgc_FunctionButton_GetFeature(p_State->FunctionButtonConfig, FeatureId); } bool Lgc_Core_HasFeature(const Lgc_Core_Struct_State* p_State, int FeatureId) { return Lgc_Core_GetFeature(p_State, FeatureId).Id > 0; } QString Lgc_Core_GetUsageShortText(quint16 Usage) { return Lgc_FunctionButton_GetUsageShortText(Usage); } QString Lgc_Core_GetFeatureTypeText(Lgc_FunctionFeature_Type Type) { return Lgc_FunctionButton_GetFeatureTypeText(Type); } QString Lgc_Core_GetFeatureNameById(const Lgc_Core_Struct_State* p_State, int FeatureId) { const Lgc_FunctionFeature_Definition Feature = Lgc_Core_GetFeature(p_State, FeatureId); return Feature.Id > 0 ? Lgc_FunctionButton_GetFeatureName(Feature) : QStringLiteral("No Function"); } QString Lgc_Core_GetFeatureDescriptionById( const Lgc_Core_Struct_State* p_State, int FeatureId) { return p_State == nullptr ? QString() : Lgc_FunctionButton_GetFeatureDescriptionById( p_State->FunctionButtonConfig, FeatureId); } QString Lgc_Core_GetFeatureBindingSummary( const Lgc_Core_Struct_State* p_State, int FeatureId) { return p_State == nullptr ? QStringLiteral("No function selected yet.") : Lgc_FunctionButton_GetFeatureBindingSummary( p_State->FunctionButtonConfig, FeatureId); } int Lgc_Core_GetUsageFeatureId(const Lgc_Core_Struct_State* p_State, quint16 Usage) { return p_State == nullptr ? 0 : Lgc_FunctionButton_GetUsageFeatureId(p_State->FunctionButtonConfig, Usage); } bool Lgc_Core_HasUsageFeature(const Lgc_Core_Struct_State* p_State, quint16 Usage) { return p_State != nullptr && Lgc_FunctionButton_HasUsageFeature(p_State->FunctionButtonConfig, Usage); } void Lgc_Core_ClearTestLog(Lgc_Core_Struct_State* p_State) { if (p_State == nullptr) { return; } p_State->TestTxHelloReqCount = 0; p_State->TestTxBitmapCount = 0; p_State->TestTxTimeSyncCount = 0; p_State->TestTxThemeRgbCount = 0; p_State->TestRxHelloRspCount = 0; p_State->TestRxFunctionKeyEventCount = 0; p_State->TestRxLedStateCount = 0; p_State->TestRxAckCount = 0; p_State->TestRxErrorCount = 0; p_State->TestLastTxSummary.clear(); p_State->TestLastRxSummary.clear(); p_State->TestLastTxBytes.clear(); p_State->TestLastRxBytes.clear(); p_State->TestLastFunctionEventBitmap.clear(); p_State->TestLogLines.clear(); p_State->LastLoggedNusEndpointSummary.clear(); } Lgc_Core_Struct_TestView Lgc_Core_GetTestView(const Lgc_Core_Struct_State* p_State) { Lgc_Core_Struct_TestView View; if (p_State == nullptr) { return View; } View.StatusText = p_State->TextFunctionStatus; View.UsbPortName = p_State->DriCdcPort.PortName; View.NusEndpointSummary = p_State->DriNusPort.TextEndpointSummary; View.IsUsbOpened = p_State->DriCdcPort.IsOpened; View.IsNusOpened = p_State->DriNusPort.IsOpened; View.IsNusConnected = p_State->DriNusPort.IsConnected; View.IsUsbProtocolReady = p_State->IsUsbProtocolReady; View.IsNusProtocolReady = p_State->IsNusProtocolReady; View.DeviceReady = p_State->DeviceReady; View.HelloProtocolVersion = p_State->HelloResponse.ProtocolVersion; View.HelloVendorId = p_State->HelloResponse.VendorId; View.HelloProductId = p_State->HelloResponse.ProductId; View.HelloFirmwareMajor = p_State->HelloResponse.FirmwareMajor; View.HelloFirmwareMinor = p_State->HelloResponse.FirmwareMinor; View.HelloCapabilityFlags = p_State->HelloResponse.CapabilityFlags; View.DeviceLedMask = p_State->DeviceLedMask; View.LastAckedType = p_State->LastAckedType; View.LastErrorType = p_State->LastErrorType; View.LastErrorCode = p_State->LastErrorCode; View.PendingUsbCommandBits = p_State->PendingUsbCommandBits; View.PendingNusCommandBits = p_State->PendingNusCommandBits; View.LastTxSummary = p_State->TestLastTxSummary; View.LastRxSummary = p_State->TestLastRxSummary; View.LastTxHex = Lgc_Core_FormatPacketHex(p_State->TestLastTxBytes); View.LastRxHex = Lgc_Core_FormatPacketHex(p_State->TestLastRxBytes); View.FunctionMaskHex = Lgc_Core_FormatPacketHex(p_State->FunctionMaskBitmap); View.LastFunctionEventHex = Lgc_Core_FormatPacketHex(p_State->TestLastFunctionEventBitmap); View.TxHelloReqCount = p_State->TestTxHelloReqCount; View.TxBitmapCount = p_State->TestTxBitmapCount; View.TxTimeSyncCount = p_State->TestTxTimeSyncCount; View.TxThemeRgbCount = p_State->TestTxThemeRgbCount; View.RxHelloRspCount = p_State->TestRxHelloRspCount; View.RxFunctionKeyEventCount = p_State->TestRxFunctionKeyEventCount; View.RxLedStateCount = p_State->TestRxLedStateCount; View.RxAckCount = p_State->TestRxAckCount; View.RxErrorCount = p_State->TestRxErrorCount; View.LogText = p_State->TestLogLines.join(QLatin1Char('\n')); return View; } bool Lgc_Core_BeginSequenceRecording(Lgc_Core_Struct_State* p_State, int FeatureId) { if (p_State == nullptr) { return false; } const Lgc_FunctionFeature_Definition Feature = Lgc_Core_GetFeature(p_State, FeatureId); if (Feature.Id <= 0) { return false; } if ((Feature.Type != Lgc_FunctionFeature_Type::KeyCombination) && (Feature.Type != Lgc_FunctionFeature_Type::KeySequence)) { return false; } p_State->IsFunctionSequenceRecording = true; p_State->TextFunctionStatus = Feature.Type == Lgc_FunctionFeature_Type::KeySequence ? QStringLiteral("Sequence recording started. Press keys to append combinations.") : QStringLiteral("Combination recording started. Press the target combo."); return true; } void Lgc_Core_EndSequenceRecording(Lgc_Core_Struct_State* p_State) { if (p_State == nullptr) { return; } p_State->IsFunctionSequenceRecording = false; p_State->TextFunctionStatus = QStringLiteral("Sequence recording stopped. Changes saved."); } void Lgc_Core_UpdateSequenceRecordingStatus( Lgc_Core_Struct_State* p_State, const QString& SequenceText) { if ((p_State == nullptr) || SequenceText.trimmed().isEmpty()) { return; } p_State->TextFunctionStatus = QStringLiteral("Recording: %1").arg(SequenceText.trimmed()); } void Lgc_Core_HandleUiKeyPress(Lgc_Core_Struct_State* p_State, quint16 Usage) { if (p_State == nullptr) { return; } QString TextStatus; if (Lgc_Core_HasUsageFeature(p_State, Usage)) { if (!Lgc_FunctionButton_RunBinding(*p_State, Usage, TextStatus)) { TextStatus = QStringLiteral("%1 has no runnable feature.") .arg(Lgc_FunctionButton_GetUsageShortText(Usage)); } } else if (Lgc_FunctionButton_SendUsageToWindows(Usage, true)) { p_State->UiPressedUsageSet.insert(Usage); TextStatus = QStringLiteral("Pressed %1.") .arg(Lgc_FunctionButton_GetUsageShortText(Usage)); } else { TextStatus = QStringLiteral("Failed to press %1.") .arg(Lgc_FunctionButton_GetUsageShortText(Usage)); } if (!TextStatus.isEmpty()) { p_State->TextFunctionStatus = TextStatus; } } void Lgc_Core_HandleUiKeyRelease(Lgc_Core_Struct_State* p_State, quint16 Usage) { if ((p_State == nullptr) || !p_State->UiPressedUsageSet.remove(Usage)) { return; } p_State->TextFunctionStatus = Lgc_FunctionButton_SendUsageToWindows(Usage, false) ? QStringLiteral("Released %1.").arg(Lgc_FunctionButton_GetUsageShortText(Usage)) : QStringLiteral("Failed to release %1.") .arg(Lgc_FunctionButton_GetUsageShortText(Usage)); }